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?
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(*)
count(*)
pour compterUtilisez *
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 *
.
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
)
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 tourEn 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
Deux d’entre eux produisent toujours la même réponse:
COUNT(*)
compte le nombre de lignesCOUNT(1)
compte également le nombre de lignesEn 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 lignesToutefois, 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.
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)
.
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.
Au moins sur Oracle, ils sont tous identiques: http://www.oracledba.co.uk/tips/count_speed.htm