COUNT (*) против COUNT (1) и COUNT (pk): что лучше?

Я часто нахожу эти три варианта:

SELECT COUNT(*) FROM Foo;
SELECT COUNT(1) FROM Foo;
SELECT COUNT(PrimaryKey) FROM Foo;

Насколько я вижу, все они делают то же самое, и я нахожу себя использующим три в своей кодовой базе. Однако я не люблю делать то же самое по-разному. К чему я должен придерживаться? Кто-нибудь из них лучше, чем два других?

Ответ 1

Нижняя линия

Используйте либо COUNT(field), либо COUNT(*), и придерживайтесь его последовательно, и если ваша база данных позволяет COUNT(tableHere) или COUNT(tableHere.*), используйте это.

Короче говоря, не используйте COUNT(1) для чего-либо. Это одноходовой пони, который редко делает то, что вы хотите, и в этих редких случаях эквивалентно COUNT(*)

Используйте COUNT(*) для подсчета

Используйте * для всех ваших запросов, которые должны считать все, даже для соединений, используйте *

SELECT boss.boss_id, COUNT(subordinate.*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Но не используйте COUNT(*) для LEFT-соединений, так как это вернет 1, даже если подчиненная таблица ничего не сопоставляет с родительской таблицей

SELECT boss.boss_id, COUNT(*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Не обманывайтесь теми, кто советует, что при использовании * в COUNT он извлекает целую строку из вашей таблицы, говоря, что * работает медленно. * на SELECT COUNT(*) и SELECT * не имеют отношения друг к другу, они совершенно разные, они просто разделяют общий токен, т.е. *.

Альтернативный синтаксис

На самом деле, если не разрешено указывать поле так же, как имя его таблицы, разработчик языка RDBMS может предоставить COUNT(tableNameHere) ту же семантику, что и COUNT(*). Пример:

Для подсчета строк мы могли бы иметь это:

SELECT COUNT(emp) FROM emp

И они могли бы сделать это проще:

SELECT COUNT() FROM emp

И для LEFT JOINs мы могли бы иметь это:

SELECT boss.boss_id, COUNT(subordinate)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Но они не могут этого сделать (COUNT(tableNameHere)), так как стандарт SQL позволяет называть поле с тем же именем, что и его имя таблицы:

CREATE TABLE fruit -- ORM-friendly name
(
fruit_id int NOT NULL,
fruit varchar(50), /* same name as table name, 
                and let say, someone forgot to put NOT NULL */
shape varchar(50) NOT NULL,
color varchar(50) NOT NULL
)

Подсчет с нулем

Кроме того, не рекомендуется делать поле нулевым, если его имя соответствует имени таблицы. Скажем, у вас есть значения "Банана", "Яблоко", "NULL", "Груши" в поле fruit. Это не будет считать все строки, это даст только 3, а не 4

SELECT count(fruit) FROM fruit

Хотя некоторые СУБД выполняют такой принцип (для подсчета строк таблицы он принимает имя таблицы как параметр COUNT), это будет работать в Postgresql (если в любой из двух таблиц ниже нет поля subordinate, т.е. если между именем поля и именем таблицы нет конфликта имен):

SELECT boss.boss_id, COUNT(subordinate)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Но это может привести к путанице позже, если мы добавим в таблицу поле subordinate, поскольку оно будет считать поле (которое может быть нулевым), а не строки таблицы.

Чтобы быть в безопасности, используйте:

SELECT boss.boss_id, COUNT(subordinate.*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

COUNT(1): однопоточный пони

В частности, для COUNT(1), это одноконтактный пони, он работает только на одном запросе таблицы:

SELECT COUNT(1) FROM tbl

Но когда вы используете объединения, этот трюк не будет работать в многозадачных запросах, если его семантика не запутана и, в частности, вы не можете писать:

-- count the subordinates that belongs to boss
SELECT boss.boss_id, COUNT(subordinate.1)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Итак, что здесь означает значение COUNT (1)?

SELECT boss.boss_id, COUNT(1)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Разве это...?

-- counting all the subordinates only
SELECT boss.boss_id, COUNT(subordinate.boss_id)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Или это...?

-- or is that COUNT(1) will also count 1 for boss regardless if boss has a subordinate
SELECT boss.boss_id, COUNT(*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Соблюдая осторожность, вы можете сделать вывод, что COUNT(1) совпадает с COUNT(*), независимо от типа соединения. Но для результата LEFT JOINs мы не можем формовать COUNT(1) для работы как: COUNT(subordinate.boss_id), COUNT(subordinate.*)

Поэтому просто используйте одно из следующих действий:

-- count the subordinates that belongs to boss
SELECT boss.boss_id, COUNT(subordinate.boss_id)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Работает над Postgresql, ясно, что вы хотите подсчитать мощность множества

-- count the subordinates that belongs to boss
SELECT boss.boss_id, COUNT(subordinate.*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Еще один способ подсчета мощности набора, очень английский (просто не создавайте столбец с именем, аналогичным имени его таблицы): http://www.sqlfiddle.com/#!1/98515/7

select boss.boss_name, count(subordinate)
from boss
left join subordinate on subordinate.boss_code = boss.boss_code
group by boss.boss_name

Вы не можете этого сделать: http://www.sqlfiddle.com/#!1/98515/8

select boss.boss_name, count(subordinate.1)
from boss
left join subordinate on subordinate.boss_code = boss.boss_code
group by boss.boss_name

Вы можете сделать это, но это приводит к неправильному результату: http://www.sqlfiddle.com/#!1/98515/9

select boss.boss_name, count(1)
from boss
left join subordinate on subordinate.boss_code = boss.boss_code
group by boss.boss_name

Ответ 2

Два из них всегда дают один и тот же ответ:

  • COUNT (*) подсчитывает количество строк
  • COUNT (1) также подсчитывает количество строк

Предполагая, что 'pk' является первичным ключом и что в значениях не допускаются никакие значения, тогда

  • COUNT (pk) также подсчитывает количество строк

Однако, если 'pk' не ограничено, чтобы оно не было нулевым, то оно выдавало другой ответ:

  • COUNT (возможно_null) подсчитывает количество строк с ненулевыми значениями в столбце possibly_null.

  • COUNT (DISTINCT pk) также подсчитывает количество строк (поскольку первичный ключ не позволяет дублировать).

  • COUNT (DISTINCT возможно_null_or_dup) подсчитывает количество различных ненулевых значений в столбце possibly_null_or_dup.

  • COUNT (DISTINCT возможно_duplicated) подсчитывает количество различных (обязательно ненулевых) значений в столбце possibly_duplicated, если на нем есть предложение NOT NULL.

Обычно пишу COUNT(*); это оригинальная рекомендуемая нотация для SQL. Аналогично, с условием EXISTS я обычно пишу WHERE EXISTS(SELECT * FROM ...), потому что это была оригинальная рекомендация. Не должно быть преимуществ для альтернатив; оптимизатор должен видеть через более неясные обозначения.

Ответ 3

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

Например, используя MySQL, count(*) будет быстро работать под таблицей MyISAM, но медленнее под InnoDB. В InnoDB вы должны использовать count(1) или count(pk).

Ответ 4

Отвечали на вопрос и ответили ранее...

Книги в Интернете говорит "COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1" - это ненулевое выражение, так что оно равно как COUNT(*). Оптимизатор распознает его как тривиальный, поэтому дает тот же план. PK уникален и не равен null (в SQL Server по крайней мере), поэтому COUNT(PK)= COUNT (*)

Это аналогичный миф EXISTS (SELECT * ... или EXISTS (SELECT 1 ...

И посмотрите спецификацию ANSI 92, раздел 6.5, Общие правила, случай 1

        a) If COUNT(*) is specified, then the result is the cardinality
          of T.

        b) Otherwise, let TX be the single-column table that is the
          result of applying the <value expression> to each row of T
          and eliminating null values. If one or more null values are
          eliminated, then a completion condition is raised: warning-
          null value eliminated in set function.

Ответ 6

Я чувствую, что производительность charecteristics изменяется с СУБД на СУБД. Все о том, как они решили внедрить его. Поскольку я много работал над оракулом, я говорю с этой точки зрения.

COUNT (*) - выводит целую строку в результирующий набор для перехода к функции count, функция count будет агрегировать 1, если строка не равна null

COUNT (1) - Не будет извлекать ни одну строку, вместо этого счет вызывается с постоянным значением 1 для каждой строки таблицы, когда совпадение совпадает.

Count (PK) - PK в oracle индексируется. Это означает, что Oracle должен читать только индекс. Обычно одна строка в индексе B + Tree во много раз меньше фактической строки. Поэтому, учитывая скорость IOPS на диске, Oracle может извлекать во много раз больше строк из индекса с одним блочным переносом по сравнению со всей строкой. Это приводит к увеличению объема запроса.

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