web-dev-qa-db-fra.com

COUNT (*) vs. COUNT (1) vs. COUNT (pk): quel est le meilleur?

Je trouve souvent ces trois variantes:

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

Autant que je sache, ils font tous la même chose, et je me retrouve à utiliser les trois dans ma base de code. Cependant, je n'aime pas faire la même chose de différentes manières. A qui devrais-je me tenir? Est-ce que l'un d'entre eux est meilleur que les deux autres?

210
zneak

Ligne de fond

Utilisez COUNT(field) ou COUNT(*), et respectez-le de manière cohérente. Si votre base de données vous permet COUNT(tableHere) ou COUNT(tableHere.*), utilisez-le.

En bref, n'utilisez pas COUNT(1) pour rien. C'est un poney à un tour, qui fait rarement ce que vous voulez, et dans ces rares cas équivaut à count(*)

Utilisez count(*) pour compter

Utilisez * pour toutes vos requêtes qui doivent tout compter, même pour les jointures, utilisez *.

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

Mais n'utilisez pas COUNT(*) pour les jointures LEFT, car cela renverra 1 même si la table subordonnée ne correspond à rien de la table parent

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

Ne vous laissez pas berner par ceux qui vous diront que, lorsque vous utilisez * dans COUNT, celui-ci extrait une ligne entière de votre table en disant que * est lent. Le * sur SELECT COUNT(*) et SELECT * n'a aucun rapport l'un avec l'autre, ils sont totalement différents, ils partagent juste un jeton commun, c'est-à-dire *.

Une syntaxe alternative

En fait, s'il n'est pas permis de nommer un champ avec le même nom que son nom de table, le concepteur de langage RDBMS pourrait donner à COUNT(tableNameHere) la même sémantique que COUNT(*). Exemple:

Pour compter les lignes, nous pourrions avoir ceci:

SELECT COUNT(emp) FROM emp

Et ils pourraient le rendre plus simple:

SELECT COUNT() FROM emp

Et pour les JOINTS DE GAUCHE, nous pourrions avoir ceci:

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

Mais ils ne peuvent pas faire cela (COUNT(tableNameHere)) car le standard SQL permet de nommer un champ avec le même nom que son nom de table:

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
)

Compter avec null

De plus, ce n'est pas une bonne pratique de rendre un champ nullable si son nom correspond au nom de la table. Supposons que vous ayez les valeurs 'Banana', 'Apple', NULL, 'Pears' sur le champ fruit. Cela ne comptera pas toutes les lignes, il ne donnera que 3, pas 4

SELECT count(fruit) FROM fruit

Bien que certains SGBDR fassent ce genre de principe (pour compter les lignes de la table, il accepte le nom de la table comme paramètre COUNT), cela fonctionnera sous Postgresql (s’il n’existe pas de champ subordinate dans l’une des deux tables ci-dessous, tant qu'il n'y a pas de conflit de nom entre nom de champ et nom de table):

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

Mais cela pourrait semer la confusion ultérieurement si nous ajoutions un champ subordinate dans la table, car il compterait le champ (qui pourrait être nullable), pas les lignes de la table.

Donc, pour être sûr, utilisez:

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

count(1): Le poney à un tour

En particulier pour COUNT(1), il s’agit d’un poney one-trick , il ne fonctionne bien que sur une requête de table:

SELECT COUNT(1) FROM tbl

Mais lorsque vous utilisez des jointures, cette astuce ne fonctionnera pas sur des requêtes multi-tables sans confusion de sémantique, et vous ne pourrez notamment pas écrire:

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

Alors, quel est le sens de COUNT (1) ici?

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

Est-ce cela...?

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

Ou ca...?

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

En réfléchissant bien, vous pouvez en déduire que COUNT(1) est identique à COUNT(*), quel que soit le type de jointure. Mais pour le résultat LEFT JOINs, nous ne pouvons pas mouler COUNT(1) pour fonctionner comme: COUNT(subordinate.boss_id), COUNT(subordinate.*)

Il suffit donc d'utiliser l'un des éléments suivants:

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

Fonctionne sur Postgresql, il est clair que vous souhaitez compter la cardinalité de l'ensemble

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

Une autre façon de compter la cardinalité de l'ensemble, très anglaise (ne créez pas une colonne portant le même nom que le nom de la table): 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

Vous ne pouvez pas faire ceci: 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

Vous pouvez le faire, mais cela produit un résultat erroné: 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
210
Michael Buen

Deux d’entre eux produisent toujours la même réponse:

  • COUNT(*) compte le nombre de lignes
  • COUNT(1) compte également le nombre de lignes

En supposant que pk est une clé primaire et qu'aucune valeur NULL n'est autorisée dans les valeurs, alors

  • COUNT(pk) compte également le nombre de lignes

Toutefois, si pk n'est pas contraint de ne pas être null, il donne une réponse différente:

  • COUNT(possibly_null) compte le nombre de lignes avec des valeurs non NULL dans la colonne possibly_null.

  • COUNT(DISTINCT pk) compte également le nombre de lignes (car une clé primaire n'autorise pas les doublons).

  • COUNT(DISTINCT possibly_null_or_dup) compte le nombre de valeurs non nulles distinctes dans la colonne possibly_null_or_dup.

  • COUNT(DISTINCT possibly_duplicated) compte le nombre de valeurs distinctes (nécessairement non nulles) dans la colonne possibly_duplicated lorsque la clause NOT NULL s'y trouve.

Normalement, j'écris COUNT(*); c'est la notation d'origine recommandée pour SQL. De même, avec la clause EXISTS, j’écris normalement WHERE EXISTS(SELECT * FROM ...) car c’était la notation recommandée à l’origine. Il ne devrait y avoir aucun avantage aux alternatives; l'optimiseur devrait voir à travers les notations les plus obscures.

49
Jonathan Leffler

Cela dépendra du type de base de données que vous utilisez ainsi que du type de table dans certains cas.

Par exemple, en utilisant MySQL, count(*) sera rapide sous une table MyISAM mais lent sous un InnoDB. Sous InnoDB, vous devez utiliser count(1) ou count(pk).

9
Jarod Elliott

demandé et répondu avant ...

Livres en ligne dit "COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1" est une expression non nulle, donc identique à COUNT(*). L'optimiseur le reconnaît en tant que trivial donne donc le même plan. Une PK est unique et non nulle (au moins dans SQL Server) donc COUNT(PK) = COUNT (*)

Ceci est un mythe similaire à EXISTS (SELECT * ... ou EXISTS (SELECT 1 ...

Et voir le ANSI 92 spec , paragraphe 6.5, Règles générales, cas 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
gbn

Au moins sur Oracle, ils sont tous identiques: http://www.oracledba.co.uk/tips/count_speed.htm

5
ZeissS