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 mon code. Cependant, je n’aime pas faire la même chose de différentes manières. À qui dois-je coller? L’un d’entre eux est-il meilleur que les deux autres?

Bottom Line

Utilisez soit COUNT(field) ou COUNT(*) , et respectez-le régulièrement, et si votre firebase database autorise COUNT(tableHere) ou COUNT(tableHere.*) , Utilisez-la.

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(*)

Utiliser le 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 soyez pas dupé par ceux qui conseillent que lorsque vous utilisez * dans COUNT, il récupère une ligne entière de votre table, indiquant que * est lent. Le * sur SELECT COUNT(*) et SELECT * n’ont aucun rapport l’un avec l’autre, ils sont totalement différents, ils partagent simplement un jeton commun, à savoir * .

Une syntaxe alternative

En fait, s’il n’est pas autorisé à nommer un champ de la même manière que son nom de table, le concepteur de langage SGBDR 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 LEFT JOIN, 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) ) puisque 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, il n’est pas recommandé de rendre un champ nullable si son nom correspond au nom de la table. Supposons que vous avez les valeurs ‘Banana’, ‘Apple’, NULL, ‘Pears’ sur le champ de fruit . Cela ne comptera pas toutes les lignes, il ne produira 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 en tant que paramètre de COUNT), cela fonctionnera dans Postgresql (s’il n’y a pas de champ subordinate dans l’une des deux tables ci-dessous, aucun conflit de nom entre le nom du champ et le nom de la 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 causer de la confusion plus tard si nous ajoutons un champ subordinate dans la table, car cela comptera le champ (qui pourrait être nullable), pas les lignes du tableau.

Donc, pour être du bon côté, 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 à COUNT(1) , il s’agit d’un poney à un tour , qui 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 les requêtes multi-tables sans que sa sémantique ne soit confuse, et en particulier vous ne pouvez 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, quelle est la signification 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 y 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 de JOINTS GAUCHE, nous ne pouvons pas mouler COUNT(1) pour fonctionner comme COUNT(subordinate.boss_id) : COUNT(subordinate.boss_id) , COUNT(subordinate.*)

Donc, utilisez l’une des méthodes suivantes:

 -- 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 anglais (ne faites pas une colonne avec un nom identique à son nom de 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 incorrect: 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 lignes
  • COUNT (1) compte également le nombre de lignes

En supposant que le ” pk ” est une clé primaire et qu’aucun null n’est autorisé dans les valeurs, alors

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

Cependant, si ‘ pk ‘ n’est pas contraint à ne pas être nul, il produit une réponse différente:

  • COUNT (éventuellement_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 éventuellement_null_or_dup) compte le nombre de valeurs non nulles distinctes dans la colonne possibly_null_or_dup .

  • COUNT (DISTINCT éventuellement_dupliqué) compte le nombre de valeurs distinctes (nécessairement non nulles) dans la colonne possibly_duplicated lorsque la clause NOT NULL est présente.

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

Cela dépendra du type de firebase database 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 InnoDB. Sous InnoDB, vous devez utiliser count(1) ou count(pk) .

Demandé et répondu avant …

Books on line indique ” COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )

“1” est une expression non nulle, donc identique à COUNT(*) . L’optimiseur le reconnaît comme sortingvial et donne le même plan. Un PK est unique et non nul (au moins dans SQL Server), donc COUNT(PK) = COUNT (*)

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

Et voir la spécification ANSI 92 , section 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  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

Je pense que les caractéristiques de performance changent du SGBD au SGBD. Son tout sur la façon dont ils choisissent de l’implémenter. Depuis que j’ai beaucoup travaillé sur Oracle, Ill dit de cette perspective.

COUNT (*) – Récupère l’intégralité de la ligne dans le jeu de résultats avant de passer à la fonction count, la fonction count agrégera 1 si la ligne n’est pas null

COUNT (1) – N’attaque aucune ligne, count est appelé avec une valeur constante de 1 pour chaque ligne de la table lorsque le correspond.

Count (PK) – PK dans Oracle est indexé. Cela signifie qu’Oracle ne doit lire que l’index. Normalement, une ligne dans Index B + Tree est plusieurs fois plus petite que la ligne réelle. Ainsi, compte tenu du débit IOPS du disque, Oracle peut récupérer plusieurs fois plus de lignes dans Index avec un seul transfert de bloc par rapport à une ligne entière. Cela conduit à une augmentation du débit de la requête.

Vous pouvez voir que le premier compte est le plus lent et que le dernier compte est le plus rapide dans Oracle.