Как в PDO получить количество строк выбранных запросом SELECT?

Многие разработчики используют расширение PHP Data Objects (PDO) для PHP, предоставляющее разработчику простой и универсальный интерфейс для доступа к различным базам данных. У PDO множество плюсов, однако есть и минусы. PDO не предоставляет метод для узнавания количества полученных строк.

Для работы с БД разработчики PHP предлагают использовать расширение MySQLi или PDO. MySQLi является улучшенной версией устаревшего расширения MySQL. В этих PHP расширениях есть соответсвующие функции mysql_num_rows и mysqli_num_rows, которые получают число рядов в результате. А как же дела обстоят с PDO?

В PDO есть метод columnCount(), который возвращает столько столбцов сколько было запрошено в SELECT вне зависимости от количества рядов, т.е. вернёт количество столбцов даже если результата выборки нет, т.е. 0 рядов. В то же время в PDO нет надёжного метода, который бы вернул количество полученных в результате запроса строк/рядов. Как разработчики такой мощной вещи как PDO не сделали что-нибудь в стиле numRows? Как тогда в PHP Data Objects (PDO) получить количество строк выбранных запросом SELECT?

Дело в том, что определение количества выбранных записей с точки зрения концепции реляционных БД является нежелательной операцией, потому что требует полной выборки всего результата запроса в память, а не работы с указателем на текущий ряд выборки в БД. Если запрос возвращает большие данные с типом BLOB, то это может быть вообще невозможно. Как выйти из сложившейся ситуации? Лучше всего переписать алгоритм так, чтобы он не требовал определения общего количества записей, а просто работал с выборкой пока она не закончится.

Способы получения количества выбранных строк в PDO

Но если вам всё же сильно нужно количество выбранных строк, то есть некоторые варианты.

PDO метод rowCount

PDOStatement::rowCount() — негарантированный метод. Если последним запросом, запущенным соответствующим объектом PDOStatement, было SQL выражение SELECT, некоторые СУБД могут вернуть количество строк в результирующем наборе. Однако, такое поведение метода не гарантируется для всех баз данных, и это нужно учитывать при проектировании приложений.

Вообще, метод rowCount возвращает количество именно затронутых запросом рядов, т.е. изменённых/обновлённых или добавленных. Таким образом возвращается результата только после операций DELETE, INSERT или UPDATE, но никак не SELECT. И всё же некоторые СУБД, например, MySQL, при запросе SELECT возвращают количество результатов при вызове rowCount(), хотя делать этого не должны. В общем, если использовать rowCount, тогда о портабельности можно забыть т.к. он не будет работать со всеми БД.

Пример использования PDO метода rowCount:

$query = $dbh->query("SELECT id FROM users");
$members = $query->rowCount();

Можно записать короче:

$members = $dbh->query("SELECT id FROM users")->rowCount();

SQL оператор COUNT

Предварительно выполнять запрос с аналогичными условиями (WHERE и GROUP BY), но вместо списка полей выбрать COUNT(0):

SELECT COUNT(0) AS ROW_COUNT FROM <здесь все то же самое, что идет вашем запросе после слова FROM>

Если лень писать еще один запрос, то можно так (менее оптимально, но удобнее):

SELECT COUNT(0) AS ROW_COUNT FROM (<здесь ваш запрос в исходном виде>)

Пример использования SQL оператора COUNT:

$query = $dbh->query("SELECT COUNT(*) as count FROM users");
$query->setFetchMode(PDO::FETCH_ASSOC);
$row = $query->fetch();
$members = $row['count'];

PDO метод fetchAll

Выбрать все записи в память вызовом PDOStatement::fetchAll(), а затем просто проверить длину возвращенного этим вызовом массива. Требует памяти для хранения полного результата запроса:
Использование этого метода для извлечения строк больших результирующих наборов может пагубно сказаться на производительности системы и сетевых ресурсов. Вместо извлечения всех данных и их обработки в PHP рекомендуется использовать встроенные средства СУБД. Например, использование выражений WHETE и ORDER BY языка SQL может уменьшить размеры результирующего набора.

Какой вариант лучше

Как быстрее и правильнее подсчитать количество записей в базе данных MySQL, используя PDO? Правильнее работать с указателем и ничего не подсчитывать. Если же надо подсчитать, то всё индивидуально в зависимости от конкретного случая.

PDO и MySQLi

Почему в PHP расширениях MySQL и MySQLi есть метод для получения количества рядов запроса, а в PDO его нет? Потому что эту операцию поддерживает только СУБД MySQL, в то время как другие СУБД не поддерживают, как то рекомендуется в концепции реляционных БД. PHP расширения MySQL и MySQLi работают только с СУБД MySQL, в то время как PDO это портабельное решения, работающее с множеством СУБД.