КОЛИЧЕСТВО (*) по сравнению с Количеством (1) по сравнению с Количеством (pk): который лучше? [дубликат]

Этот вопрос уже имеет ответ здесь:

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

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

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

214
задан zneak 25 April 2010 в 14:10
поделиться

6 ответов

Bottom Line

Использование либо COUNT (поле) , либо 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 * не имеют отношения друг к другу, это совершенно разные вещи, они просто имеют общий токен, т.е. * .

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

Фактически, если не разрешено называть поле таким же, как имя его таблицы, разработчик языка СУБД может дать COUNT (tableNameHere) ту же семантику, что и СЧЕТЧИК (*) . Пример:

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

SELECT COUNT(emp) FROM emp

И они могли бы упростить:

SELECT COUNT() FROM emp

А для ЛЕВЫХ СОЕДИНЕНИЙ мы могли бы иметь это:

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's say, someone forgot to put NOT NULL */
shape varchar(50) NOT NULL,
color varchar(50) NOT NULL
)

Подсчет с нулевым значением

Кроме того, не рекомендуется делать поле допускающим значение NULL, если его имя совпадает с имя таблицы. Допустим, у вас есть значения Banana, Apple, NULL, Pears в поле fruit .Это не будет считать все строки, это даст только 3, а не 4

SELECT count(fruit) FROM fruit

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

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

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

На всякий случай используйте:

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 (подчиненный. *)

Так что просто используйте одно из следующих:

-- 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
213
ответ дан 23 November 2019 в 04:26
поделиться

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

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

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

COUNT (PK) - PK в Oracle индексируется. Это означает, что Oracle должен читать только индекс. Обычно одна строка в дереве индекса B + во много раз меньше, чем фактическая строка.Таким образом, учитывая скорость дискового ввода-вывода в секунду, Oracle может получить во много раз больше строк из индекса с помощью передачи одного блока по сравнению со строкой целиком. Это приводит к более высокой пропускной способности запроса.

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

-1
ответ дан 23 November 2019 в 04:26
поделиться

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

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

Если предположить, что pk является первичным ключом и в значениях не допускаются нули, то

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

Однако, если pk не ограничен значением not null, то получается другой ответ:

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

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

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

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

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

49
ответ дан 23 November 2019 в 04:26
поделиться

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

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

9
ответ дан 23 November 2019 в 04:26
поделиться

По крайней мере, на Oracle они все одинаковые: http://www.oracledba.co.uk/tips/count_speed. htm

5
ответ дан 23 November 2019 в 04:26
поделиться

Спрашивали и отвечали раньше...

Книга в интернете говорит:«COUNT ( { [ [ ВСЕ | ОТДЕЛЬНОЕ выражение ] | * } )"

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

Это миф, похожий на 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
ответ дан 23 November 2019 в 04:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: