230

There are many conflicting statements around. What is the best way to get the row count using PDO in PHP? Before using PDO, I just simply used mysql_num_rows.

fetchAll is something I won't want because I may sometimes be dealing with large datasets, so not good for my use.

Do you have any suggestions?

matteo
  • 2,934
  • 7
  • 44
  • 59
James
  • 5,942
  • 15
  • 48
  • 72

21 Answers21

309

When you need only the number of rows, but not the data itself, such a function shouldn't be used anyway. Instead, ask the database to do the count, with a code like this:

$sql = "SELECT count(*) FROM `table` WHERE foo = ?"; 
$result = $con->prepare($sql); 
$result->execute([$bar]); 
$number_of_rows = $result->fetchColumn(); 

For getting the number of rows along with the data retrieved, PDO has PDOStatement::rowCount(), which apparently does work in MySql with buffered queries (enabled by default).

But it's not guaranteed for other drivers. From the PDO Doc:

For most databases, PDOStatement::rowCount() does not return the number of rows affected by a SELECT statement. Instead, use PDO::query() to issue a SELECT COUNT(*) statement with the same predicates as your intended SELECT statement, then use PDOStatement::fetchColumn() to retrieve the number of rows that will be returned. Your application can then perform the correct action.

But in this case you can use the data itself. Assuming you are selecting a reasonable amount of data, it can be fetched into array using PDO::fetchAll(), and then count() will give you the number of rows.

EDIT: The above code example uses a prepared statement, but if a query doesn't use any variables, one can use query() function instead:

$nRows = $pdo->query('select count(*) from blah')->fetchColumn(); 
echo $nRows;
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
karim79
  • 339,989
  • 67
  • 413
  • 406
  • 1
    this would mean doing an extra database query. I assume he has already done a select query and now wants to know how many rows were returned. – nickf May 19 '09 at 15:17
  • 1
    nickf is correct. mysql_num_rows() won't work when using PDO though will it? – James May 19 '09 at 15:19
  • True, apparently there's PDOStatement::rowCount() but that does not work in MySql – karim79 May 19 '09 at 15:20
  • Yea rowCount() fails with MySQL unfortunately. – James May 19 '09 at 15:21
  • After a bit of googling, it's starting to look like the select count(*) might be the prettiest approach! Weird – karim79 May 19 '09 at 15:26
  • 11
    Using this approach, `fetchColumn()` returns a string "1234" ... your EDIT has `echo count($nRows);` - `count()` is an array function :P. I'd also recommend type casting the result from `fetchColumn()` to an integer. `$count = (int) $stmt->fetchColumn()` – Cobby May 26 '11 at 23:59
  • 1
    @karim79 The non-prepared statement approach is returning 1 only instead of actual number of rows. The prepared statement works fine. What can be the issue ? – SilentAssassin Feb 26 '13 at 10:23
  • 1
    @SilentAssassin I also encountered the same problem. Cobbys comment, just a little bit above your comment, tells to not use `count($nRows)` as it is meant for arrays. I guess the function sees the result as an array with a result (a value or null) anyway and always returns 1. – Nurp Nov 03 '13 at 22:00
  • And right now quest. What use and what is faster rowCount() or fetchColumn() or query mysql> SELECT SQL_CALC_FOUND_ROWS * FROM ... ; mysql> SET @rows = FOUND_ROWS(); – fearis Sep 22 '14 at 23:07
  • 1
    That and selecting all data just to do a count is not best for performance anyways. – Tom Tom Jan 26 '17 at 17:23
  • According to php PDOStatement::rowCount() returns the number of rows affected by a DELETE, INSERT, or UPDATE statement. So it will not work with select as per PHP manual. – Senior PHP Developer Jan 29 '19 at 11:46
  • What if i need to select columns also? – Ingus Apr 07 '20 at 08:32
  • This method can't work if the SELECT statement contains a LIMIT – migli Dec 11 '22 at 08:52
95

As I wrote previously in an answer to a similar question, the only reason mysql_num_rows() worked is because it was internally fetching all the rows to give you that information, even if it didn't seem like it to you.

So this behavior is replicated in pdo_mysql driver as well (for the buffered queries that are used by default). Which means that with MySQL you can use PDO::rowCount() the same way as mysql_num_rows() (i.e. almost never, as this function is really useless).

