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

738

Imaginando se algum de vocês usa Count(1)demais Count(*)e se há uma diferença perceptível no desempenho ou se esse é apenas um hábito herdado que foi trazido adiante desde os dias passados.

O banco de dados específico é SQL Server 2005.

super9
fonte
7
Não sei sobre o SQL Server, mas no MySQL não há diferença. COUNT (coluna) sobre o outro lado é diferente
Greg
118
Não é verdade. COUNT (SomeColumn) retornará apenas a contagem de linhas que contêm valores não nulos para SomeColumn. COUNT (*) e COUNT ('Foo') retornarão o número total de linhas na tabela.
213 Steve Broberg
1
para mais detalhes verificar isso SELECT COUNT 1 vs select count * em detalhes com gráfico
Ali Adravi
4
Uau, Steve, e aqui eu estava com 5 anos no TSQL sem saber count (*) vs Count (ColumnName). Obrigado
Harindaka
3
Observe também as respostas para COUNT(*)vs COUNT(1)vs COUNT(pk)- o que é melhor? . Há também COUNT(*)vs COUNT(column-name)- o que é mais correto? . Pode haver outras duplicatas.
Jonathan Leffler

Respostas:

598

Não há diferença.

Razão:

Livros on-line dizem " COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1" é uma expressão não nula: portanto, é a mesma coisa que COUNT(*). O otimizador reconhece o que é: trivial.

O mesmo que EXISTS (SELECT * ...ouEXISTS (SELECT 1 ...

Exemplo:

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

Mesmo IO, mesmo plano, as obras

Editar, ago 2011

Pergunta semelhante sobre DBA.SE .

Editar, dez 2011

COUNT(*)é mencionado especificamente no ANSI-92 (procure por " Scalar expressions 125")

Caso:

a) Se COUNT (*) for especificado, o resultado será a cardinalidade de T.

Ou seja, o padrão ANSI reconhece como sangramento óbvio o que você quer dizer. COUNT(1)foi otimizado pelos fornecedores de RDBMS por causa dessa superstição. Caso contrário, seria avaliado conforme ANSI

b) Caso contrário, seja TX a tabela de coluna única que resulta da aplicação da <expressão de valor> a cada linha de T e da eliminação de valores nulos. Se um ou mais valores nulos forem eliminados, uma condição de conclusão será gerada: warning-

gbn
fonte
73

No SQL Server, essas instruções geram os mesmos planos.

Ao contrário da opinião popular, na Oracle eles também.

SYS_GUID() no Oracle é bastante função intensiva de computação.

No meu banco de dados de teste, t_evenhá uma tabela com 1,000,000linhas

Esta consulta:

SELECT  COUNT(SYS_GUID())
FROM    t_even

é executado por 48segundos, já que a função precisa avaliar cada um SYS_GUID()retornado para garantir que não seja um NULL.

No entanto, esta consulta:

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

é executado por apenas 2alguns segundos, já que nem tenta avaliar SYS_GUID()(apesar de *argumentar COUNT(*))

Quassnoi
fonte
deve avaliar SYS_GUID()pelo menos (quero dizer, exatamente) uma vez para a subconsulta retornar o resultado, certo?
ASGs
@asgs: por que você acha isso? Como COUNT(*)depende dos valores de SYS_GUID?
Quassnoi 28/10/19
agora que você pergunta, não tenho certeza. Eu pensei em COUNT(*)executar, ele precisa de uma tabela, então a subconsulta deve agir como uma. Caso contrário, não vejo uma maneira de COUNT(*)retornar um valor significativo
asgs 29/10
1
@ asgs: supondo que você saiba o que o mapmétodo faz, você vê como essas duas expressões: t_even.map(() => sys_guid()).lengthe t_even.lengthsempre retornaria o mesmo valor? O otimizador da Oracle é inteligente o suficiente para ver e otimizar a mappeça.
Quassnoi 30/10/19
1
@asgs exatamente. Apenas uma pequena correção: lengthnão depende muito do que a coleção consiste, apenas do número de seus elementos. Se esse número estiver armazenado nos metadados da coleção (esse não é o caso do Oracle ou da maioria dos outros RDBMS modernos, mas o do mecanismo de armazenamento antigo do MySQL, MyISAM), COUNT(*)seria necessário apenas pegar o valor dos metadados.
Quassnoi 31/10/19
65

