web-dev-qa-db-fra.com

Nombre (*) vs nombre (1) - SQL Server

Vous vous demandez simplement si certains d'entre vous utilisent Count(1) sur Count(*) et s'il y a une différence notable de performances ou s'il s'agit simplement d'une habitude héritée du passé?

(La base de données spécifique est SQL Server 2005.)

675
super9

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: trivial.

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

Edit, août 2011

Question similaire sur DBA.SE .

Edit, déc 2011

COUNT(*) est spécifiquement mentionné 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 reconnaît que ce que vous voulez dire est évident. COUNT(1) a été optimisé par les fournisseurs de SGBDR car 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 <value expression> à chaque ligne de T et de l'élimination des valeurs nulles. Si une ou plusieurs valeurs nulles sont éliminées, une condition d'achèvement est alors créée: warning-

562
gbn

Dans SQL Server, ces instructions génèrent les mêmes plans.

Contrairement à l'opinion populaire, ils le font également dans Oracle.

SYS_GUID() dans Oracle est une fonction assez intensive en calculs.

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ée pour s'assurer que ce n'est pas un NULL.

Cependant, cette requête:

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

s'exécute pendant mais 2 secondes, car il n'essaie même pas d'évaluer SYS_GUID() (malgré que * soit l'argument de COUNT(*))

70
Quassnoi

Clairement, COUNT (*) et COUNT (1) vont toujours retourner le même résultat. Par conséquent, si l'un était plus lent que l'autre, cela serait effectivement dû à un bug d'optimiseur. Étant donné que les deux formulaires sont très fréquemment utilisés dans les requêtes, il n’a aucun sens pour un SGBD de laisser un tel bogue rester non corrigé. Par conséquent, vous constaterez que les performances des deux formulaires sont (probablement) identiques dans tous les principaux SGBD SQL.

58
Tony Andrews