For the other drivers, that don't support returning the number of rows found but the SELECT query (or with MySQL but when unbuffered query is used), your options are:

  1. Use PDO's fetchAll() function to fetch all the rows into an array, then use count() on it.
  2. Do an extra query to SELECT COUNT(*), as karim79 suggested.
  3. Use MySQL's FOUND_ROWS() function UNLESS the query had SQL_CALC_FOUND_ROWS or a LIMIT clause (in which case the number of rows that were returned by the query and the number returned by FOUND_ROWS() may differ). However, this function is deprecated and will be removed in the future.
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
Chad Birch
  • 73,098
  • 23
  • 151
  • 149
  • 8
    Thank you for educating me further about mysql_num_rows() looks like that may be an important bottleneck I was giving myself. Thanks again. – James May 19 '09 at 15:41
  • 2
    fetch_all actually is fetchAll() :) – dynamic Mar 13 '15 at 14:24
  • 2
    Option 2 is not advisable if the result is big. – Edson Horacio Junior Nov 21 '18 at 14:22
  • The FOUND_ROWS() will be removed from MySQL so please check the link to FOUND_ROWS before using this if you were already familiar with this. – anatak May 22 '20 at 01:05
  • `FOUND_ROWS()` is NOT the same thing as counting the rows returned in the result of the last query. If you do a `SELECT SQL_CALC_FOUND_ROWS ... LIMIT 100`, the number of rows in the result (what the OP asks for) is limited by the limit clause, while `SQL_CALC_FOUND_ROWS()` will return the total number without the limit. I'll propose an edit removing the "use MySQL's FOUND_ROWS()" option – matteo Sep 23 '20 at 17:28
  • Actually, `FOUND_ROWS()` is **never** right if you use a `LIMIT` clause, even if you haven't used `SQL_CALC_FOUND_ROWS`. As stated in the docs: "If the statement includes a LIMIT clause, FOUND_ROWS() returns the **number of rows up to the limit**. For example, `FOUND_ROWS()` **returns 60 if the statement includes `LIMIT 50, 10`**". That is not, in any way, equivalent to the number of rows returned by the query. – matteo Sep 25 '20 at 13:49
  • The answer itself is fair. Only it's trying to solve a problem that doesn't exist. – Your Common Sense Sep 25 '20 at 14:53
47

As it often happens, this question is very confusing. People are coming here having two different tasks in mind:

  1. They need to know how many rows in the table (or match some condition). For this purpose rowCount() should never be used, no matter whether it's available or not.
  2. They need to know whether a query returned any rows. For this purpose this function is rather useless - again, no matter whether it's available or not.

That's two absolutely different tasks that have nothing in common and cannot be solved by the same function. Ironically, for neither of them the actual PDOStatement::rowCount() function has to be used.

Let's see why

Counting rows in the table

Before using PDO I just simply used mysql_num_rows().

Means you already did it wrong. Using mysql_num_rows() or rowCount() to count the number of rows in the table is a real disaster in terms of consuming the server resources. A database has to read all the rows from the disk, consume the memory on the database server, then send all this heap of data to PHP, consuming PHP process' memory as well, burdening your server with absolute no reason.
Besides, selecting rows only to count them simply makes no sense. A count(*) query has to be run instead. The database will count the records out of the index, without reading the actual rows and then only one row returned.

For this purpose the code suggested in the accepted answer is fair, save for the fact it won't be an "extra" query but the only query to run.

Counting the number rows returned.

The second use case is not as disastrous as rather pointless: in case you need to know whether your query returned any data, you always have the data itself!

Say, if you are selecting only one row. All right, you can use the fetched row as a flag:

$stmt->execute();
$row = $stmt->fetch();
if (!$row) { // here! as simple as that
    echo 'No data found';
}

In case you need to get many rows, then you can use fetchAll().

fetchAll() is something I won't want as I may sometimes be dealing with large datasets

Yes of course, for the first use case it would be twice as bad. But as we learned already, just don't select the rows only to count them, neither with rowCount() nor fetchAll().

But in case you are going to actually use the rows selected, there is nothing wrong in using fetchAll(). Remember that in a web application you should never select a huge amount of rows. Only rows that will be actually used on a web page should be selected, hence you've got to use LIMIT, WHERE or a similar clause in your SQL. And for such a moderate amount of data it's all right to use fetchAll(). And again, just use this function's result in the condition:

$stmt->execute();
$data = $stmt->fetchAll();
if (!$data) { // again, no rowCount() is needed!
    echo 'No data found';
}

And of course it will be absolute madness to run an extra query only to tell whether your other query returned any rows, as it suggested in the two top answers.

Counting the number of rows in a large resultset

In such a rare case when you need to select a real huge amount of rows (in a console application for example), you have to use an unbuffered query, in order to reduce the amount of memory used. But this is the actual case when rowCount() won't be available, thus there is no use for this function as well.

