COUNT ( * ) vs. COUNT(1) vs. COUNT (pk): która jest lepsza? [duplikat]

To pytanie ma już odpowiedź tutaj:

Często znajduję te trzy warianty:

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

Z tego co widzę, wszystkie robią to samo, a ja używam tych trzech w mojej bazie kodowej. Jednak nie lubię robić tego samego na różne sposoby. Do którego mam się przyczepić? Czy każdy z nich lepszych od dwóch pozostałych?

Author: zneak, 2010-04-26

6 answers

Bottom Line

Użyj COUNT(field) lub COUNT(*) i trzymaj się tego konsekwentnie, a jeśli twoja baza danych pozwala COUNT(tableHere) lub COUNT(tableHere.*), użyj tego.

Krótko mówiąc, nie używaj COUNT(1) do niczego. Jest to kucyk jednokierunkowy, który rzadko robi to, co chcesz, a w rzadkich przypadkach jest odpowiednikiem count(*) {51]}

Użyj count(*) do liczenia

Użyj * dla wszystkich zapytań, które muszą liczyć wszystko, nawet dla łączy, użyj *

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

Ale nie używaj COUNT(*) dla LEFT joins, ponieważ zwróci 1, nawet jeśli tabela podrzędna nie pasuje do niczego z tabeli nadrzędnej

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

Nie daj się zwieść tym, którzy doradzają, że gdy używasz * w liczeniu, pobiera cały wiersz z Twojej tabeli, mówiąc, że * jest powolny. * na SELECT COUNT(*) i SELECT * nie mają ze sobą żadnego związku, są zupełnie inną rzeczą, po prostu dzielą wspólny znak, tj. *.

Składnia alternatywna

W rzeczywistości, jeśli nie jest dozwolone nazwanie pola jako podobnie jak nazwa tabeli, projektant języka RDBMS może nadać COUNT(tableNameHere) taką samą semantykę jak COUNT(*). Przykład:

Do liczenia wierszy możemy mieć to:

SELECT COUNT(emp) FROM emp

I mogliby to uprościć:

SELECT COUNT() FROM emp
/ Align = "LEFT" / ]}
SELECT boss.boss_id, COUNT(subordinate)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Ale nie mogą tego zrobić (COUNT(tableNameHere)), ponieważ standard SQL pozwala na nazwanie pola o tej samej nazwie, co nazwa tabeli:

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
)

Liczenie przez null

[50]} a także, nie jest dobrą praktyką, aby pole nullable, jeśli jego nazwa odpowiada nazwie tabeli. Powiedzmy, że masz wartości 'Banana', 'Apple', NULL, 'Pears' w polu fruit. To nie zliczy wszystkich wierszy, da tylko 3, a nie 4
SELECT count(fruit) FROM fruit

Chociaż niektóre RDBM stosują taką zasadę (do zliczania wierszy tabeli przyjmuje nazwę tabeli jako parametr COUNT), będzie to działać w Postgresql (jeśli nie ma pola subordinate w żadnej z dwóch poniższych tabel, tzn. tak długo, jak nie ma konfliktu nazw między nazwą pola a tabelą "nazwa": {]}

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

Ale może to spowodować zamieszanie później, jeśli dodamy pole subordinate w tabeli, ponieważ będzie liczyć pole (które może być nullable), a nie wiersze tabeli.

Aby być po bezpiecznej stronie, użyj:

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

count(1): the one-trick pony

W szczególności do COUNT(1), jest to kucyk jednokierunkowy , działa dobrze tylko na jednym zapytaniu tabeli:

SELECT COUNT(1) FROM tbl

Ale kiedy używasz joins, ta sztuczka nie będzie działać na zapytaniach wielostolikowych bez jej semantyki jest zdezorientowany, a w szczególności nie można napisać:

-- 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

Więc jakie jest znaczenie COUNT (1) tutaj?

SELECT boss.boss_id, COUNT(1)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id
To jest to...?
-- 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
Albo to...?
-- 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

Poprzez uważne przemyślenie można wywnioskować, że COUNT(1) jest tym samym co COUNT(*), niezależnie od rodzaju połączenia. / Align = "LEFT" / : COUNT(subordinate.boss_id), COUNT(subordinate.*)

Więc po prostu użyj jednej z następujących opcji:

-- 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