Dans la norme SQL-92, COUNT(*) signifie spécifiquement "la cardinalité de l'expression de table" (pourrait être une table de base, `VIEW, une table dérivée, CTE, etc.).

Je suppose que l’idée était que COUNT(*) est facile à analyser. L'utilisation de toute autre expression nécessite que l'analyseur s'assure qu'il ne référence aucune colonne (COUNT('a')a est un littéral et COUNT(a)a est une colonne pouvant donner des résultats différents).

Dans le même ordre d'idées, COUNT(*) peut être facilement sélectionné par un codeur humain familiarisé avec les normes SQL, une compétence utile pour travailler avec l'offre SQL de plusieurs fournisseurs.

De plus, dans le cas spécial SELECT COUNT(*) FROM MyPersistedTable;, on pense que le SGBD est susceptible de contenir des statistiques sur la cardinalité de la table.

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

20
onedaywhen

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

16
Nakul Chaudhary

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

Comme pour tout, le seul moyen de le savoir est de mesurer vos cas spécifiques.

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

12
Richard

J'ai effectué un test rapide sur SQL Server 2012 sur un boîtier hyper-v de 8 Go RAM. Vous pouvez voir les résultats par vous-même. Je n'exécutais aucune autre application fenêtrée que 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 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 compile 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 compile 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 compile 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 compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Vous remarquerez une différence de 83 millisecondes (= 70265 - 70182) qui peut facilement être attribuée à la condition exacte du système au moment de l’exécution des requêtes. De plus, j’ai fait un seul essai, alors cette différence deviendra plus précise si je fais plusieurs essais et que je fais la moyenne. Si, pour un ensemble de données aussi volumineux, la différence est inférieure à 100 millisecondes, nous pouvons facilement en conclure que les deux requêtes ne présentent aucune différence de performances présentée par le moteur SQL Server.

Note: RAM frappe près de 100% d'utilisation dans les deux exécutions. J'ai redémarré le service SQL Server avant de lancer les deux exécutions.

8
RBT

Alors que cette question revient sans cesse, voici une autre réponse. J'espère ajouter quelque chose pour les débutants qui s'interrogent sur les "meilleures pratiques" ici.

SELECT COUNT(*) FROM something compte les 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 consiste essentiellement à compter les enregistrements, mais de manière plus compliquée.

Cela dit: Les bons dbms notent que la deuxième déclaration entraînera le même décompte que la première déclaration et la réinterprétera en conséquence, afin de ne pas effectuer de travail inutile. Ainsi, les deux instructions aboutissent généralement au même plan d’exécution et prennent le même temps.

Cependant, du point de vue de la lisibilité, vous devriez utiliser la première déclaration. Vous voulez compter les enregistrements, alors comptez les enregistrements, pas les expressions. Utilisez COUNT (expression) uniquement lorsque vous souhaitez compter des occurrences non nulles de quelque chose.

8
SET STATISTICS TIME ON

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

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

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

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

Je l'ai exécutée des centaines de fois, en effaçant le cache à chaque fois. Les résultats varient en fonction de la charge du serveur, mais comptent presque toujours (*) avec un temps processeur supérieur.

7
Eyal Z.

Je travaille sur l'équipe SQL Server et j'espère pouvoir clarifier quelques points de ce fil (je ne l'avais jamais vu auparavant, je suis donc désolé, l'équipe d'ingénieurs ne l'a pas encore fait auparavant).

Premièrement, il n'y a pas de différence sémantique entre select count(1) from table et select count(*) from table. Ils retournent les mêmes résultats dans tous les cas (et c'est un bug sinon). Comme indiqué dans les autres réponses, select count(column) from table est sémantiquement différent et ne renvoie pas toujours les mêmes résultats que count(*).

Deuxièmement, en ce qui concerne les performances, deux aspects importent dans SQL Server (et SQL Azure): le travail au moment de la compilation et le travail au moment de l'exécution. Le temps de compilation est un peu plus de travail supplémentaire dans la mise en œuvre actuelle. Il existe dans certains cas une extension de * à toutes les colonnes, suivie d'une réduction à 1 colonne en raison du mode de fonctionnement de certaines opérations internes dans les opérations de liaison et d'optimisation. Je doute que tout test mesurable apparaisse et que tout ce qui se passe sous la couverture (comme les statistiques automatiques, les sessions xevent, la surcharge de la banque de requêtes, les déclencheurs, etc.) se perde dans le bruit. C'est peut-être quelques milliers d'instructions supplémentaires du processeur. Ainsi, count (1) effectue un peu moins de travail lors de la compilation (ce qui se produit généralement une fois et le plan est mis en cache lors de plusieurs exécutions ultérieures). Pour le temps d'exécution, en supposant que les plans soient les mêmes, il ne devrait y avoir aucune différence mesurable. (L’un des exemples précédents montre une différence - cela est probablement dû à d’autres facteurs sur la machine si le plan est identique).

En ce qui concerne la façon dont le plan peut potentiellement être différent. Il est extrêmement peu probable que cela se produise, mais cela est potentiellement possible dans l’architecture de l’optimiseur actuel. L'optimiseur de SQL Server fonctionne comme un programme de recherche (pensez: un programme informatique jouant aux échecs recherchant différentes alternatives pour différentes parties de la requête et calculant le coût des alternatives pour trouver le plan le moins cher dans un délai raisonnable). Cette recherche a quelques limites sur la façon dont elle fonctionne pour que la compilation de requêtes se termine dans un délai raisonnable. Pour les requêtes allant au-delà des plus simples, il y a des phases de recherche qui traitent des tranches de requêtes en fonction du coût d'exécution potentiel de l'optimiseur. Il y a 3 phases de recherche principales et chaque phase peut exécuter des heuristiques plus agressives (coûteuses) en essayant de trouver un plan moins cher que n'importe quelle solution antérieure. En fin de compte, un processus de décision à la fin de chaque phase tente de déterminer si le plan trouvé jusqu'ici doit être restitué ou s'il doit continuer à chercher. Ce processus utilise le temps total pris jusqu'ici par rapport au coût estimé du meilleur plan trouvé jusqu'à présent. Ainsi, sur différentes machines avec des vitesses de processeur différentes, il est possible (bien que rare) d’obtenir différents plans en raison de l’expiration du délai plus tôt avec un plan plutôt que de passer à la phase de recherche suivante. Il existe également quelques scénarios similaires liés à la temporisation de la dernière phase et au manque de mémoire potentiel pour des requêtes très coûteuses qui consomment toute la mémoire de la machine (ce n’est généralement pas un problème sur 64 bits, mais c’est un problème plus important). retour sur les serveurs 32 bits). En fin de compte, si vous obtenez un plan différent, les performances au moment de l'exécution différeront. Je ne pense pas qu'il soit peu probable que la différence de temps de compilation conduise JAMAIS à ce que l'une de ces conditions se produise.

Net-net: Veuillez utiliser l’un des deux de votre choix, car cela n’a aucune importance pratique. (Honnêtement, de nombreux facteurs beaucoup plus importants ont une incidence sur les performances en SQL.).

J'espère que ça aide. J'ai écrit un chapitre de livre sur le fonctionnement de l'optimiseur, mais je ne sais pas s'il convient de l'afficher ici (car je reçois toujours de minimes redevances, je crois). Donc, au lieu de poster cela, je posterai un lien vers une conférence que j'ai donnée à SQLBits au Royaume-Uni sur le fonctionnement de l'optimiseur à un niveau élevé afin que vous puissiez voir les différentes phases principales de la recherche de manière plus détaillée si vous le souhaitez. pour apprendre à ce sujet. Voici le lien vidéo: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer

6

Il y a un article montrant que la COUNT(1) sur Oracle est simplement un alias de COUNT(*), avec un preuve à ce sujet.

Je vais citer quelques parties:

Il existe une partie du logiciel de base de données appelée "L’optimiseur", définie dans la documentation officielle par le terme "logiciel de base de données intégré déterminant le moyen le plus efficace d’exécuter une instruction SQL".

L'un des composants de l'optimiseur s'appelle "le transformateur". Son rôle est de déterminer s'il est avantageux de réécrire l'instruction SQL d'origine en une instruction SQL équivalente sur le plan sémantique qui pourrait être plus efficace.

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

Avec un utilisateur avec le privilège ALTER SESSION, vous pouvez placer un tracefile_identifier, activer le traçage de l'optimiseur et exécuter la sélection COUNT(1), telle que: 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 constater, il s’agit simplement d’un alias pour COUNT(*).

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

Count (1) a été réécrit en count (*) depuis 7.3 car Oracle aime ajuster automatiquement les énoncés mythiques. Dans les versions antérieures d'Oracle7, Oracle devait évaluer (1) pour chaque ligne, en tant que fonction, avant que DETERMINISTIC et NON-DETERMINISTIC n'existent.

Donc, il y a deux décennies, compter (*) était plus rapide

Pour les autres bases de données telles que Sql Server, il convient de rechercher chacune d'elles individuellement.

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

3
Dherik

COUNT (1) n'est pas substantiellement différent de COUNT (*), voire pas du tout. En ce qui concerne la question COUNTing NULLable COLUMNs, il peut être simple de démontrer les différences entre COUNT (*) et COUNT (<some col>) -

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