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


738

Ti stai solo chiedendo se qualcuno di voi usa Count(1)più Count(*)e se c'è una notevole differenza nelle prestazioni o se questa è solo un'abitudine ereditaria che è stata portata avanti da giorni passati?

Il database specifico è SQL Server 2005.


7
Non so di SQL Server ma in MySQL non c'è differenza. COUNT (colonna) invece è diverso
Greg

118
Non vero. COUNT (SomeColumn) restituirà solo il conteggio delle righe che contengono valori non nulli per SomeColumn. COUNT (*) e COUNT ('Foo') restituiranno il numero totale di righe nella tabella.
Steve Broberg,


4
Wow Steve e qui sono stato a 5 anni in TSQL senza conoscere count (*) vs Count (ColumnName). Grazie
Harindaka,

Risposte:


598

Non c'è differenza.

Motivo:

I libri online dicono " COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1" è un'espressione non nulla: quindi è uguale a COUNT(*). L'ottimizzatore lo riconosce per quello che è: banale.

Lo stesso di EXISTS (SELECT * ...oEXISTS (SELECT 1 ...

Esempio:

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

Stesso IO, stesso piano, i lavori

Modifica, ago 2011

Domanda simile su DBA.SE .

Modifica, dic 2011

COUNT(*)è menzionato specificamente in ANSI-92 (cercare " Scalar expressions 125")

Astuccio:

a) Se viene specificato COUNT (*), il risultato è la cardinalità di T.

Cioè, lo standard ANSI lo riconosce come sanguinante ovvio cosa intendi. COUNT(1)è stato ottimizzato dai rivenditori RDBMS a causa di questa superstizione. Altrimenti sarebbe valutato secondo ANSI

b) Altrimenti, sia TX la tabella a colonna singola che è il risultato dell'applicazione di <espressione di valore> a ciascuna riga di T e dell'eliminazione di valori nulli. Se uno o più valori null vengono eliminati, viene sollevata una condizione di completamento: avviso-


73

In SQL Server, queste istruzioni producono gli stessi piani.

Contrariamente all'opinione popolare, anche in Oracle lo fanno.

SYS_GUID() in Oracle è piuttosto una funzione intensiva di calcolo.

Nel mio database di test, t_evenè una tabella con 1,000,000righe

Questa query:

SELECT  COUNT(SYS_GUID())
FROM    t_even

viene eseguito per 48secondi, poiché la funzione deve valutare ogni SYS_GUID()restituito per assicurarsi che non sia un NULL.

Tuttavia, questa query:

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

funziona per 2pochi secondi, dal momento che non prova nemmeno a valutare SYS_GUID()(nonostante *sia argomento per COUNT(*))


dovrebbe valutare SYS_GUID()almeno (intendo esattamente) una volta che la sottoquery restituisca il risultato, giusto?
as

@asgs: perché la pensi così? In che modo COUNT(*)dipende dai valori di SYS_GUID?
Quassnoi,

ora che me lo chiedi, non ne sono sicuro. Ho pensato COUNT(*)di funzionare, ha bisogno di una tabella, quindi la query secondaria dovrebbe agire come una. Altrimenti, non vedo un modo per COUNT(*)restituire un valore significativo
chiede il

1
@asgs: supponendo che tu sappia cosa fa il mapmetodo, vedi come queste due espressioni: t_even.map(() => sys_guid()).lengthe t_even.lengthrestituirebbero sempre lo stesso valore? L'ottimizzatore di Oracle è abbastanza intelligente da vederlo e ottimizzare la mapparte.
Quassnoi,

1
@asgs esattamente. Solo una piccola correzione: lengthnon dipende del tutto dalla composizione della collezione, ma solo dal numero dei suoi elementi. Se questo numero è memorizzato nei metadati della raccolta (questo non è il caso di Oracle o della maggior parte degli altri RDBMS moderni, ma è il caso del vecchio motore di archiviazione di MySQL, MyISAM), allora COUNT(*)dovrebbe semplicemente prendere il valore dai metadati.
Quassnoi,

65

Chiaramente, COUNT(*)e COUNT(1)sarà sempre lo stesso risultato. Pertanto, se uno fosse più lento dell'altro, sarebbe effettivamente dovuto a un bug di ottimizzazione. Poiché entrambi i moduli vengono utilizzati molto frequentemente nelle query, non avrebbe senso per un DBMS lasciare che un tale bug rimanga non corretto. Quindi scoprirai che le prestazioni di entrambi i moduli sono (probabilmente) identiche in tutti i principali DBMS SQL.