Działa na Postgresql, to jasne, że chcesz policz Kardynalność zestawu

-- 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

Inny sposób liczenia kardynalności zbioru, bardzo angielski-podobny (tylko nie rób kolumny o nazwie takiej samej jak nazwa tabeli) : 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

Nie możesz tego zrobić: 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

Możesz to zrobić, ale to daje zły wynik: 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
 188
Author: Michael Buen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-06 14:34:01

Dwa z nich zawsze dają tę samą odpowiedź:

  • COUNT (*) zlicza liczbę wierszy
  • COUNT (1) zlicza również liczbę wierszy

Zakładając, że' pk ' jest kluczem podstawowym i że żadne NULL nie są dozwolone w wartościach, to

  • COUNT (pk) zlicza również liczbę wierszy

Jeśli jednak 'pk ' nie jest ograniczone do nie null, to daje inną odpowiedź:

  • COUNT (possibly_null) liczy liczbę wiersze z wartościami innymi niż null w kolumnie possibly_null.

  • COUNT (DISTINCT pk) zlicza również liczbę wierszy(ponieważ klucz podstawowy nie pozwala na duplikaty).

  • COUNT (DISTINCT possibly_null_or_dup) zlicza liczbę różnych wartości innych niż null w kolumnie possibly_null_or_dup.

  • COUNT (DISTINCT possibly_duplicated) zlicza liczbę odrębnych (koniecznie nie-null) wartości w kolumnie possibly_duplicated, Gdy ta ma klauzulę NOT NULL na to.

Zwykle piszę COUNT(*); jest to oryginalna zalecana notacja dla SQL. Podobnie, z klauzulą EXISTS, Zwykle piszę WHERE EXISTS(SELECT * FROM ...), ponieważ była to oryginalna notacja polecająca. Nie powinno być żadnych korzyści dla alternatyw; optymalizator powinien przejrzeć bardziej niejasne notacje.

 47
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-26 01:30:15

Zależy to od typu bazy danych, z której korzystasz, a także od typu tabeli w niektórych przypadkach.

Na przykład, używając MySQL, count(*) będzie szybko pod tabelą MyISAM, ale wolno pod InnoDB. Pod InnoDB należy stosować count(1) lub count(pk).

 9
Author: Jarod Elliott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-26 01:14:44

Zapytany I odpowiedział wcześniej...

Książki on line mówi "COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

" 1 " jest wyrażeniem nie-null, więc jest takie samo jak COUNT(*). Optymalizator rozpoznaje go jako trywialne więc daje ten sam plan. PK jest unikalne i nie-null (przynajmniej w SQL serverze) więc COUNT(PK) = COUNT (*)

Jest to podobny Mit do EXISTS (SELECT * ... lub EXISTS (SELECT 1 ...

I patrz ANSI 92 spec , sekcja 6.5, Zasady ogólne, przypadek 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
Author: gbn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:26

Przynajmniej na Oracle wszystkie są takie same: http://www.oracledba.co.uk/tips/count_speed.htm

 5
Author: ZeissS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-26 01:15:08

Czuję, że wydajność charecteristics zmienia się z DBMS na DBMS. Wszystko zależy od sposobu, w jaki zdecydują się go wszczepić. Ponieważ pracowałem intensywnie na oracle, powiem z tej perspektywy.

COUNT (*) - pobiera cały wiersz do zestawu wyników przed przekazaniem do funkcji count, funkcja count zagreguje 1, jeśli wiersz nie jest null

COUNT (1) - nie pobierze żadnego wiersza, zamiast tego count jest wywoływany ze stałą wartością 1 dla każdego wiersza w tabeli, gdy where zapałki.

Count (PK) - PK w oracle jest indeksowane. Oznacza to, że Oracle musi odczytywać tylko indeks. Zwykle jeden wiersz w drzewie indeksu B+ jest wiele razy mniejszy niż rzeczywisty wiersz. Biorąc więc pod uwagę szybkość operacji IOPS na dysku, Oracle może pobrać wiele razy więcej wierszy z indeksu za pomocą pojedynczego transferu bloków w porównaniu do całego wiersza. Prowadzi to do większej przepustowości zapytania.

Z tego widać, że pierwsza liczba jest najwolniejsza, a ostatnia jest najszybsza w Oracle.

 -2
Author: arunmur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-26 03:45:36