Обычный выбор быстрее, чем count(*)

Я хочу сделать такой подсчет (например, не считая собак):

SELECT COUNT(*)
FROM dogs AS d INNER JOIN races AS r ON d.race_id = r.race_id
    LEFT INNER colors AS c ON c.color_id = r.color_id
WHERE d.deceased = 'N'

У меня 130 000 собак в таблице MyISAM. Races имеет 1500 записей и представляет собой таблицу InnoDB с 9 столбцами, colors имеет 83 записи и также является InnoDB и имеет два столбца (id, name). Все столбцы *_id являются первичными ключами, у меня есть индексы для «внешних» ключей dogs.race_id и races.color_id, и у меня есть индекс для dogs.deceased. Ни один из упомянутых столбцов не может быть NULL.

# mysql --version
mysql  Ver 14.12 Distrib 5.0.51a, for debian-linux-gnu (i486) using readline 5.2

Теперь дело в том, что в моем PhpMyAdmin этот запрос занимает 1,8 секунды (с SQL_NO_CACHE) с результатом подсчета 64 315. Изменение COUNT(*) на COUNT(d.dog_id) или COUNT(d.deceased) также приводит к тому, что запрос выполняется в течение 1,8 секунды с тем же результатом.

Но когда я удаляю COUNT() и просто делаю SELECT * или SELECT dog_id, для запуска требуется около 0,004 секунды (и затем подсчет результата с чем-то вроде mysql_num_rows()).

Как это может быть? И как мне заставить COUNT() работать быстрее?

Изменить: добавлено EXPLAIN ниже

EXPLAIN SELECT COUNT(*)
FROM dogs AS d INNER JOIN races AS r ON d.race_id = r.race_id
    INNER JOIN colors AS c ON c.color_id = r.color_id
WHERE d.deceased = 'N'

Дает мне:

+----+-------------+-------+-------+------------------+----------+---------+----------------------+------+-------------+
| id | select_type | table | type  | possible_keys    | key      | key_len | ref                  | rows | Extra       |
+----+-------------+-------+-------+------------------+----------+---------+----------------------+------+-------------+
|  1 | SIMPLE      | c     | index | color_id         | color_id | 4       | NULL                 |   83 | Using index | 
|  1 | SIMPLE      | r     | ref   | PRIMARY,color_id | color_id | 4       | database.c.color_id  |   14 | Using index | 
|  1 | SIMPLE      | d     | ref   | race_id,deceased | race_id  | 4       | database.r.race_id   |  123 | Using where | 
+----+-------------+-------+-------+------------------+----------+---------+----------------------+------+-------------+

person 7ochem    schedule 09.04.2013    source источник
comment
возможный дубликат COUNT(*) против COUNT(1) против COUNT (пк): что лучше?   -  person John Woo    schedule 09.04.2013
comment
Да, я читал тот пост. Этот вопрос больше похож на «SELECT COUNT (*) vs. SELECT *»   -  person 7ochem    schedule 09.04.2013


Ответы (3)


MySQL Optimizer выполняет полное сканирование таблицы, только если это необходимо, потому что столбец может быть NULL, что означает, что если столбец не определен как NOT NULL, в нем могут быть некоторые значения NULL, и поэтому MySQL должен выполнить сканирование таблицы, чтобы выяснить это. Если ваш столбец d.dog_id обнуляемый? попробуйте запустить подсчет в другом столбце, который не может принимать значения NULL, это должно обеспечить лучшую производительность, чем count (*).

person CloudyMarble    schedule 09.04.2013

Попробуйте установить индекс на dogs.deceased и использовать SELECT COUNT(*) ... USE INDEX my_index_name.

person mrks    schedule 09.04.2013
comment
У меня уже был список умерших собак. Я изменил запрос на SELECT COUNT(*) FROM dogs AS d USE INDEX (idx_deceased) LEFT JOIN ..., но опять же: запрос занял 2,9 секунды. В любом случае спасибо за предложение! - person 7ochem; 09.04.2013

Создайте индексы, чтобы ускорить подсчет:

CREATE INDEX ix_temp ON dogs (d.race_id)
INCLUDE (columns needed for the query)
person Nik Drosakis    schedule 09.04.2013
comment
Вы уверены, что можете создать индекс для нескольких таблиц? ... ON dogs (d.race_id,r.race_id) ... d.race_id находится в таблице собак, но r.race_id находится в таблице рас. - person 7ochem; 09.04.2013