Hence, that's the only use case when you may possibly need to run an extra query, in case you'd need to know a close estimate for the number of rows selected.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • it is useful if the API needs to print out the total results of a search query. it will only give you 10 or 15 rows back, but it also should tell you that there are 284 total results. – Andres SK Jun 03 '13 at 01:24
  • 5
    @andufo It is not. Please remember: a developer should **never** do it this way. Search query should never return all 284 rows. 15 have to be returned to show and **one** row from separate query to tell that 284 were found. – Your Common Sense Jun 03 '13 at 03:34
  • 3
    This is a very good point - unintuitive at first, but valid. Most people forget that two simple SQL queries are *way* faster than a slightly bigger one. To justify any counting, you'd have to have a very slow query that cannot be optimized and will propably yield few results. Thanks for pointing that out! – PeerBr Jan 30 '14 at 21:39
  • @Your Common Sense: Just du be sure: won't fetchAll() be a bad idea if the resultset is very large? Wouldn't it be better then to use fetch() to get data successive. – timmornYE May 12 '16 at 06:37
  • @timmornYE that is exactly what is said in the last paragraph of my answer – Your Common Sense May 12 '16 at 07:33
  • After using `$stmt->fetchAll()` in order to count the rows, will you be able to use `$stmt->fetch()` to fetch the results one by one? – matteo Sep 23 '20 at 17:48
  • 2
    @matteo you won't have any reason to. The rows are already fetched into a handy array, you just have to foreach() it – Your Common Sense Sep 23 '20 at 17:50
  • @YourCommonSense given your user name it's Ironic that I need to explain this, but yes I have plenty of reasons to. For example, I'm upgrading legacy code that used mysqli, and used a $mysqli->num_rows beforehand for whatever reason, and then started fetching results in a while() loop. Of course that's not the way I would have designed it, but legacy software using mysqli does that a lot, perhaps because the mysqli api kind of encourages it. If I have to rewrite that to use the array obtained by fetchAll it's a lot more rewriting. – matteo Sep 24 '20 at 19:01
  • 1
    Well, at least using num rows "is not the way you'd have designed it", so, probably there *is* some common sense in not using it (: – Your Common Sense Sep 24 '20 at 19:15
24

I ended up using this:

$result = $db->query($query)->fetchAll();

if (count($result) > 0) {
    foreach ($result as $row) {
        echo $row['blah'] . '<br />';
    }
} else {
    echo "<p>Nothing matched your query.</p>";
}
Eric Warnke
  • 1,325
  • 12
  • 18
15

This post is old but Getting row count in php with PDO is simple

$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
Junius L
  • 15,881
  • 6
  • 52
  • 96
  • 3
    See the documentation quoted in karim79 's answer. This sometimes works but is not reliable. – octern Jul 07 '15 at 01:32
  • 4
    Selecting all the data only to count it, is against the most basic rules of database interaction. – Your Common Sense Apr 01 '21 at 07:20
  • [`PDOStatement::rowCount()`](https://www.php.net/manual/en/pdostatement.rowcount.php) **does not work reliably** with `SELECT` statements. Only some databases return the count you expect (like MySQL in buffered mode), but others (like SQLite) will fail. – NullUserException Jun 09 '23 at 04:14
13

This is super late, but I ran into the problem and I do this:

function countAll($table){
   $dbh = dbConnect();
   $sql = "select * from `$table`";

   $stmt = $dbh->prepare($sql);
    try { $stmt->execute();}
    catch(PDOException $e){echo $e->getMessage();}

return $stmt->rowCount();

It's really simple, and easy. :)

Dan
  • 1,065
  • 1
  • 11
  • 18
  • 24
    Selecting all the data only to count it, is against the most basic rules of database interaction. – Your Common Sense Jun 27 '16 at 04:32
  • Maybe you want to have a progress bar for all of the returned values, so you need to know the number of rows upfront. – Jonny Mar 27 '19 at 02:42
  • @Jonny DON'T you understand such a primitive logical equation: in case dude is showing all returned values, he can simply count them **without this function**? Not to mention that nobody's talking about "progress bars" here. The question is how to get the number of rows, not how to show a progress bar. And for getting the number of rows this function is just horrible. – Your Common Sense Mar 25 '23 at 08:59
  • [`PDOStatement::rowCount()`](https://www.php.net/manual/en/pdostatement.rowcount.php) **does not work reliably** with `SELECT` statements. Only some databases return the count you expect (like MySQL in buffered mode), but others (like SQLite) will fail. – NullUserException Jun 09 '23 at 04:14
5

This is an old post, but getting frustrated looking for alternatives. It is super unfortunate that PDO lacks this feature, especially as PHP and MySQL tend to go hand in hand.

There is an unfortunate flaw in using fetchColumn() as you can no longer use that result set (effectively) as the fetchColumn() moves the needle to the next row. So for example, if you have a result similar to

  1. Fruit->Banana
  2. Fruit->Apple
  3. Fruit->Orange

If you use fetchColumn() you can find out that there are 3 fruits returned, but if you now loop through the result, you only have two columns, The price of fetchColumn() is the loss of the first column of results just to find out how many rows were returned. That leads to sloppy coding, and totally error ridden results if implemented.

So now, using fetchColumn() you have to implement and entirely new call and MySQL query just to get a fresh working result set. (which hopefully hasn't changed since your last query), I know, unlikely, but it can happen. Also, the overhead of dual queries on all row count validation. Which for this example is small, but parsing 2 million rows on a joined query, not a pleasant price to pay.

I love PHP and support everyone involved in its development as well as the community at large using PHP on a daily basis, but really hope this is addressed in future releases. This is 'really' my only complaint with PHP PDO, which otherwise is a great class.

Eric
  • 67
  • 1
  • 1
  • This "answer" just makes no sense. It is written out of confusion and keeps confusing everything all the way through. I have no idea why fetchColumn() is discussed here at all. Let alone why someone would use it at all if they need all the row values. Why not to use fetch() instead? – Your Common Sense Apr 30 '22 at 06:59
4

Answering this because I trapped myself with it by now knowing this and maybe it will be useful.

Keep in mind that you cant fetch results twice. You have to save fetch result into array, get row count by count($array), and output results with foreach. For example:

$query = "your_query_here";
$STH = $DBH->prepare($query);
$STH->execute();
$rows = $STH->fetchAll(PDO::FETCH_ASSOC);
//all your results is in $rows array
if (count($rows) > 0) {             
    foreach ($rows as $row) {
        //output your rows
    }                       
}
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
csharp newbie
  • 647
  • 1
  • 12
  • 31
4

If you just want to get a count of rows (not the data) ie. using COUNT(*) in a prepared statement then all you need to do is retrieve the result and read the value:

$sql = "SELECT count(*) FROM `table` WHERE foo = bar";
$statement = $con->prepare($sql); 
$statement->execute(); 
$count = $statement->fetch(PDO::FETCH_NUM); // Return array indexed by column number
return reset($count); // Resets array cursor and returns first value (the count)

Actually retrieving all the rows (data) to perform a simple count is a waste of resources. If the result set is large your server may choke on it.

madfish
  • 51
  • 1
  • This code uses the same approach (though using more code) as the accepted answer written 7 years prior. What was the point writing this answer? – Your Common Sense Mar 25 '23 at 09:51
3

Have a look at this link: http://php.net/manual/en/pdostatement.rowcount.php It is not recommended to use rowCount() in SELECT statements!

2

When it is matter of mysql how to count or get how many rows in a table with PHP PDO I use this

// count total number of rows
$query = "SELECT COUNT(*) as total_rows FROM sometable";
$stmt = $con->prepare($query);

// execute query
$stmt->execute();

// get total rows
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$total_rows = $row['total_rows'];

credits goes to Mike @ codeofaninja.com

Robert
  • 490
  • 1
  • 5
  • 17
  • This code uses the same approach (though using more code) as the accepted answer written 8 years prior. What was the point writing this answer? – Your Common Sense Mar 25 '23 at 09:52
0

To use variables within a query you have to use bindValue() or bindParam(). And do not concatenate the variables with " . $variable . "

$statement = "SELECT count(account_id) FROM account
                  WHERE email = ? AND is_email_confirmed;";
$preparedStatement = $this->postgreSqlHandler->prepare($statement);
$preparedStatement->bindValue(1, $account->getEmail());
$preparedStatement->execute();
$numberRows= $preparedStatement->fetchColumn();

GL

Braian Coronel
  • 22,105
  • 4
  • 57
  • 62
-1

when you make a COUNT(*) in your mysql statement like in

$q = $db->query("SELECT COUNT(*) FROM ...");

your mysql query is already counting the number of result why counting again in php? to get the result of your mysql

$q = $db->query("SELECT COUNT(*) as counted FROM ...");
$nb = $q->fetch(PDO::FETCH_OBJ);
$nb = $nb->counted;

and $nb will contain the integer you have counted with your mysql statement a bit long to write but fast to execute

Edit: sorry for the wrong post but as some example show query with count in, I was suggesting using the mysql result, but if you don't use the count in sql fetchAll() is efficient, if you save the result in a variable you won't loose a line.

$data = $dbh->query("SELECT * FROM ...");
$table = $data->fetchAll(PDO::FETCH_OBJ);

count($table) will return the number of row and you can still use the result after like $row = $table[0] or using a foreach

foreach($table as $row){
  print $row->id;
}
-1

I tried $count = $stmt->rowCount(); with Oracle 11.2 and it did not work. I decided to used a for loop as show below.

   $count =  "";
    $stmt =  $conn->prepare($sql);
    $stmt->execute();
   echo "<table border='1'>\n";
   while($row = $stmt->fetch(PDO::FETCH_OBJ)) {
        $count++;
        echo "<tr>\n";
    foreach ($row as $item) {
    echo "<td class='td2'>".($item !== null ? htmlentities($item, ENT_QUOTES):"&nbsp;")."</td>\n";
        } //foreach ends
        }// while ends
        echo "</table>\n";
       //echo " no of rows : ". oci_num_rows($stmt);
       //equivalent in pdo::prepare statement
       echo "no.of rows :".$count;
Mat
  • 202,337
  • 40
  • 393
  • 406
-1

For straight queries where I want a specific row, and want to know if it was found, I use something like:

function fetchSpecificRow(&$myRecord) {
    $myRecord = array();
    $myQuery = "some sql...";
    $stmt = $this->prepare($myQuery);
    $stmt->execute(array($parm1, $parm2, ...));
    if ($myRecord = $stmt->fetch(PDO::FETCH_ASSOC)) return 0;
    return $myErrNum;
}
Misa Lazovic
  • 2,805
  • 10
  • 32
  • 38
-1

The simplest way, it is only 2 lines,

$sql = $db->query("SELECT COUNT(*) FROM tablename WHERE statement='condition'");
echo $sql->fetchColumn();
Bbbb
  • 69
  • 2
  • 10
-1

So, the other answers have established that rowCount() shouldn't be used to count the rows of a SELECT statement. The documentation even says, that :

PDOStatement::rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed by the corresponding PDOStatement object.

So it's okay for other queries, but not great for SELECT. Most answers suggest that you should make two queries, one to count rows, and one to get the subset of records you need. However, you could query the row count and your subset of the data in one request. This is a bit of an exercise in code golf, but could actually prove more efficient than two requests if the request time is a bit costly and these requests are made frequently.

If you're in PostgreSQL you can provide clean JSON output, like so:

WITH mytable as (VALUES(1,2,3),(4,5,6),(7,8,9),(10,11,12))
SELECT
    jsonb_build_object(
        'rowcount', (SELECT count(1) FROM mytable)
        ,'data', (
            SELECT jsonb_agg(data.*)
            FROM (
                SELECT *
                FROM mytable
                WHERE column1 > 1 -- pagination offset
                ORDER BY column1
                LIMIT 2 -- page size
            ) as data
        )
    ) jsondata

Output:

{"data": [
    {
      "column1": 4,
      "column2": 5,
      "column3": 6
    },
    {
      "column1": 7,
      "column2": 8,
      "column3": 9
    }
  ],
"rowcount": 4
}

If you're not in postgres, those functions won't be available, but you could do this:

WITH mytable as (VALUES(1,2,3),(4,5,6),(7,8,9),(10,11,12))
SELECT
    (SELECT count(1) FROM mytable) as rowcount
    ,data.*
FROM (
    SELECT *
    FROM mytable as mytable(column1, column2, column3)
    WHERE column1 > 1 -- pagination offset
    ORDER BY column1
    LIMIT 2 -- page size
) as data

but it will return the rowcount on every row, which might be a bit wasteful:

rowcount column1 column2 column3
4 4 5 6
4 7 8 9
ADJenks
  • 2,973
  • 27
  • 38
-2

Short answer: I wanted this idea to be as short as 1 line of code:

$stmt = $PDO->query( "SELECT * FROM my_table" ); 
$rows = (int) $PDO->query('SELECT FOUND_ROWS()')->fetchColumn();

use SELECT FOUND_ROWS(), then fetchColumn(), then class it as an (int) Dude below points out FOUND_ROWS() is dep in MySQL8, but, how many sites are you managing on MySQL5.x right now?


Shortest possible copy/paste examples:

EXAMPLE 1 - COUNT(*) cast as (int) - not do stuff with rows

$rowCount = (int) $PDO->query( "SELECT COUNT(*) FROM my_table WHERE this='that' " )->fetchColumn();

EXAMPLE 2 - foreach() count + do stuff with rows

$stmt = $PDO->query( "SELECT * FROM my_table WHERE this='that' " ); 
$rowCount = 0; foreach($stmt as $row) {$rowCount ++;} 

EXAMPLE 3 - shorthand count of fetchAll() + do stuff with rows

    $haystack = $PDO->prepare("SELECT * FROM my_table WHERE this=:that");
    $haystack->execute($needles);
    $rows = $haystack->fetchAll();
    $rowCount = count($rows)>0 ? count($rows) : 0;

EXAMPLE 4 - where rowCount() can be trusted: INSERT/UPDATE (but that's not a SELECT)

    $update = $PDO->prepare("UPDATE my_table SET this=:that WHERE ID=:MYID");
    $update->execute($execute);
    $rowCount = $update->rowCount();

TLDR Version:

Since PHP8 I have just been using rowCount() with impunity, no problems. Then when working on a stack of WP updates across other peoples servers I find rowCount() failing to return the proper number of rows

Of course, I google it and find a decades long debate. I did not like most answers, so I read the documentation https://www.php.net/manual/en/pdostatement.rowcount.php

According to docs DELETE, INSERT, or UPDATE work with rowCount(), but even on the PHP.NET examples page as far back as 11 years ago folks have been wrestling with the best/easiest way to just count the result.

After testing about 20 answers (stackOverflow to Php.net) the final script [below] worked best to patch 32 WP sites across 20 different servers, 5 hosts and several versions of PHP ranging from 7.4 to 8.1 (GoDaddy, Bluehost, WPengine, InMotion, Amazon AWS Lightsail)

  • WP Engine was always using MySQL 5.7
  • Typically Amazon AWS Lightsail was also using MySQL 5.7
  • Other Amazon AWS configs and shared hosts (GoDaddy, BH, Gator) all seem to be on standard PHP Drivers for MySQL (mysqli, ext/mysqli, PDO_MYSQL, PHP_MYSQLND)

Note that $opts turned out to be important (shared, cloud, dedicated)

    $db_host = "localhost"; //DB_HOST in WP
    $db_name = "yours";     //DB_NAME in WP
    $db_charset = "utf8mb4";//usually
    $dsn = "mysql:host=$db_host;dbname=$db_name;charset=$db_charset";
    $opt = [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false,
        PDO::MYSQL_ATTR_LOCAL_INFILE => true, 
        // on/off based on your need: https://www.php.net/manual/en/ref.pdo-mysql.php
        PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
        //makes sure addons like counting rows is enabled
    ];
   $PDO = new PDO($dsn, $db_user, $db_password, $opt);
   
   // Run the Jewels - do your stuff here
   $stmt = $PDO->query( "SELECT * FROM wp_posts WHERE post_status='publish' " );

   // one-line count, no shenanagans
   $rows = (int) $PDO->query('SELECT FOUND_ROWS()')->fetchColumn();

   echo "tell me something useful: $rows";

It should also be noted that this has some merit, but ONLY if your table is MYISAM. I mention this because most of the MySQL DB's I've been working with are WordPress and they are all running InnoDB, which doesn't give you an accurate row count for STATUS

$sth = $PDO->query("SHOW TABLE STATUS LIKE my_table"); 
$dbstatus = $sth->fetch(PDO::FETCH_ASSOC);
print_r($dbstatus);
    /* dbstatus[Engine] => InnoDB 
        - if its InnoDB you can not trust the count
        - use MYISAM for correct rowcount
    dbstatus[Rows] => 51
    dbstatus[Avg_row_length] => 321
    dbstatus[Data_length] => 16384
    */
 // theoretically, you now have this, only trust MYISAM, InnoDB will lie to you
 $rowcount = $dbstatus['Rows'];

in closing, I think it would be great if rowCount() always consistently worked across SELECT, UPDATE, INSERT, but it turns out IRL: it doesn't...

if you're in a situation where you need to patch some table across multiple websites, multiple hosts, multiple configs, and you need a reliable "count" when selecting, then updating, then inserting and you noticed that rowCount()seems to be lying to you? then this is the page to be on, and at least one of these solutions here should do the trick... probably going to be different for a lot of folks.

in my case I just needed to "touch the table" and see how many rows were affected by a particular problem. I just wanted a count, a simple copy paste script that lets me SELECT X tell me "how many". I wanted to do that in stack workflow in PHP.

Now, lets dress up a real world scenario: Consider a World City Population Database with 4 million unique cities and towns from every country in the world deployed across 32 websites and 3 countries, all with different rules for how they want to use that data, an AJAX updating tool, no API and the need to allow each site owner to search, check, update data based on their own criteria and decision to import CSV data.


THERE ARE FOUR LIGHTS

EXAMPLE 1 - COUNT(*) then cast as (int)

//JUST GIVE ME SOME TOTALS: Ok, then COUNT(*) cast as (int)
// in this scenario I just want a number
// I trust my WHERE clause and do not need to do stuff
$rowCount = (int) $PDO->query("
    SELECT COUNT(*) 
    FROM world_cities WHERE incorporated='TRUE' ")->fetchColumn();
echo $rowCount." are incorporated"; //simple, it was cast as (int)

EXAMPLE 2 - foreach() count

//GIVE ME TOTALS AND LET ME DO STUFF:
// in this scenario I want a number
// and I want to do stuff with rows
// and I trust my WHERE clause 
$stmt = $PDO->query( "
    SELECT * 
        FROM world_cities WHERE incorporated='TRUE' " ); 
$rowCount = 0;
foreach($stmt as $row) {$rowCount ++;} //do stuff in there
echo $rowCount." need to be updated"; //great
//note: count($stmt) notwork, $stmt is a PDOobject not an array

EXAMPLE 3 - shorthand count of fetchAll()

//GIVE ME TOTALS AND LET ME DO STUFF BUT WITH UNTRUSTED DATA:
// in this scenario I want a number
// and I want to do stuff with rows
// but I do not trust the WHERE clause 
   $user_search = $_POST['user_search']; //clean that
   $inc_search = $_POST['inc_search'];  //clean that
   $needles = array(
         'user_search' => $user_search,
         'inc_search' => $inc_search
    );
    $haystack = $PDO->prepare("
        SELECT * FROM world_cities 
        WHERE city_name=:user_search AND incorporated=:inc_search");
    // execute, fetch, count
    $haystack->execute($needles);
    $rows = $haystack->fetchAll();
    $rowCount = count($rows)>0 ? count($rows) : 0;
    echo $rowCount." matched your search";

EXAMPLE 4 - the one and only case where rowCount() worked well, consistently

//GIVE ME TOTALS AFTER I UPDATE RECORDS
// in this scenario I want a number
// and I want to push data in
try {

    $execute = array(
        'PKEY_ID'           => 938572,
        'NEW_STATUS'        => 'FALSE',
        'NEW_POPULATION'    => '9285'
    );

    $update = $PDO->prepare("
    UPDATE world_cities 
    SET 
        incorporated=:NEW_STATUS, 
        population=:NEW_POPULATION
    WHERE PKEY_ID=:PKEY_ID ");

    $update->execute($execute);
    $update_rowCount = $update->rowCount();
    
    if($update_rowCount == 0){
        //eh, why?
        print_r($execute);
    }else {
        echo $update_rowCount." records have been updated";
    }

} catch (PDOException $e) {
    // yuk, do tell...
    echo $e->getMessage();
}

In ALL scenarios, at the end, I just want "a total" to push up into the AJAX layer, or, at least to echo and tell me 'how many'.

Christian Žagarskas
  • 1,068
  • 10
  • 20
  • You forgot to mention what *driver* you are using for your "confirmation". Given that in 9 out of 10 projects PDO is used with MySQL, claiming that "It does not return the number of rows from SELECT" is not overly credible. – Your Common Sense Mar 25 '23 at 08:39
  • You are right. After spending all night on this (12hr) I have updated my post. – Christian Žagarskas Mar 25 '23 at 20:40
  • This updated answer makes very little sense. 1. FOUND_ROWS() is deprecated. 2. $opt has no effect on the count at all. 3. **your should use count(*) if you need only the number!!!** Can't you understand this simple fact, one should never select A LOT of data only to count it?! – Your Common Sense Mar 25 '23 at 20:45
  • Depricated as of as of MySQL 8... and how many sites are you managing on MySQL 8 vs MySQL 5? – Christian Žagarskas Mar 25 '23 at 20:59
  • I don't know what your stack looks like... but I just spent 12 hours on this. there's nothing you're going to be able to say to me here that's going to convince me the script that I posted doesn't work in a real world scenario, especially after trying to copy and paste almost everything on this page from the other answers and seeing what works best. 'find it out' – Christian Žagarskas Mar 25 '23 at 21:12
  • Now, the topmost part of your answer is slightly better, but at whole this answer is so muddled and inconsistent that it hardly can be considered helpful. You are still confusing a lot of things and you still don't understand the very basic principles either. – Your Common Sense Mar 26 '23 at 06:54
  • For example, for some reason you never explain your problem with "rowCount() failing to return the proper number of rows". Which is obviously being a **problem with your own code**, not the function. rowCount always returns the correct number. And not a single answer here nor PHP manual says otherwise. Only it says that for some drivers it may not be available at all. And you cannot even understand the difference between "not available at all" and "available but returns incorrect number". For some reason you pulled here a SHOW TABLE STATUS query that's absolutely unrelated to rowCount(). – Your Common Sense Mar 26 '23 at 06:57
  • The only two conclusions you should have made, are: 1. rowCount should **never, never, never** be used to get only the number, no matter if it's available or not. count(*) should be used instead. 2. For Mysql, rowCount() is **always available** and **always** returns the **correct number** for SELECT query. But being used with SELECT queries that return the data to be used, this function is rather **useless**, because when you select the data, you can always count it as well. – Your Common Sense Mar 26 '23 at 07:03
  • rowCount() does not "always" return the correct number. Fire up a DB with 100k records as InnoDB, you will NOT get the correct rowCount() - find out why. – Christian Žagarskas Apr 21 '23 at 19:22
  • here's what's going to happen here, people in real world scenarios will come across this post, copy and paste the code that I've presented and it will work, and it will solve their problem. I don't know what they're going to get out of whatever it is you're trying to do here... but I would encourage you: "find it out" IRL. The spirit of my post here on stack overflow is very simple: "provide a copy paste solution to a problem I ran into that I didn't see answered well enough to fix the problem I had". and I'm having trouble seeing how any of your comments contribute to that – Christian Žagarskas Apr 21 '23 at 19:28
  • as for your claim about count(*) and rowCount, I presume you haven't actually run into this problem in the real world, so let me set this on the table for you but also say "try it" try to use that method on INNODB, you'll see. which is why I point that out in my OP with regards to MyISAM https://dba.stackexchange.com/questions/17926/why-doesnt-innodb-store-the-row-count so no dude. no. it does not always return the correct result. and when having 100% accurate numbers is important? it's something that needs to be on your radar. (for example totaling up the exact number of zip codes in a state) – Christian Žagarskas Apr 21 '23 at 19:32
-4

Here's a custom-made extension of the PDO class, with a helper function to retrieve the number of rows included by the last query's "WHERE" criteria.

You may need to add more 'handlers', though, depending on what commands you use. Right now it only works for queries that use "FROM " or "UPDATE ".

class PDO_V extends PDO
{
    private $lastQuery = null;

    public function query($query)
    {
        $this->lastQuery = $query;    
        return parent::query($query);
    }
    public function getLastQueryRowCount()
    {
        $lastQuery = $this->lastQuery;
        $commandBeforeTableName = null;
        if (strpos($lastQuery, 'FROM') !== false)
            $commandBeforeTableName = 'FROM';
        if (strpos($lastQuery, 'UPDATE') !== false)
            $commandBeforeTableName = 'UPDATE';

        $after = substr($lastQuery, strpos($lastQuery, $commandBeforeTableName) + (strlen($commandBeforeTableName) + 1));
        $table = substr($after, 0, strpos($after, ' '));

        $wherePart = substr($lastQuery, strpos($lastQuery, 'WHERE'));

        $result = parent::query("SELECT COUNT(*) FROM $table " . $wherePart);
        if ($result == null)
            return 0;
        return $result->fetchColumn();
    }
}
Venryx
  • 15,624
  • 10
  • 70
  • 96
  • The problem doesn't worth an effort. Such a number being needed so rarely that one don't need a dedicated extension. Not to mention it doesn't support prepared statements - the only reason to use PDO. – Your Common Sense Jul 15 '13 at 08:34
-4

You can combine the best method into one line or function, and have the new query auto-generated for you:

function getRowCount($q){ 
    global $db;
    return $db->query(preg_replace('/SELECT [A-Za-z,]+ FROM /i','SELECT count(*) FROM ',$q))->fetchColumn();
}

$numRows = getRowCount($query);
Bryan
  • 45
  • 1
  • 2
-4

There is a simple solution. If you use PDO connect to your DB like this:

try {

    $handler = new PDO('mysql:host=localhost;dbname=name_of_your_db', 'your_login', 'your_password'); 
    $handler -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

} catch (PDOException $e) { 

    echo $e->getMessage();
}

Now, if you want to know how many rows are existing in your table and you have for example column 'id' as the primary key, the query to DB will be:

$query = $handler->query("SELECT id FROM your_table_name");

And finally, to get the amount of the rows matching your query, write like this:

$amountOfRows = $query->rowCount();

Or you can write:

$query = $handler ->query("SELECT COUNT(id) FROM your_table_name");

$amountOfRows = $query->rowCount();

Or, if you want to know how many products there are in the table 'products' have the price between 10 and 20, write this query:

$query = $handler ->query("SELECT id FROM products WHERE price BETWEEN 10 AND 
20");

$amountOfRows = $query->rowCount();
Vlad
  • 1
  • 2