Count (*) vs Count (1) – SQL Server

Je me demande si certains d’entre vous utilisent Count(1) sur Count(*) et s’il y a une différence de performance notable ou s’il s’agit d’une habitude héritée du passé?

(La firebase database spécifique est SQL Server 2005 )

Il n’y a pas de différence.

Raison:

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

“1” est une expression non nulle: c’est donc la même chose que COUNT(*) . L’optimiseur le reconnaît pour ce qu’il est: sortingvial.

EXISTS (SELECT * ... à EXISTS (SELECT * ... ou EXISTS (SELECT 1 ...

Exemple:

 SELECT COUNT(1) FROM dbo.tab800krows SELECT COUNT(1),FKID FROM dbo.tab800krows GROUP BY FKID SELECT COUNT(*) FROM dbo.tab800krows SELECT COUNT(*),FKID FROM dbo.tab800krows GROUP BY FKID 

Même IO, même plan, les travaux

Modifier, août 2011

Question similaire sur DBA.SE.

Edit, déc 2011

COUNT(*) est mentionné spécifiquement dans ANSI-92 (recherchez ” Scalar expressions 125 “)

Cas:

a) Si COUNT (*) est spécifié, le résultat est la cardinalité de T.

C’est-à-dire que la norme ANSI le reconnaît comme une évidence évidente. COUNT(1) a été optimisé par les fournisseurs de SGBDR en raison de cette superstition. Sinon, il serait évalué selon ANSI

b) Sinon, laissez TX être le tableau à colonne unique résultant de l’application de l’expression à chaque ligne de T et en éliminant les valeurs nulles. Si une ou plusieurs valeurs nulles sont éliminées, une condition d’achèvement est déclenchée:

Dans SQL Server, ces instructions produisent les mêmes plans.

Contrairement à l’opinion populaire, chez Oracle aussi.

SYS_GUID() dans Oracle est une fonction très intensive en calcul.

Dans ma base de test, t_even est une table avec 1,000,000 lignes

Cette requête:

 SELECT COUNT(SYS_GUID()) FROM t_even 

s’exécute pendant 48 secondes, car la fonction doit évaluer chaque SYS_GUID() renvoyé pour s’assurer que ce n’est pas une NULL .

Cependant, cette requête:

 SELECT COUNT(*) FROM ( SELECT SYS_GUID() FROM t_even ) 

fonctionne pendant 2 secondes, car il n’essaie même pas d’évaluer SYS_GUID() (malgré que * soit un argument pour COUNT(*) )

Clairement, COUNT (*) et COUNT (1) renverront toujours le même résultat. Par conséquent, si l’un était plus lent que l’autre, cela serait dû à un bogue d’optimisation. Comme les deux formulaires sont utilisés très fréquemment dans les requêtes, cela n’aurait aucun sens qu’un SGBD permette à un tel bogue de ne pas être corrigé. Par conséquent, vous constaterez que les performances des deux formes sont (probablement) identiques dans tous les principaux SGBD SQL.

Dans le standard SQL-92, COUNT(*) signifie spécifiquement “la cardinalité de l’expression de la table” (peut être une table de base, VIEW, table dérivée, CTE, etc.).

Je suppose que l’idée était que COUNT(*) est facile à parsingr. Utiliser une autre expression nécessite que l’parsingur vérifie qu’il ne fait référence à aucune colonne ( COUNT('a')a est un littéral et COUNT(a)a est une colonne peut donner des résultats différents).

Dans le même ordre d’idées, COUNT(*) peut être facilement choisi par un codeur humain familiarisé avec les normes SQL, une compétence utile lorsque vous travaillez avec plusieurs offres SQL d’un fournisseur.

En outre, dans le cas spécial SELECT COUNT(*) FROM MyPersistedTable; , la pensée est que le SGBD est susceptible de contenir des statistiques pour la cardinalité de la table.

Par conséquent, parce que COUNT(1) et COUNT(*) sont sémantiquement équivalents, j’utilise COUNT(*) .

COUNT(*) et COUNT(1) sont identiques en cas de résultat et de performance.

Je m’attendrais à ce que l’optimiseur s’assure qu’il n’y a pas de différence réelle en dehors des cas de bord bizarres.

Comme pour tout, la seule façon de le savoir est de mesurer vos cas spécifiques.

Cela dit, j’ai toujours utilisé COUNT(*) .

Comme cette question se pose encore et encore, voici une autre réponse. J’espère append quelque chose aux débutants qui s’interrogent sur les “meilleures pratiques” ici.

SELECT COUNT(*) FROM something compte des enregistrements, ce qui est une tâche facile.

SELECT COUNT(1) FROM something récupère un 1 par enregistrement et compte les 1 qui ne sont pas nuls, ce qui est essentiellement le compte des enregistrements, mais plus compliqué.

Ceci étant dit, les bons dbms notent que la deuxième instruction donnera le même compte que la première déclaration et la réinterprétera en conséquence, afin de ne pas faire de travail inutile. Donc, généralement, les deux déclarations aboutiront au même plan d’exécution et prendront le même temps.

Cependant, du sharepoint vue de la lisibilité, vous devez utiliser la première déclaration. Vous voulez compter les enregistrements, donc compter les enregistrements, pas les expressions. Utilisez COUNT (expression) uniquement lorsque vous voulez compter les occurrences non nulles de quelque chose.

J’ai effectué un test rapide sur SQL Server 2012 sur une boîte Hyper-V de 8 Go de RAM. Vous pouvez voir les résultats pour vous-même. Je n’utilisais aucune autre application fenêtrée en dehors de SQL Server Management Studio lors de l’exécution de ces tests.

Mon schéma de table:

 CREATE TABLE [dbo].[employee]( [Id] [bigint] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](50) NOT NULL, CONSTRAINT [PK_employee] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO 

Nombre total d’enregistrements dans la table des Employee : 178090131 (~ 178 millions de lignes)

Première requête:

 Set Statistics Time On Go Select Count(*) From Employee Go Set Statistics Time Off Go 

Résultat de la première requête:

  SQL Server parse and comstack time: CPU time = 0 ms, elapsed time = 35 ms. (1 row(s) affected) SQL Server Execution Times: CPU time = 10766 ms, elapsed time = 70265 ms. SQL Server parse and comstack time: CPU time = 0 ms, elapsed time = 0 ms. 

Deuxième requête:

  Set Statistics Time On Go Select Count(1) From Employee Go Set Statistics Time Off Go 

Résultat de la deuxième requête:

  SQL Server parse and comstack time: CPU time = 14 ms, elapsed time = 14 ms. (1 row(s) affected) SQL Server Execution Times: CPU time = 11031 ms, elapsed time = 70182 ms. SQL Server parse and comstack time: CPU time = 0 ms, elapsed time = 0 ms. 

Vous remarquerez qu’il y a une différence de 83 (= 70265 – 70182) millisecondes qui peut facilement être atsortingbuée à la condition exacte du système au moment de l’exécution des requêtes. Aussi, j’ai fait une seule course, donc cette différence deviendra plus précise si je fais plusieurs courses et fais de la moyenne. Si, pour un dataset aussi important, la différence est inférieure à 100 millisecondes, nous pouvons facilement conclure que le moteur SQL Server ne présente aucune différence de performance entre les deux requêtes.

Remarque : la RAM atteint une utilisation proche de 100% dans les deux exécutions. J’ai redémarré le service SQL Server avant de démarrer les deux exécutions.

 SET STATISTICS TIME ON select count(1) from MyTable (nolock) -- table containing 1 million records. 

Temps d’exécution SQL Server:
Temps CPU = 31 ms, temps écoulé = 36 ms.

 select count(*) from MyTable (nolock) -- table containing 1 million records. 

Temps d’exécution SQL Server:
Temps CPU = 46 ms, temps écoulé = 37 ms.

J’ai couru des centaines de fois en effaçant le cache à chaque fois. Les résultats varient de temps en temps, la charge du serveur variant, mais presque toujours le temps de calcul du compte (*) est plus élevé.

Il y a un article montrant que le COUNT(1) sur Oracle est juste un alias à COUNT(*) , avec une preuve à ce sujet.

Je citerai quelques parties:

Une partie du logiciel de firebase database s’appelle «The Optimizer», qui est définie dans la documentation officielle comme «Logiciel de firebase database intégré qui détermine le moyen le plus efficace d’exécuter une requête SQL».

L’un des composants de l’optimiseur est appelé «le transformateur», dont le rôle est de déterminer s’il est avantageux de réécrire l’instruction SQL d’origine en une instruction SQL sémantiquement équivalente qui pourrait être plus efficace.

Voulez-vous voir ce que fait l’optimiseur lorsque vous écrivez une requête à l’aide de COUNT (1)?

Avec un utilisateur disposant du privilège ALTER SESSION , vous pouvez mettre un tracefile_identifier , activer le traçage de l’optimiseur et exécuter la sélection COUNT(1) , comme: SELECT /* test-1 */ COUNT(1) FROM employees; .

Après cela, vous devez localiser les fichiers de trace, ce qui peut être fait avec SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace'; . Plus tard dans le fichier, vous trouverez:

 SELECT COUNT(*) “COUNT(1)” FROM “COURSE”.”EMPLOYEES” “EMPLOYEES” 

Comme vous pouvez le voir, c’est juste un alias pour COUNT(*) .

Autre commentaire important: le COUNT(*) était vraiment plus rapide il y a deux décennies sur Oracle, avant Oracle 7.3:

Count (1) a été réécrit dans count (*) depuis 7.3 car Oracle aime régler automatiquement les déclarations mythiques. Dans Oracle 7, oracle devait évaluer (1) pour chaque ligne, en tant que fonction, avant que DETERMINISTIC et NON-DETERMINISTIC n’existent.

Il y a deux décennies, count (*) était plus rapide

Pour une autre firebase database en tant que Sql Server, elle doit être recherchée individuellement pour chacune.

Je sais que cette question est spécifique à Sql Server, mais les autres questions sur SO concernant le même sujet, sans mentionner la firebase database, ont été fermées et marquées comme dupliquées à partir de cette réponse.

Facile à démontrer COUNT (*) vs COUNT () –

 USE tempdb; GO IF OBJECT_ID( N'dbo.Blitzen', N'U') IS NOT NULL DROP TABLE dbo.Blitzen; GO CREATE TABLE dbo.Blitzen (ID INT NULL, Somelala CHAR(1) NULL); INSERT dbo.Blitzen SELECT 1, 'A'; INSERT dbo.Blitzen SELECT NULL, NULL; INSERT dbo.Blitzen SELECT NULL, 'A'; INSERT dbo.Blitzen SELECT 1, NULL; SELECT COUNT(*), COUNT(1), COUNT(ID), COUNT(Somelala) FROM dbo.Blitzen; GO DROP TABLE dbo.Blitzen; GO