SQL запрос, к-ство строк GROUP By

KA
На сайте с 03.01.2007
Offline
137
2779

Здравствуйте

Есть такой запрос

SELECT * FROM table WHERE ... GROUP BY artist LIMIT $offset, $per_page

Запрос выводит информацию для текущей страницы

Для постраничной навигации нужно узнать сколько всего будет строчек возвращать запрос без LIMIT

SELECT * FROM table WHERE ... GROUP BY artist

про mysql_num_rows - знаю, не выход

нужен оптимизированный запрос

Без Group By всегда пользовался count(*), но в данном случае возвращает не общее количество а сумму по группам

SELECT count(*) FROM table WHERE ... GROUP BY artist
Интернет магазин оптических приборов (http://oz.com.ua) в Украине - бинокли, микроскопы, телескопы.
Robin_Bad
На сайте с 24.12.2007
Offline
85
#1

KOSMOS aka Anarion, как-то не очень понятно. В данном случаем COUNT(*) вкупе с GROUP BY вернёт вам именно то число строк, которое вам выдаст MySQL при обычном запросе. Если же вам нужна общая сумма строк подпадающих под данное условие во WHERE, то просто не используйте GROUP BY. Но тогда я уже не очень понимаю, как вы вообще у себя потом всё это собираете и реализуете постраничную листалку, если в одном случае вам нужно одно, в другом - другое.

[umka]
На сайте с 25.05.2008
Offline
456
#2

SQL_CALC_FOUND_ROWS вам в помощь

[umka] добавил 28.09.2010 в 00:26

$q=mysql_query("SELECT SQL_CALC_FOUND_ROWS field1, field2, ... FROM table WHERE ... LIMIT ...");
$count=0+@mysql_result(mysql_query("SELECT FOUND_ROWS()"),0,0); // общее количество страниц, попавших под условие WHERE

Далее разбираем результат $q любым привычным способом.

Лог в помощь!
KA
На сайте с 03.01.2007
Offline
137
#3
'[umka:
;7688679']SQL_CALC_FOUND_ROWS вам в помощь

[umka] добавил 28.09.2010 в 00:26
$q=mysql_query("SELECT SQL_CALC_FOUND_ROWS field1, field2, ... FROM table WHERE ... LIMIT ...");
$count=0+@mysql_result(mysql_query("SELECT FOUND_ROWS()"),0,0); // общее количество страниц, попавших под условие WHERE

Далее разбираем результат $q любым привычным способом.

Спасибо, именно то, что было нужно.

[Удален]
#4

Не используйте никогда SQL_CALC_FOUND_ROWS (убивает использование индексов). Лучше 2 запроса - один на выборку, второй - на подсчет результатов

SELECT * FROM table WHERE ... GROUP BY artist

SELECT count(*) FROM table WHERE ... GROUP BY artist

и есть самый оптимальный вариант.

А вообще не совсем понятно. Если вы исполняете запрос

SELECT * FROM table WHERE ... GROUP BY artist
,

то посчитать кол-во строк не составит труда в PHP (насколько я понял), верно?

И собак (@) в PHP не используйте никогда - сжирает дополнительно время + ошибки отлавливать сложней становится.

Написал только что специально для вас :)


nemoden@Megacomp:/proc$ php -r '$s=microtime(true); for($i=0;$i<1000000;$i++) { $ar = array(1,2,3); $e = print_r($arr, 1); } echo PHP_EOL.(microtime(true) - $s).PHP_EOL;'

1.10546708107
nemoden@Megacomp:/proc$ php -r '$s=microtime(true); for($i=0;$i<1000000;$i++) { $ar = array(1,2,3); $e = print_r($arr, 1); } echo PHP_EOL.(microtime(true) - $s).PHP_EOL;'

1.021900177
nemoden@Megacomp:/proc$ php -r '$s=microtime(true); for($i=0;$i<1000000;$i++) { $ar = array(1,2,3); $e = print_r($arr, 1); } echo PHP_EOL.(microtime(true) - $s).PHP_EOL;'

1.05678701401
nemoden@Megacomp:/proc$ php -r '$s=microtime(true); for($i=0;$i<1000000;$i++) { $ar = array(1,2,3); $e = print_r($arr, 1); } echo PHP_EOL.(microtime(true) - $s).PHP_EOL;'

1.03543806076
nemoden@Megacomp:/proc$ php -r '$s=microtime(true); for($i=0;$i<1000000;$i++) { $ar = array(1,2,3); $e = @print_r($arr, 1); } echo PHP_EOL.(microtime(true) - $s).PHP_EOL;'

1.51796078682
nemoden@Megacomp:/proc$ php -r '$s=microtime(true); for($i=0;$i<1000000;$i++) { $ar = array(1,2,3); $e = @print_r($arr, 1); } echo PHP_EOL.(microtime(true) - $s).PHP_EOL;'

1.50326180458
nemoden@Megacomp:/proc$ php -r '$s=microtime(true); for($i=0;$i<1000000;$i++) { $ar = array(1,2,3); $e = @print_r($arr, 1); } echo PHP_EOL.(microtime(true) - $s).PHP_EOL;'

1.46839904785

красным зло выделено.

благодаря всего одной собаке этот супер сложный скрипт выполняется в полтора раза дольше, чем мог бы.

[umka], что же вы 3-мя строчками 2-м плохим вещам человека научили?

