The.Anti.9
The.Anti.9

Reputation: 44648

How do I know how many rows a Perl DBI query returns?

I'm trying to basically do a search through the database with Perl to tell if there is an item with a certain ID. This search can return no rows, but it can also return one.

I have the following code:

my $th = $dbh->prepare(qq{SELECT bi_exim_id FROM bounce_info WHERE bi_exim_id = '$exid'});
$th->execute();
if ($th->fetch()->[0] != $exid) {
        ...

Basically, this tries to see if the ID was returned and if it's not, continue with the script. But it is throwing a Null array reference error on the $th->fetch()->[0] thing. How can i just simply check to see if it returned rows or now?

Upvotes: 20

Views: 66177

Answers (7)

Lee Goddard
Lee Goddard

Reputation: 11173

This article may users of MySQL prior to version 8:

http://www.arraystudio.com/as-workshop/mysql-get-total-number-of-rows-when-using-limit.html

Luckily since MySQL 4.0.0 you can use SQL_CALC_FOUND_ROWS option in your query which will tell MySQL to count total number of rows disregarding LIMIT clause. You still need to execute a second query in order to retrieve row count, but it’s a simple query and not as complex as your query which retrieved the data.

Usage is pretty simple. In you main query you need to add SQL_CALC_FOUND_ROWS option just after SELECT and in second query you need to use FOUND_ROWS() function to get total number of rows. Queries would look like this:

SELECT SQL_CALC_FOUND_ROWS name, email FROM users WHERE name LIKE 'a%' LIMIT 10;

SELECT FOUND_ROWS();

The only limitation is that you must call second query immediately after the first one because SQL_CALC_FOUND_ROWS does not save number of rows anywhere.

Although this solution also requires two queries it’s much more faster, as you execute the main query only once.

You can read more about SQL_CALC_FOUND_ROWS and FOUND_ROWS() in MySQL docs.

Sadly, as of 8.0.17:

The SQL_CALC_FOUND_ROWS query modifier and accompanying FOUND_ROWS() function are deprecated as of MySQL 8.0.17 ...

Details here.

Upvotes: 1

Paul Tomblin
Paul Tomblin

Reputation: 182782

my $th = $dbh->prepare(qq{SELECT bi_exim_id FROM bounce_info WHERE bi_exim_id = '$exid'});
$th->execute();
my $found = 0;
while ($th->fetch()) 
{
   $found = 1;
}

Your query won't return anything if the row doesn't exist, so you can't de-reference the fetch.

Update: you might want to re-write that as

my $found = $th->fetch();

Upvotes: 13

brian d foy
brian d foy

Reputation: 132812

The DBD::mysql driver has a the rows() method that can return the count of the results:

$sth = $dbh->prepare( ... );
$sth->execute;
$rows = $sth->rows;

This is database-driver specific, so it might not work in other drivers, or it might work differently in other drivers.

Upvotes: 38

runrig
runrig

Reputation: 6524

Why don't you just "select count(*) ..."??

my ($got_id) = $dbh->selectrow_array("SELECT count(*) from FROM bounce_info WHERE bi_exim_id = '$exid'");

Or to thwart Little Bobby Tables:

my $q_exid = $dbh->quote($exid);
my ($got_id) = $dbh->selectrow_array("SELECT count(*) from FROM bounce_info WHERE bi_exim_id = $q_exid");

Or if you're going to execute this a lot:

my $sth = $dbh->prepare("SELECT count(*) from FROM bounce_info WHERE bi_exim_id = ?");
....save $sth (or use prepare_cached()) and then later
my ($got_id) = $dbh->selectrow_array($sth, undef, $exid);

Upvotes: 5

Dave Sherohman
Dave Sherohman

Reputation: 46187

The only reliable way to find out how many rows are returned by a SELECT query is to fetch them all and count them. As stated in the DBI documentation:

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.

However, when you get down to it, you almost never need to know that in advance anyhow. Just loop on while ($sth->fetch) to process each row or, for the special case of a query that will only return zero or one rows,

if ($sth->fetch) {
  # do stuff for one row returned
} else {
  # do stuff for no rows returned
}

Upvotes: 1

jrockway
jrockway

Reputation: 42674

In general, I'm not sure why people are so afraid of exceptions. You catch them and move on.

my $sth = prepare ...
$sth->execute;
my $result = eval { $sth->fetchrow_arrayref->[1] };

if($result){ say "OH HAI. YOU HAVE A RESULT." }
else       { say "0 row(s) returned."         }

In this case, though, Paul's answer is best.

Also, $sth->rows doesn't usually work until you have fetched every row. If you want to know how many rows match, then you have to actually ask the database engine the question you want to know the answer to; namely select count(1) from foo where bar='baz'.

Upvotes: 2

user3458
user3458

Reputation:

Change select to always return something? This should work in Sybase, dunno about other DBs.

my $th = $dbh->prepare(qq{SELECT count(*) FROM bounce_info WHERE bi_exim_id = '$exid'});
$th->execute();
if ($th->fetch()->[0]) {
....
}

Upvotes: 4

Related Questions