Claramente, COUNT(*)e COUNT(1)irá sempre retornar o mesmo resultado. Portanto, se um fosse mais lento que o outro, seria efetivamente devido a um bug do otimizador. Como os dois formulários são usados ​​com muita frequência em consultas, não faria sentido para um DBMS permitir que esse bug permanecesse sem correção. Portanto, você descobrirá que o desempenho de ambos os formulários é (provavelmente) idêntico em todos os principais DBMSs SQL.

Tony Andrews
fonte
Eu não consideraria um erro se count (1) fosse mais lento que count (*). Se você pedir aos dbms para gerar 1s e contar os que não são nulos, sim, tudo se resume à contagem de registros, mas você não pode esperar que os dbms detectem todas as bobagens que você escreve e as contornem para você.
Thorsten Kettner
1
Bem, um otimizador deve otimizar e, para uma contagem, existem apenas 2 casos a serem considerados: expressão que pode ser nula, expressão que nunca será nula: count (1) se enquadra nessa última, portanto não há necessidade de o DBMS "gere" 1s para responder à pergunta. (BTW eu nunca usaria nada, mas COUNT (*), apenas por razões estéticas.)
Tony Andrews
46

Trabalho na equipe do SQL Server e espero esclarecer alguns pontos deste segmento (eu não o havia visto anteriormente, então lamento que a equipe de engenharia não tenha feito isso anteriormente).

Em primeiro lugar, não há diferença entre semântica select count(1) from tablevs select count(*) from table. Eles retornam os mesmos resultados em todos os casos (e é um erro, se não). Conforme observado nas outras respostas, select count(column) from tableé semanticamente diferente e nem sempre retorna os mesmos resultados que count(*).

Segundo, com relação ao desempenho, há dois aspectos que importariam no SQL Server (e no SQL Azure): trabalho em tempo de compilação e trabalho em tempo de execução. O trabalho de tempo de compilação é uma quantidade trivialmente pequena de trabalho extra na implementação atual. Há uma expansão do * para todas as colunas em alguns casos, seguida por uma redução de volta para 1 coluna, devido à forma como algumas das operações internas funcionam na ligação e otimização. Duvido que isso apareça em qualquer teste mensurável e provavelmente se perca no ruído de todas as outras coisas que acontecem sob as cobertas (como estatísticas automáticas, sessões xevent, sobrecarga do armazenamento de consultas, gatilhos etc.). São talvez alguns milhares de instruções extras sobre a CPU. Assim, count (1) trabalha um pouco menos durante a compilação (o que geralmente acontece uma vez e o plano é armazenado em cache em várias execuções subseqüentes). Para o tempo de execução, assumindo que os planos sejam os mesmos, não deve haver diferença mensurável. (Um dos exemplos anteriores mostra uma diferença - provavelmente é devido a outros fatores na máquina se o plano for o mesmo).

Sobre como o plano pode ser potencialmente diferente. É extremamente improvável que isso aconteça, mas é potencialmente possível na arquitetura do otimizador atual. O otimizador do SQL Server funciona como um programa de pesquisa (pense: programa de computador jogando xadrez pesquisando várias alternativas para diferentes partes da consulta e custando as alternativas para encontrar o plano mais barato em tempo razoável). Essa pesquisa possui alguns limites sobre como ela opera para manter a compilação de consultas concluída em tempo razoável. Para consultas além das mais triviais, há fases da pesquisa e elas lidam com trechos de consultas com base no quão caro o otimizador pensa que a consulta é potencialmente executada. Existem três fases principais de pesquisa e cada fase pode executar heurísticas mais agressivas (caras), tentando encontrar um plano mais barato do que qualquer solução anterior. Por fim, existe um processo de decisão no final de cada fase que tenta determinar se deve retornar o plano encontrado até agora ou se deve continuar pesquisando. Esse processo usa o tempo total gasto até agora em relação ao custo estimado do melhor plano encontrado até o momento. Portanto, em máquinas diferentes com velocidades diferentes de CPUs, é possível (embora raro) obter planos diferentes devido ao tempo limite excedido em uma fase anterior com um plano e continuar na próxima fase de pesquisa. Existem também alguns cenários semelhantes relacionados ao tempo limite da última fase e à falta de memória em consultas muito caras que consomem toda a memória da máquina (geralmente não é um problema em 64 bits, mas era uma preocupação maior). de volta em servidores de 32 bits). Por fim, se você obtiver um plano diferente, o desempenho no tempo de execução será diferente. Eu não'

Net-net: use o que você quiser, pois nada disso importa em nenhuma forma prática. (Existem fatores muito, muito maiores, que afetam o desempenho no SQL além deste tópico, honestamente).

