У меня есть такой вопрос. Нужно узнать количество строк, которые вернулись в результате запроса SELECT. Наткнулся на различные варианты решения, но не могу понять какой правильный. На англоязычном StackOverflow почему-то очень многие рекомендуют просто вместо SELECT * FROM...
делать SELECT COUNT(*) FROM...
. А если у меня запрос на пол-экрана? С вложенными SELECT'ами, JOIN'ами и прочей прелестью? Мне его что, дублировать и добавлять везде COUNT()? Это криво, ИМХО. Не хочу так.
Есть второй вариант. Делать так:
$result = $pdo->query( 'SELECT * FROM `foo_bar`;' );
$count_result = $pdo->query( 'SELECT FOUND_ROWS();' );
$row_count = $count_result->fetchColumn();
И тогда нам вернётся то, что нужно. Но сразу вопрос - какие есть ограничения/баги в таком подходе? Подводные камни, либо неочевидные проблемы.
Есть третий вариант.
$result = $pdo->query( 'SELECT * FROM `foo_bar`;' );
$row_count = $result->rowCount();
В официальной доке по PDO написано, что rowCount() не работает с SELECT-запросами. Вернее, написано, что не гарантируется его корректная работа со всеми БД. Мои тесты показывают, что MySQL и PostgreSQL rowCount()
работает как надо.
Вопрос - как, всё-таки, правильно посчитать количество строк, которые выбрал SELECT-запрос?
P.S. count( $result->fetchAll() )
не предлагать. Ибо даже на жалких 100к записей... сами понимаете.
пример запроса:
$sql="SELECT SQL_CALC_FOUND_ROWS
metering.id,
metering.super_number,
metering.contract,
metering.name,
metering.city,
metering.address,
metering.phone,
metering.additional_phone,
metering.responsible,
metering.name_metering,
metering.number_card_customer,
metering.date_contract,
metering.date_metering,
metering.time_metering,
metering.status_customer,
metering.status,
metering.notification,
metering.date_status,
metering.date,
metering.creator,
metering.type,
metering.importance,
metering.source_attraction,
metering.production,
metering.status_metering,
metering.delivery_date,
metering.delivery_time,
metering.production_date,
user_retail.login AS login_responsible,
login_name_metering.login AS login_name_metering,
(SELECT COUNT(*) FROM prepay_metering WHERE prepay_metering.id_custom = metering.id) AS prepay,
(SELECT COUNT(*) FROM manufacture_cost WHERE manufacture_cost.name = metering.id) AS prepay_cost,
(SELECT COUNT(*) FROM `communication_metering` WHERE `communication_metering`.`id_metering` = `metering`.`id`) AS comment
FROM `metering`
LEFT JOIN user_retail ON user_retail.id = metering.responsible
LEFT JOIN user_retail AS login_name_metering ON login_name_metering.id = metering.name_metering
WHERE
metering.group_id = '".$_SESSION['group_id']."%' AND
metering.id LIKE '".$_SESSION['id']."%' AND
metering.super_number LIKE '".$_SESSION['super_number']."%' AND
metering.contract LIKE '".$_SESSION['contract']."%' AND
metering.name LIKE '%".$_SESSION['name']."%' AND
metering.city LIKE '%".$_SESSION['city']."%' AND
metering.address LIKE '%".$_SESSION['address']."%' AND
(metering.phone LIKE '%".$_SESSION['phone']."%' OR metering.additional_phone LIKE '%".$_SESSION['phone']."%') AND
metering.name_metering LIKE '".$_SESSION['name_metering']."%' AND
metering.number_card_customer LIKE '".$_SESSION['number_card_customer']."%' AND
metering.type LIKE '".$_SESSION['type']."%' AND
metering.responsible LIKE '".$_SESSION['name_responsible']."%'
";
Это исходный запрос. Дальше два экрана того, что с этим запросом происходит:
$_SESSION['status_customer']=$_POST['status_customer'];
if (!empty($_SESSION['status_customer'])) $sql.="AND metering.status_customer='".$_SESSION['status_customer']."'";
$_SESSION['status']=$_POST['status'];
if (!empty($_SESSION['status'])) $sql.="AND metering.status_customer=2 AND metering.status='".$_SESSION['status']."'";
$_SESSION['contract_yes']=$_POST['contract_yes'];
if (!empty($_SESSION['contract_yes'])) $sql.="AND metering.contract!=''";
$_SESSION['metering_sr']=$_POST['metering'];
if (!empty($_SESSION['metering_sr'])) $sql.="AND metering.metering='3'";
//Дата создания
if (!empty($_SESSION['date1'])) {
$start_date = substr($_SESSION['date1'],6,4).'-'.substr($_SESSION['date1'],3,2).'-'.substr($_SESSION['date1'],0,2);
$sql=$sql."AND metering.date >= '".$start_date."'";
}
if (!empty($_SESSION['date2'])) {
$end_date = substr($_SESSION['date2'],6,4).'-'.substr($_SESSION['date2'],3,2).'-'.substr($_SESSION['date2'],0,2);
$sql=$sql."AND metering.date <= '".$end_date."' ";
}
//Дата замера
if (!empty($_SESSION['date_metering1'])) {
$start_date = substr($_SESSION['date_metering1'],6,4).'-'.substr($_SESSION['date_metering1'],3,2).'-'.substr($_SESSION['date_metering1'],0,2);
$sql=$sql."AND metering.date_metering >= '".$start_date."'";
}
if (!empty($_SESSION['date_metering2'])) {
$end_date = substr($_SESSION['date_metering2'],6,4).'-'.substr($_SESSION['date_metering2'],3,2).'-'.substr($_SESSION['date_metering2'],0,2);
$sql=$sql."AND metering.date_metering <= '".$end_date."' ";
}
//Дата контракта
if (!empty($_SESSION['date_contract1'])) {
$start_date = substr($_SESSION['date_contract1'],6,4).'-'.substr($_SESSION['date_contract1'],3,2).'-'.substr($_SESSION['date_contract1'],0,2);
$sql=$sql."AND metering.date_contract >= '".$start_date."'";
}
if (!empty($_SESSION['date_contract2'])) {
$end_date = substr($_SESSION['date_contract2'],6,4).'-'.substr($_SESSION['date_contract2'],3,2).'-'.substr($_SESSION['date_contract2'],0,2);
$sql=$sql."AND metering.date_contract <= '".$end_date."' ";
}
//Дата доставки
if (!empty($_SESSION['date_delivery1'])) {
$start_date = substr($_SESSION['date_delivery1'],6,4).'-'.substr($_SESSION['date_delivery1'],3,2).'-'.substr($_SESSION['date_delivery1'],0,2);
$sql=$sql."AND metering.delivery_date >= '".$start_date."'";
}
if (!empty($_SESSION['date_delivery2'])) {
$end_date = substr($_SESSION['date_delivery2'],6,4).'-'.substr($_SESSION['date_delivery2'],3,2).'-'.substr($_SESSION['date_delivery2'],0,2);
$sql=$sql."AND metering.delivery_date <= '".$end_date."' ";
}
//Постраничное отображение
if (isset($_GET['pag'])) $pag=$_GET['pag'];
else $pag=0;
if (empty($_GET['lim'])) $sql .=" LIMIT ".$pag.", 30;";
Код не мой. Стоит задача перевести на PDO. Поэтому не хотелось бы видеть комментариев в стиле "кривые руки" и "кто так пишет", ибо будет не по адресу :) спасибо
Тебе это не нужно
Если коротко, то rowCount()
для селекта - это самая бессмысленная функция во всех Database API
Случаев, когда тебе может понадобиться что-то посчитать, ровно два:
count(данные)
.SELECT count(*)
из базы без вариантов.Всё, больше случаев нет, все любимые ламерами mysql_num_rows и rowCount не у дел.
SQL_CALC_FOUND_ROWS
не слишком оптимален, и может использоваться только если общее число строк невелико. В противном случае оно будет слишком сильно нагружать базу.
Поэтому, как правильно предложено в комментариях, сделать список полей переменной, и подставлять в зависимости от запроса либо ее, либо count(*). Т.е.
$select = "SELECT ид, супер нумбер, Авраам родил Исаака, Исаак родил Иакова, Иаков родил Иуду и братьев его и так далее до седьмого колена...";
$from = "FROM `metering` ... и далее весь развесистый SQL без лимита";
$count = $pdo->query("SELECT count(*) $from"))->fetchColumn();
$rows = $pdo->query("$select $from LIMIT 0, 20")->fetchAll();
Запрос делаешь таким образом "SELECT COUNT(*) FROM...", а в PDO делаешь после query этого запроса, fetchColumn(); оно вернёт число записей. http://php.net/manual/ru/pdostatement.fetchcolumn.php
Пример:
Запрос: $res = $pdo->query("SELECT COUNT(*) FROM table");
Получаем число: $countNum = $res->fetchColumn();
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Пытаюсь добавить в инфоблок новый элемент, но на выходе получаю ошибку такого рода "Для добавления элементов инфоблоков используйте вызов...
Такие сообщение(обратный звонок на сайте) приходят на почту каждый день, по 10-15 шт
Подскажите функции для защиты) вот сам запрос) Все параметры в метод передаются из строки запроса