Non lo considero un bug se count (1) fosse più lento di count (*). Se chiedi ai dbms di generare 1 e contare quelli che non sono nulli, allora sì, si riduce a essere il conteggio dei record, ma non puoi aspettarti che i dbms rilevi tutte le assurdità che scrivi e aggirino per te.
Thorsten Kettner,

1
Bene, un ottimizzatore ha lo scopo di ottimizzare e per un conteggio ci sono solo 2 casi da considerare: espressione che può essere nulla, espressione che non sarà mai nulla: il conteggio (1) cade in quest'ultimo, quindi non è necessario che il DBMS "genera" 1s per rispondere alla domanda. (A proposito non userei mai altro che contare (*), solo per motivi estetici.)
Tony Andrews,

46

Lavoro nel team di SQL Server e spero di chiarire alcuni punti in questo thread (non l'avevo mai visto prima, quindi mi dispiace che il team di ingegneri non l'abbia già fatto in precedenza).

In primo luogo, non v'è alcuna differenza semantica tra select count(1) from tablevs. select count(*) from table. Restituiscono gli stessi risultati in tutti i casi (ed è un bug in caso contrario). Come notato nelle altre risposte, select count(column) from tableè semanticamente diverso e non restituisce sempre gli stessi risultati di count(*).

In secondo luogo, per quanto riguarda le prestazioni, ci sono due aspetti che contano in SQL Server (e SQL Azure): lavoro in fase di compilazione e lavoro in fase di esecuzione. Il tempo di compilazione è una quantità banale di lavoro extra nell'attuale implementazione. Vi è un'espansione del * in tutte le colonne in alcuni casi seguita da una riduzione di 1 colonna in uscita a causa del modo in cui alcune delle operazioni interne funzionano in associazione e ottimizzazione. Dubito che si presenterebbe in qualsiasi test misurabile e probabilmente si perderà nel rumore di tutte le altre cose che accadono sotto le copertine (come auto-statistiche, sessioni xevent, overhead dell'archivio query, trigger, ecc.). È forse qualche migliaio di istruzioni extra sulla CPU. Così, count (1) fa un po 'meno lavoro durante la compilazione (che di solito accade una volta e il piano viene memorizzato nella cache in più esecuzioni successive). Per i tempi di esecuzione, supponendo che i piani siano gli stessi, non ci dovrebbero essere differenze misurabili. (Uno degli esempi precedenti mostra una differenza: è molto probabilmente dovuto ad altri fattori sulla macchina se il piano è lo stesso).

Come il piano può potenzialmente essere diverso. È estremamente improbabile che ciò accada, ma è potenzialmente possibile nell'architettura dell'attuale ottimizzatore. L'ottimizzatore di SQL Server funziona come un programma di ricerca (pensate: programma per computer che gioca a scacchi cercando varie alternative per diverse parti della query e costando le alternative per trovare il piano più economico in tempi ragionevoli). Questa ricerca ha alcuni limiti su come funziona per mantenere la compilazione della query in tempi ragionevoli. Per le query oltre le più banali, ci sono fasi della ricerca e si occupano di tranche di query in base a quanto l'ottimizzatore ritiene costoso eseguire potenzialmente la query. Esistono 3 fasi di ricerca principali e ogni fase può eseguire euristiche più aggressive (costose) cercando di trovare un piano più economico di qualsiasi soluzione precedente. Alla fine, c'è un processo decisionale alla fine di ogni fase che cerca di determinare se deve restituire il piano trovato finora o se deve continuare a cercare. Questo processo utilizza il tempo totale impiegato finora rispetto al costo stimato del miglior piano trovato finora. Quindi, su macchine diverse con diverse velocità di CPU è possibile (anche se raro) ottenere piani diversi a causa del timeout in una fase precedente con un piano anziché continuare nella fase di ricerca successiva. Esistono anche alcuni scenari simili relativi al timeout dell'ultima fase e alla potenziale esaurimento della memoria per query molto, molto costose che consumano tutta la memoria della macchina (di solito non è un problema a 64 bit ma era una preoccupazione maggiore di nuovo su server a 32 bit). In definitiva, se si ottiene un piano diverso, le prestazioni in fase di esecuzione differirebbero. Io non

Net-net: si prega di utilizzare qualunque dei due desiderati poiché nulla di tutto ciò ha importanza in qualsiasi forma pratica. (Ci sono fattori molto, molto più grandi che incidono sulle prestazioni in SQL oltre questo argomento, onestamente).

Spero che questo possa essere d'aiuto. Ho scritto un capitolo di un libro su come funziona l'ottimizzatore, ma non so se sia appropriato pubblicarlo qui (visto che ancora ricevo piccoli canoni da esso, credo). Quindi, invece di postare che posterò un link a un discorso che ho tenuto a SQLBits nel Regno Unito su come funziona l'ottimizzatore ad alto livello in modo da poter vedere le diverse fasi principali della ricerca in modo un po 'più dettagliato se vuoi per saperne di più. Ecco il link del video: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer


2
la mia convinzione è che 1subisce anche la stessa espansione. Baso questo sui test perf qui stackoverflow.com/questions/1597442/… vedi anche l'esempio in quella risposta di una query usando 1fallimenti imprevisti quando sono in gioco permessi a livello di colonna
Martin Smith

21

Nello standard SQL-92, COUNT(*)significa specificamente "la cardinalità dell'espressione di tabella" (potrebbe essere una tabella di base, `VIEW, tabella derivata, CTE, ecc.).

Immagino che l'idea fosse COUNT(*)facile da analizzare. L'uso di qualsiasi altra espressione richiede al parser di assicurarsi che non faccia riferimento a nessuna colonna ( COUNT('a')dove aè letterale e COUNT(a)dove si atrova una colonna può produrre risultati diversi).

Allo stesso modo, COUNT(*)può essere facilmente individuato da un programmatore umano che abbia familiarità con gli standard SQL, un'abilità utile quando si lavora con più di un'offerta SQL di un fornitore.

Inoltre, nel caso speciale SELECT COUNT(*) FROM MyPersistedTable;, è probabile che il DBMS sia in grado di contenere statistiche per la cardinalità della tabella.

Pertanto, poiché COUNT(1)e COUNT(*)sono semanticamente equivalenti, io uso COUNT(*).


1
Testo SQL-92 collegato dalla mia risposta su DBA.SE: dba.stackexchange.com/questions/2511/…
gbn


12

Mi aspetto che l'ottimizzatore assicuri che non vi siano differenze reali al di fuori dei casi strani.

Come in ogni altra cosa, l'unico vero modo di dire è misurare i tuoi casi specifici.

Detto questo, l'ho sempre usato COUNT(*).


Per la risposta accettata, questo non è vero per MS SQL - in realtà non c'è differenza tra i due.
David Manheim,

10

Man mano che questa domanda sorge ripetutamente, ecco un'altra risposta. Spero di aggiungere qualcosa per i principianti che si chiedono "best practice" qui.

SELECT COUNT(*) FROM something conta i record che è un compito facile.

SELECT COUNT(1) FROM something recupera un 1 per record e quindi conta gli 1 che non sono nulli, che essenzialmente conta i record, solo più complicati.

Detto questo: i buoni dbms notano che la seconda istruzione comporterà lo stesso conteggio della prima istruzione e la reinterpreteranno di conseguenza, in modo da non fare lavori inutili. Di solito, entrambe le dichiarazioni comporteranno lo stesso piano di esecuzione e impiegheranno la stessa quantità di tempo.

Comunque dal punto di leggibilità dovresti usare la prima affermazione. Vuoi contare i record, quindi conta i record, non le espressioni. Usa COUNT (espressione) solo quando vuoi contare occorrenze non nulle di qualcosa.


8

Ho eseguito un test rapido su SQL Server 2012 su una scatola hyper-v RAM da 8 GB. Puoi vedere i risultati per te stesso. Non stavo eseguendo altre applicazioni con finestre a parte SQL Server Management Studio durante l'esecuzione di questi test.

Il mio schema di tabella:

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

Numero totale di record nella Employeetabella: 178090131 (~ 178 milioni di righe)

Prima query:

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

Risultato della prima query:

 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.

Seconda query:

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

Risultato della seconda query:

 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.

È possibile notare una differenza di 83 (= 70265 - 70182) millisecondi che può essere facilmente attribuita all'esatta condizione del sistema al momento dell'esecuzione delle query. Inoltre ho fatto una singola corsa, quindi questa differenza diventerà più accurata se faccio diverse corse e faccio un po 'di media. Se per un set di dati così grande la differenza arriva a meno di 100 millisecondi, allora possiamo facilmente concludere che le due query non presentano alcuna differenza di prestazioni esibita da SQL Server Engine.

Nota : la RAM raggiunge un utilizzo prossimo al 100% in entrambe le esecuzioni. Ho riavviato il servizio SQL Server prima di avviare entrambe le esecuzioni.


7
SET STATISTICS TIME ON

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

Tempi di esecuzione di SQL Server:
tempo CPU = 31 ms, tempo trascorso = 36 ms.

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

Tempi di esecuzione di SQL Server:
tempo CPU = 46 ms, tempo trascorso = 37 ms.

L'ho eseguito centinaia di volte, svuotando la cache ogni volta .. I risultati variano di volta in volta al variare del carico del server, ma quasi sempre count(*)ha un tempo di CPU più elevato.


14
Non riesco a riprodurre questo. count(*)e count(1)restituire risultati a pochi ms l'uno dall'altro, anche quando si conteggia una tabella con 4,5 milioni di righe, nella mia istanza di SQL 2008.
Jeff Atwood,

2
A volte, in alcuni sistemi, l'istruzione eseguita per prima viene eseguita sempre più velocemente ... hai randomizzato l'ordine in cui sono eseguite?
JosephDoggie,

@JosephDoggie si dovrebbe sempre riavviare il servizio SQL Server prima di eseguire ogni query durante l'esecuzione di tali misurazioni / statistiche. Quando hai appena avviato il servizio SQL Server, ogni esecuzione diventa totalmente indipendente e l'ordine della query non dovrebbe avere importanza. D'altra parte, se non si riavvia il servizio SQL Server e il motore esegue una sorta di memorizzazione nella cache dei piani di esecuzione, la query in esecuzione in un secondo momento dovrebbe essere eseguita più velocemente, non la prima.
RBT

I tempi di esecuzione devono esaminare i piani di query esatti quando si effettuano i confronti. Se sono diversi (diciamo aggregato hash vs. ordinamento + aggregato flusso), i risultati non sono comparabili. Pertanto, esorto cautela nel trarre conclusioni qui senza ulteriori dati.
Conor Cunningham MSFT,

3

C'è un articolo che mostra che OracleCOUNT(1) su è solo un alias , con una prova al riguardo.COUNT(*)

Citerò alcune parti:

C'è una parte del software di database che si chiama "The Optimizer", che è definito nella documentazione ufficiale come "Software di database integrato che determina il modo più efficiente per eseguire un'istruzione SQL".

Uno dei componenti dell'ottimizzatore si chiama "il trasformatore", il cui ruolo è determinare se sia vantaggioso riscrivere l'istruzione SQL originale in un'istruzione SQL semanticamente equivalente che potrebbe essere più efficiente.

Ti piacerebbe vedere cosa fa l'ottimizzatore quando scrivi una query usando COUNT (1)?

Con un utente con ALTER SESSIONprivilegi, si può mettere un tracefile_identifier, consenta di rintracciare ottimizzatore ed eseguire il COUNT(1)selezionare, come: SELECT /* test-1 */ COUNT(1) FROM employees;.

Successivamente, è necessario localizzare i file di traccia, cosa è possibile fare SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';. Più avanti nel file troverai:

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

Come puoi vedere, è solo un alias per COUNT(*).

Un altro commento importante: COUNT(*)era molto più veloce due decenni fa su Oracle, prima di Oracle 7.3:

Count (1) è stato riscritto in count (*) dalla versione 7.3 perché Oracle ama mettere a punto le dichiarazioni mitiche. In Oracle7 precedente, Oracle doveva valutare (1) per ogni riga, come una funzione, prima che esistessero DETERMINISTIC e NON DETERMINISTIC.

Quindi, due decenni fa, contare (*) era più veloce

Per altri database come SQL Server, dovrebbe essere ricercato individualmente per ognuno.

So che questa domanda è specifica per SQL Server, ma le altre domande su SO relative allo stesso argomento, senza menzionare il database, sono state chiuse e contrassegnate come duplicate da questa risposta.


1

In tutti i RDBMS, i due modi di contare sono equivalenti in termini di risultato che producono. Per quanto riguarda le prestazioni, non ho osservato alcuna differenza di prestazioni in SQL Server, ma potrebbe valere la pena sottolineare che alcuni RDBMS, ad esempio PostgreSQL 11, hanno implementazioni meno ottimali per COUNT(1)il controllo della nullità dell'espressione dell'argomento, come si può vedere in questo post .

Ho riscontrato una differenza di prestazioni del 10% per le righe 1M durante l'esecuzione:

-- Faster
SELECT COUNT(*) FROM t;

-- 10% slower
SELECT COUNT(1) FROM t;

0

COUNT (1) non è sostanzialmente diverso da COUNT (*), se non del tutto. Per quanto riguarda la domanda di COUNTING NULLable COLUMNs, questo può essere semplice per dimostrare le differenze tra COUNT (*) e 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.