Eu espero que isso ajude. Escrevi um capítulo de livro sobre como o otimizador funciona, mas não sei se é apropriado publicá-lo aqui (já que ainda recebo pequenos royalties dele). Então, em vez de postar, postarei um link em uma palestra que fiz no SQLBits no Reino Unido sobre como o otimizador funciona em um nível alto, para que você possa ver as diferentes fases principais da pesquisa com mais detalhes, se desejar para aprender sobre isso. Aqui está o link do vídeo: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer

Conor Cunningham MSFT
fonte
2
minha crença é que 1também passa pela mesma expansão. Baseio esta nos testes perf aqui stackoverflow.com/questions/1597442/... também ver o exemplo em que a resposta de uma consulta usando 1falhando inesperadamente quando permissões de nível de coluna estão em jogo
Martin Smith
21

No padrão SQL-92, COUNT(*) significa especificamente "a cardinalidade da expressão da tabela" (pode ser uma tabela base, `VIEW, tabela derivada, CTE, etc).

Acho que a ideia era COUNT(*)fácil de analisar. O uso de qualquer outra expressão requer que o analisador garanta que não faça referência a nenhuma coluna ( COUNT('a')onde aé literal e COUNT(a)onde aestá uma coluna pode produzir resultados diferentes).

Na mesma linha, COUNT(*)pode ser facilmente escolhido por um codificador humano familiarizado com os Padrões SQL, uma habilidade útil ao trabalhar com mais de uma oferta de SQL de um fornecedor.

Além disso, no caso especial SELECT COUNT(*) FROM MyPersistedTable;, o pensamento é que o DBMS provavelmente manterá estatísticas para a cardinalidade da tabela.

Portanto, porque COUNT(1)e COUNT(*)são semanticamente equivalentes, eu uso COUNT(*).

um dia quando
fonte
1
Texto SQL-92 vinculado da minha resposta no DBA.SE: dba.stackexchange.com/questions/2511/…
gbn
17

COUNT(*)e COUNT(1)são iguais em caso de resultado e desempenho.

Nakul Chaudhary
fonte
12

Espero que o otimizador garanta que não haja diferença real fora dos casos extremos.

Como com qualquer coisa, a única maneira real de dizer é medir seus casos específicos.

Dito isto, eu sempre usei COUNT(*).

Richard
fonte
Pela resposta aceita, isso não é verdade para o MS SQL - na verdade não há diferença entre os dois.
David Manheim
10

Como essa pergunta surge repetidamente, aqui está mais uma resposta. Espero adicionar algo para iniciantes que se perguntam sobre as "melhores práticas" aqui.

SELECT COUNT(*) FROM something conta registros, o que é uma tarefa fácil.

SELECT COUNT(1) FROM something recupera 1 por registro e depois conta os 1s que não são nulos, o que é essencialmente contando registros, apenas mais complicado.

Dito isto: Os bons dbms observam que a segunda instrução resultará na mesma contagem que a primeira e a interpretará de acordo, para não realizar trabalhos desnecessários. Portanto, geralmente as duas instruções resultam no mesmo plano de execução e levam a mesma quantidade de tempo.

No entanto, do ponto de legibilidade, você deve usar a primeira instrução. Você deseja contar registros, portanto, conte registros, não expressões. Use COUNT (expressão) apenas quando desejar contar ocorrências não nulas de algo.

Thorsten Kettner
fonte
8

Fiz um teste rápido no SQL Server 2012 em uma caixa hyper-v de 8 GB de RAM. Você pode ver os resultados por si mesmo. Eu não estava executando nenhum outro aplicativo em janela além do SQL Server Management Studio durante a execução desses testes.

Meu esquema de tabela:

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

Número total de registros na Employeetabela: 178090131 (~ 178 milhões de linhas)

Primeira consulta:

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

Resultado da primeira consulta:

 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.

Segunda consulta:

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

Resultado da Segunda Consulta:

 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.

Você pode notar que há uma diferença de 83 (= 70265 - 70182) milissegundos que pode ser facilmente atribuída à condição exata do sistema no momento em que as consultas são executadas. Também fiz uma única execução, portanto essa diferença se tornará mais precisa se eu fizer várias e fizer uma média. Se, para um conjunto de dados tão grande, a diferença chegar a menos de 100 milissegundos, podemos concluir facilmente que as duas consultas não têm nenhuma diferença de desempenho exibida pelo SQL Server Engine.

Nota : a RAM atinge quase 100% de uso nas duas execuções. Eu reiniciei o serviço SQL Server antes de iniciar as duas execuções.

RBT
fonte
7
SET STATISTICS TIME ON

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

Tempos de execução do SQL Server:
tempo de CPU = 31 ms, tempo decorrido = 36 ms.

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

Tempos de execução do SQL Server:
tempo de CPU = 46 ms, tempo decorrido = 37 ms.

Eu executei isso centenas de vezes, limpando o cache todas as vezes. Os resultados variam de tempos em tempos, conforme a carga do servidor varia, mas quase sempre count(*)apresenta um tempo de CPU mais alto.

Eyal Z.
fonte
14
Eu não posso reproduzir isso. count(*)e count(1)retornar resultados a alguns ms um do outro, mesmo ao contar uma tabela com 4,5 milhões de linhas, na minha instância do SQL 2008.
Jeff Atwood
2
Às vezes, em alguns sistemas, a instrução executada primeiro sempre é executada mais rapidamente ... você aleatoriamente a ordem em que elas são executadas?
precisa saber é o seguinte
O @JosephDoggie one deve sempre reiniciar o serviço SQL Server antes de executar todas as consultas enquanto realiza essas medições / estatísticas. Quando você acaba de iniciar o serviço SQL Server, todas as execuções se tornam totalmente independentes e, portanto, a ordem das consultas não importa. Por outro lado, se você não reiniciar o serviço do SQL Server e o mecanismo fizer algum tipo de cache dos planos de execução, a consulta executada posteriormente deverá ser executada mais rapidamente, não a primeira.
RBT
Os tempos de execução precisam examinar os planos de consulta exatos ao fazer comparações. Se eles forem diferentes (por exemplo, agregado de hash vs. classificação + agregado de fluxo), os resultados não serão comparáveis. Portanto, peço cautela para tirar conclusões aqui sem mais dados.
Conor Cunningham MSFT
3

Há um artigo mostrando que a COUNT(1)sobre a Oracle é apenas um alias para COUNT(*), com uma prova sobre isso.

Vou citar algumas partes:

Há uma parte do software de banco de dados chamada "The Optimizer", que é definida na documentação oficial como "Software de banco de dados interno que determina a maneira mais eficiente de executar uma instrução SQL".

Um dos componentes do otimizador é chamado "o transformador", cuja função é determinar se é vantajoso reescrever a instrução SQL original em uma instrução SQL semanticamente equivalente que poderia ser mais eficiente.

Deseja ver o que o otimizador faz quando escreve uma consulta usando COUNT (1)?

Com um usuário com ALTER SESSIONprivilégios, você pode colocar um tracefile_identifier, habilitar o rastreamento otimizador e executar o COUNT(1)select, como: SELECT /* test-1 */ COUNT(1) FROM employees;.

Depois disso, você precisa localizar os arquivos de rastreamento, o que pode ser feito SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';. Posteriormente no arquivo, você encontrará:

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

Como você pode ver, é apenas um apelido para COUNT(*).

Outro comentário importante: COUNT(*)foi realmente mais rápido, há duas décadas, no Oracle, antes do Oracle 7.3:

O Count (1) foi reescrito no count (*) desde 7.3, porque o Oracle gosta de ajustar automaticamente as declarações míticas. No Oracle7 anterior, o oracle precisava avaliar (1) para cada linha, como uma função, antes da existência de DETERMINÍSTICO e NÃO DETERMINÍSTICO.

Então, há duas décadas, a contagem (*) era mais rápida

Para outros bancos de dados como o Sql Server, ele deve ser pesquisado individualmente para cada um.

Eu sei que esta pergunta é específica para o Sql Server, mas as outras perguntas sobre SO sobre o mesmo assunto, sem mencionar o banco de dados, foram fechadas e marcadas como duplicadas nesta resposta.

Dherik
fonte
1

Em todos os RDBMS, as duas maneiras de contar são equivalentes em termos de qual resultado elas produzem. Em relação ao desempenho, não observei nenhuma diferença de desempenho no SQL Server, mas pode valer a pena ressaltar que alguns RDBMS, por exemploCOUNT(1) , PostgreSQL 11, têm implementações menos ideais , pois verificam a nulidade da expressão de argumento, como pode ser visto nesta publicação .

Encontrei uma diferença de desempenho de 10% para 1 milhão de linhas ao executar:

-- Faster
SELECT COUNT(*) FROM t;

-- 10% slower
SELECT COUNT(1) FROM t;
Lukas Eder
fonte
0

COUNT (1) não é substancialmente diferente de COUNT (*), se houver. Quanto à questão de COUNTing NULLable COLUMNs, isso pode ser simples para demonstrar as diferenças entre 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
Graeme
fonte