Re: DBI forgets last record
by Zaxo (Archbishop) on Jan 09, 2003 at 06:39 UTC
|
I suspect it's the first one you're losing. You call $sth->fetchrow_array() once to see if anything was found, and throw away the result. Call $sth->rows instead for that test.
Update: In other words:
print 'NO ITEMS IN CART' unless $sth->rows;
After Compline, Zaxo
| [reply] [d/l] |
|
|
Beware that $sth->rows() will not work in this case for many (most?) DBI drivers. From "perldoc DBI":
Generally, you can only rely on a row count after a non-`SELECT' `execute' (for some specific operations
like `UPDATE' and `DELETE'), or after fetching all the
rows of a `SELECT' statement.
For `SELECT' statements, it is generally not possible
to know how many rows will be returned except by
fetching them all. Some drivers will return the number of rows the application has fetched so far, but
others may return -1 until all rows have been fetched.
So use of the `rows' method or `$DBI::rows' with
`SELECT' statements is not recommended.
| [reply] |
Re: DBI forgets last record
by seattlejohn (Deacon) on Jan 09, 2003 at 07:06 UTC
|
$sth->fetchrow_array() returns a row (assuming one exists), and has the side effect of moving "past" that row in the set of database results. With this line:
print "NO ITEMS IN CART" unless $sth->fetchrow_array();
you try to retrieve a row. There might not be one, in which case you print "no items". But if there is, you don't assign the return value to a variable, so it just gets thrown away -- yet the results pointer still moves forward. That row won't show up in the list you generate in your while loop.
A possible fix could work something like this (untested):
my @fields = $sth->fetchrow_array();
if (!@fields) {
print "NO ITEMS IN CART";
} else {
while (@fields) {
my ($id,$prod_id,$qty) = @fields;
print "====== $id ======<br>";
@fields = $sth->fetchrow_array();
}
}
$perlmonks{seattlejohn} = 'John Clyman'; | [reply] [d/l] [select] |
Re: DBI forgets last record
by grinder (Bishop) on Jan 09, 2003 at 09:23 UTC
|
A couple of valid solutions have been shown, here's another take on the problem (I consider this to be a more natural way of solving it):
my $rows = 0;
while ( defined( my ($id,$prod_id,$qty) = $sth->fetchrow_array()) ){
++$rows;
print "====== $id =====<br>"; # for testing
}
print "NO ITEMS IN CART" unless $rows > 0;
- I like to wrap the entire fetch in a defined() test.
- Since you are already using scalars for each result fetched (as opposed to, say, an array), it might be worth your while to investigate the bind_col method (which provides a good speed boost).
- The $sth->rows() methid is not portable across all databases, notably Sybase. There, the idea is that the only way to see how many rows would be returned by a select is to go and fetch them. Which makes a certain amount of sense. So just use your own counter.
- Remember to call $sth->finish() as the first statement after your while loop.
- I agree with the other monks' assessment in that you are losing the first record, not the last. If you perceive that you are losing the last record in the set, methinks you might be in need of an order by clause to specify the exact order in which you want the results returned.
Hope this helps.
print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u' | [reply] [d/l] [select] |
|
|
Thanks for the DBI Hints. But talking about 1st and last row, I would like to add a reminder for the SQL:
Never count on the order of rows in an SQL-Select unless you use order by. Even if every day experience may tell you something different and you use tricks like indices etc, but that is just by accident. The result is a set, which is unordered per definitionem.
It is just the first row that gets lost, but that gives no clue about the contents of the row.
| [reply] |
Re: DBI forgets last record
by pg (Canon) on Jan 09, 2003 at 16:52 UTC
|
This is actually a common logic problem, which can easily happen in any sort of programing, not just sql programming (or to be more precise, not just a problem with a particular sql interface package.)
As a general thinking, when you operate on an object, basically there are two types of operations:
- TESTINGS, this kind of operation does not alter the target object and any of its attribute(s). For exacmple, the eof() function of a file handler. When you call eof(), you SIMPLY OBSERVE whether the end of a file has been reached, there is no harm to the file descriptor at all.
- MODIFICATIONS, this kind of operation alters the target object or its attribute(s).
You may wondering how can this description fit in the current question/situation, as you just want to select from the database, which is only a kind of observation, and thinking it does not modify the database at all.
This thinking is half correct, and half wrong.
Yes, You didn't alter the database, and that is 100% true. However, you touched the ACTIVE DATASET, which is a different object. You first create a prepared sql statement, and then you call execute, the moment you call execute, an object called ACTIVE DATASET was created on the database side. It is a dataset that contains all the rows you selected. (This is not done at the time, when you fetch the rows. When you fetch, you are not fetching from the database, instead you are fetching from the ACTIVE DATASET.)
Now, there is a "pointer" points to the current row of the the ACTIVE DATASET, which will be returned to you next time when you fetch.
In this sense, fetch is NOT a TESTING, but rather a MODIFICATION, as it modifies the pointer that points to the current row of the ACTIVE DATASET.
Now go back to my general thinking, this actually reminds us that, when we create our own packages, it is always nice to provide some pure testing functions, so that the user of our packages have more freedom, and can code their logic more nicely. | [reply] |
Re: DBI forgets last record
by Spenser (Friar) on Jan 09, 2003 at 16:53 UTC
|
I also experienced the same problem recently--or maybe I only noticed it recently. For me it wasn't the last record, but the first record as Zaxo suggested above. To solve the problem I switched from a while loop to a foreach. Below is my related code excerpt. In this case I'm generating a hash from records from a mySQL database. I'm also using fetchall_hashref instead of fetchrow_array.
my $sql_stmnt = "SELECT rec_id, name FROM writers";
my $dbh = DBI->connect("DBI:mysql:db:localhost","user","passwd")
|| die "Could not connect to database: " . DBI->errstr;
my $sth = $dbh->prepare($sql_stmnt);
$sth->execute();
my $results = $sth->fetchall_arrayref();
my %results;
foreach my $row(@$results) {
my ($key,$value) = @$row;
$results{$key} = "$value";
}
$sth->finish();
$dbh->disconnect();
-Spenser
That's Spenser, with an "s" like the detective. | [reply] [d/l] [select] |
Re: DBI forgets last record
by Anonymous Monk on Jan 09, 2003 at 06:40 UTC
|
print "NO ITEMS IN CART" unless $sth->fetchrow_array();
and the script now works fine... Why? | [reply] [d/l] |