[umka]
На сайте с 25.05.2008
Offline
456
#5
So1:
Не используйте никогда SQL_CALC_FOUND_ROWS (убивает использование индексов).

С какого перепугу?

mysql> explain select sql_calc_found_rows * from user_data where user_id>1000 limit 10,10;
+----+-------------+-----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | user_data | range | user_id | user_id | 4 | NULL | 1457 | Using where |
+----+-------------+-----------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0,00 sec)

Всё используется.

Ссылка на документацию, в которой SQL_CALC_FOUND_ROWS рекомендуется именно для таких случаев:

http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_found-rows

So1:
А вообще не совсем понятно. Если вы исполняете запрос
SELECT * FROM table WHERE ... GROUP BY artist
,
то посчитать кол-во строк не составит труда в PHP (насколько я понял), верно?

Нет. Функция mysql_num_rows() вернёт лишь количество строк в результате, но не общее количество строк, участвовавших в группировании.

То же самое, если используется LIMIT.

So1:
И собак (@) в PHP не используйте никогда - сжирает дополнительно время + ошибки отлавливать сложней становится.

Сжирает времени не больше чем дискового пространства :)

Время тратится разве что на парсинг и переключение флагов. Разница становится существенна только при таких масштабах, как у вас (1000000 операций). Столько собак на всех сайтах на сервере не найдётся.

А способ обработки ошибок — дело каждого. Конкретно в данном случае (в моём примере) считаю собаку уместной :)

[Удален]
#6

mysql> create table `table` (`id` int(11) unsigned not null auto_increment, `data` int(11) unsigned not null default '0', PRIMARY KEY (`id`), KEY `data` (`DATA`)) ENGINE=InnoDB COMMENT='Rock-n-roll :)';                                                                                                                                                  
Query OK, 0 rows affected (0.11 sec)

mysql> insert into `table` (`data`) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
Query OK, 10 rows affected (0.03 sec)
Records: 10 Duplicates: 0 Warnings: 0

mysql> insert into `table` (`data`) values (1), (2), (3), (4), (5);
Query OK, 5 rows affected (0.03 sec)
Records: 5 Duplicates: 0 Warnings: 0

mysql> insert into `table` (`data`) values (6), (7), (8), (9), (10);
Query OK, 5 rows affected (0.04 sec)
Records: 5 Duplicates: 0 Warnings: 0

mysql> explain select SQL_NO_CACHE * from `table` WHERE `data` > 3 ORDER BY `data` LIMIT 2;
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| 1 | SIMPLE | table | range | data | data | 4 | NULL | 10 | Using where; Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

mysql> explain select SQL_NO_CACHE sql_calc_found_rows * from `table` WHERE `data` > 3 ORDER BY `data` LIMIT 2;
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| 1 | SIMPLE | table | range | data | data | 4 | NULL | 10 | Using where; Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+

mysql> alter table `table` ENGINE=MyISAM;
Query OK, 20 rows affected (0.16 sec)
Records: 20 Duplicates: 0 Warnings: 0

mysql> explain select SQL_NO_CACHE sql_calc_found_rows * from `table` WHERE `data` > 3 ORDER BY `data` LIMIT 2;
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
| 1 | SIMPLE | table | ALL | data | NULL | NULL | NULL | 20 | Using where; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)

mysql> explain select SQL_NO_CACHE * from `table` WHERE `data` > 3 ORDER BY `data` LIMIT 2;
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | table | range | data | data | 4 | NULL | 14 | Using where |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+

Если Engine=InnoDB и указан лимит, всё хорошо, но если у вас MyISAM (а его чаще всего нужно использовать для больших таблиц логов), то индекс не используется. Часто это может привести к тому, что у вас запросы будут выполняться в десятки раз дольше.

Собаки, я считаю, плохой стиль программирования, ну да бог с ним, но вот sql_calc_found_rows - pure evil! У нас один программист очень любил sql_calc_found_rows... Пришлось во многих случаях избавляться от нее, потому что она не добавляет никакого перворманса, а во многих случаях является, как говорится "performance pig"-ом

Сами потестируйте на таблицах с более миллиона записей и еще вот это можете почитать http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

PS я наврал только в одном - не всегда конечно sql_calc_found_rows убивает использование индексов.

Функция mysql_num_rows() вернёт лишь количество строк в результате

Да. Тут я невнимательно прочитал то, что ТС-у нужно. В любом случае, я не советую использовать sql_calc_found_rows - от него неприятно избавляться, а ссылка ваша не рекомендация, а просто описание, что так можно делать. Ничего об улучшении performance там не сказано.

dvaes
На сайте с 03.09.2007
Offline
65
#7

sql_calc_found_rows так скажем нужно использовать в отдельных случаях

если count(*) используется по индексу, а потом еще и запрос по индексу с LIMIT, то оно быстрее будет, чем sql_calc_found_rows, который реально будет по всем записям проходиться.

а вот, если без индекса, типо какой-нибудь поиск по некоторым полям, когда в любом случае будет проходить по всем записям, то тут уж лучше с sql_calc_found_rows, чтобы два раза по таблице не проходить

тс, по-моему аналогично будет запрос SELECT count(*) FROM table WHERE ..., т.е. без group

а тут уже смотреть что быстрее будет, с SQL_CALC_FOUND_ROWS или с таким count(*)

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий