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

738

Sólo me preguntaba si alguno de ustedes usan Count(1)más Count(*)y si hay una diferencia notable en el rendimiento o si esto es sólo un hábito legado que se ha adelantado a partir de días pasado se ha ido?

La base de datos específica es SQL Server 2005.

super9
fuente
77
No sé acerca de SQL Server, pero en MySQL no hay diferencia. COUNT (columna) en el otro lado es diferente
Greg
118
No es verdad. COUNT (SomeColumn) solo devolverá el recuento de filas que contienen valores no nulos para SomeColumn. COUNT (*) y COUNT ('Foo') devolverán el número total de filas en la tabla.
Steve Broberg el
1
para más detalles comprobar esto select count 1 vs seleccionar la cuenta * en detalle con el gráfico
Ali Adravi
44
Wow Steve y aquí estuve 5 años en TSQL sin saber count (*) vs Count (ColumnName). Gracias
Harindaka
3
Tenga en cuenta también las respuestas a COUNT(*)vs COUNT(1)vs COUNT(pk)- ¿cuál es mejor? . También hay COUNT(*)vs COUNT(column-name), ¿cuál es más correcto? . Bien puede haber otros duplicados.
Jonathan Leffler

Respuestas:

598

No hay diferencia.

Razón:

Los libros en línea dicen " COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1" es una expresión no nula: es lo mismo que COUNT(*). El optimizador lo reconoce por lo que es: trivial.

Lo mismo que EXISTS (SELECT * ...oEXISTS (SELECT 1 ...

Ejemplo:

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

Mismo IO, mismo plan, las obras

Editar, agosto de 2011

Pregunta similar en DBA.SE .

Edit, dic 2011

COUNT(*)se menciona específicamente en ANSI-92 (busque " Scalar expressions 125")

Caso:

a) Si se especifica COUNT (*), entonces el resultado es la cardinalidad de T.

Es decir, el estándar ANSI lo reconoce como un sangrado obvio a lo que se refiere. COUNT(1)ha sido optimizado por los proveedores de RDBMS debido a esta superstición. De lo contrario, se evaluaría según ANSI

b) De lo contrario, deje que TX sea la tabla de una sola columna que es el resultado de aplicar la <expresión de valor> a cada fila de T y eliminar los valores nulos. Si se eliminan uno o más valores nulos, se genera una condición de finalización: advertencia-

gbn
fuente
73

En SQL Server, estas declaraciones producen los mismos planes.

Contrariamente a la opinión popular, en Oracle también lo hacen.

SYS_GUID() en Oracle es bastante computacional la función intensiva.

En mi base de datos de prueba, t_evenhay una tabla con 1,000,000filas

Esta consulta:

SELECT  COUNT(SYS_GUID())
FROM    t_even

se ejecuta durante 48segundos, ya que la función necesita evaluar cada uno SYS_GUID()devuelto para asegurarse de que no sea un NULL.

Sin embargo, esta consulta:

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

se ejecuta durante unos 2segundos, ya que ni siquiera intenta evaluar SYS_GUID()(a pesar de *ser un argumento para COUNT(*))

Quassnoi
fuente
debería evaluar SYS_GUID()al menos (quiero decir, exactamente) una vez para que la subconsulta devuelva el resultado, ¿verdad?
pide el
@asgs: ¿por qué piensas eso? ¿Cómo COUNT(*)depende de los valores de SYS_GUID?
Quassnoi
ahora que lo preguntas, no estoy seguro. Pensé que para COUNT(*)ejecutarse, necesita una tabla, por lo que la subconsulta debe actuar como tal. De lo contrario, no veo una manera de COUNT(*)devolver un valor significativo
pide el
1
@asgs: suponiendo que sabes qué hace el mapmétodo, ¿ves cómo estas dos expresiones: t_even.map(() => sys_guid()).lengthy t_even.lengthsiempre devolverían el mismo valor? El optimizador de Oracle es lo suficientemente inteligente como para verlo y optimizar la mapparte.
Quassnoi
1
@asgs exactamente. Solo una corrección menor: lengthno depende del contenido de la colección, solo del número de sus elementos. Si este número se almacena en los metadatos de la colección (este no es el caso de Oracle ni de la mayoría de los RDBMS modernos, pero es el caso del antiguo motor de almacenamiento de MySQL, MyISAM), entonces COUNT(*)solo necesitaría tomar el valor de los metadatos.
Quassnoi
65

Claramente, COUNT(*)y COUNT(1)será siempre devolverá el mismo resultado. Por lo tanto, si uno fuera más lento que el otro, se debería a un error optimizador. Dado que ambos formularios se usan con mucha frecuencia en las consultas, no tendría sentido que un DBMS permita que dicho error permanezca sin reparar. Por lo tanto, encontrará que el rendimiento de ambos formularios es (probablemente) idéntico en todos los DBMS SQL principales.

Tony Andrews
fuente
No lo consideraría un error si count (1) fuera más lento que count (*). Si le pide a dbms que genere 1s y cuente los que no son nulos, entonces sí, se reduce a ser el recuento de registros, pero no puede esperar que dbms detecte todas las tonterías que escribe y lo eluda por usted.
Thorsten Kettner
1
Bueno, un optimizador está destinado a optimizar y para un recuento solo hay que considerar 2 casos: expresión que puede ser nula, expresión que nunca será nula: el recuento (1) cae en el último, por lo que no es necesario que el DBMS "generar" 1s para responder la pregunta. (Por cierto, nunca usaría otra cosa que contar (*), solo por razones estéticas).
Tony Andrews
46

Trabajo en el equipo de SQL Server y espero aclarar algunos puntos en este hilo (no lo había visto anteriormente, así que lamento que el equipo de ingeniería no lo haya hecho antes).

En primer lugar, no existe una diferencia semántica entre select count(1) from tablevs select count(*) from table. Devuelven los mismos resultados en todos los casos (y es un error si no). Como se señaló en las otras respuestas, select count(column) from tablees semánticamente diferente y no siempre devuelve los mismos resultados que count(*).

En segundo lugar, con respecto al rendimiento, hay dos aspectos que serían importantes en SQL Server (y SQL Azure): trabajo en tiempo de compilación y trabajo en tiempo de ejecución. El trabajo del tiempo de compilación es una cantidad trivialmente pequeña de trabajo adicional en la implementación actual. Hay una expansión de * a todas las columnas en algunos casos, seguida de una reducción a 1 columna que se genera debido a cómo algunas de las operaciones internas funcionan en el enlace y la optimización. Dudo que aparezca en cualquier prueba medible, y es probable que se pierda en el ruido de todas las otras cosas que suceden debajo de las cubiertas (como estadísticas automáticas, sesiones xevent, gastos generales de almacenamiento de consultas, disparadores, etc.). Es tal vez unos pocos miles de instrucciones de CPU adicionales. Entonces, count (1) hace un poco menos de trabajo durante la compilación (lo que generalmente sucederá una vez y el plan se almacena en caché en varias ejecuciones posteriores). Para el tiempo de ejecución, suponiendo que los planes sean los mismos, no debería haber una diferencia medible. (Uno de los ejemplos anteriores muestra una diferencia: lo más probable es que se deba a otros factores en la máquina si el plan es el mismo).

En cuanto a cómo el plan puede ser potencialmente diferente. Es muy poco probable que sucedan, pero es potencialmente posible en la arquitectura del optimizador actual. El optimizador de SQL Server funciona como un programa de búsqueda (piense: un programa de computadora que juega al ajedrez buscando a través de varias alternativas para diferentes partes de la consulta y calcula las alternativas para encontrar el plan más barato en un tiempo razonable). Esta búsqueda tiene algunos límites sobre cómo funciona para mantener la compilación de consultas terminando en un tiempo razonable. Para consultas más allá de lo más trivial, hay fases de la búsqueda y se ocupan de tramos de consultas en función de lo costoso que el optimizador cree que la consulta es potencialmente ejecutable. Hay 3 fases de búsqueda principales, y cada fase puede ejecutar heurísticas más agresivas (costosas) tratando de encontrar un plan más barato que cualquier solución anterior. En última instancia, hay un proceso de decisión al final de cada fase que intenta determinar si debe devolver el plan que encontró hasta ahora o si debe seguir buscando. Este proceso utiliza el tiempo total empleado hasta ahora frente al costo estimado del mejor plan encontrado hasta el momento. Por lo tanto, en diferentes máquinas con diferentes velocidades de CPU es posible (aunque raro) obtener diferentes planes debido al tiempo de espera en una fase anterior con un plan en lugar de continuar en la siguiente fase de búsqueda. También hay algunos escenarios similares relacionados con el tiempo de espera de la última fase y la posibilidad de quedarse sin memoria en consultas muy costosas que consumen toda la memoria en la máquina (no suele ser un problema en 64 bits, pero fue una preocupación mayor) de nuevo en servidores de 32 bits). En última instancia, si obtiene un plan diferente, el rendimiento en tiempo de ejecución sería diferente. Yo no'

Net-net: utilice cualquiera de los dos que desee, ya que nada de esto importa de forma práctica. (Hay factores mucho, mucho más grandes que afectan el rendimiento en SQL más allá de este tema, sinceramente).

Espero que esto ayude. Escribí un capítulo del libro sobre cómo funciona el optimizador, pero no sé si es apropiado publicarlo aquí (ya que creo que recibo pequeñas regalías). Entonces, en lugar de publicar eso, publicaré un enlace a una charla que di en SQLBits en el Reino Unido sobre cómo funciona el optimizador a un alto nivel para que pueda ver las diferentes fases principales de la búsqueda con un poco más de detalle si lo desea para aprender sobre eso. Aquí está el enlace del video: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer

Conor Cunningham MSFT
fuente
2
Mi creencia es que 1también sufre la misma expansión. Baso esto en las pruebas de rendimiento aquí stackoverflow.com/questions/1597442/… también veo el ejemplo en esa respuesta de una consulta usando 1fallar inesperadamente cuando los permisos de nivel de columna están en juego
Martin Smith
21

En el estándar SQL-92, COUNT(*)significa específicamente "la cardinalidad de la expresión de la tabla" (podría ser una tabla base, `VIEW, tabla derivada, CTE, etc.).

Supongo que la idea es que COUNT(*)es fácil de analizar. El uso de cualquier otra expresión requiere que el analizador se asegure de que no hace referencia a ninguna columna ( COUNT('a')donde aes literal y COUNT(a)dónde aes una columna puede producir resultados diferentes).

En la misma línea, COUNT(*)un codificador humano familiarizado con los Estándares SQL puede elegirlo fácilmente, una habilidad útil cuando se trabaja con más de la oferta SQL de un proveedor.

Además, en el caso especial SELECT COUNT(*) FROM MyPersistedTable;, el pensamiento es que es probable que el DBMS contenga estadísticas para la cardinalidad de la tabla.

Por lo tanto, porque COUNT(1)y COUNT(*)son semánticamente equivalentes, yo uso COUNT(*).

un día cuando
fuente
1
Texto SQL-92 vinculado desde mi respuesta en DBA.SE: dba.stackexchange.com/questions/2511/…
gbn
17

COUNT(*)y COUNT(1)son iguales en caso de resultado y rendimiento.

Nakul Chaudhary
fuente
12

Esperaría que el optimizador se asegure de que no haya una diferencia real fuera de los casos extraños.

Como con cualquier cosa, la única forma real de saberlo es midiendo sus casos específicos.

Dicho eso, siempre lo he usado COUNT(*).

Ricardo
fuente
Según la respuesta aceptada, esto no es cierto para MS SQL: en realidad no hay diferencia entre los dos.
David Manheim
10

A medida que esta pregunta surge una y otra vez, aquí hay una respuesta más. Espero agregar algo para los principiantes que se preguntan sobre las "mejores prácticas" aquí.

SELECT COUNT(*) FROM something cuenta registros, lo cual es una tarea fácil.

SELECT COUNT(1) FROM something recupera un 1 por registro y luego cuenta los 1 que no son nulos, lo que esencialmente es contar registros, solo que es más complicado.

Dicho esto: Good dbms nota que la segunda declaración dará como resultado la misma cuenta que la primera declaración y la reinterpretará en consecuencia, para no hacer un trabajo innecesario. Por lo tanto, ambas declaraciones darán como resultado el mismo plan de ejecución y tomarán la misma cantidad de tiempo.

Sin embargo, desde el punto de vista de la legibilidad, debe usar la primera declaración. Desea contar registros, así que cuente registros, no expresiones. Use COUNT (expresión) solo cuando desee contar las ocurrencias no nulas de algo.

Thorsten Kettner
fuente
8

Ejecuté una prueba rápida en SQL Server 2012 en un cuadro Hyper-V de 8 GB de RAM. Puedes ver los resultados por ti mismo. No estaba ejecutando ninguna otra aplicación con ventana aparte de SQL Server Management Studio mientras ejecutaba estas pruebas.

Mi esquema de mesa:

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 en la Employeetabla: 178090131 (~ 178 millones de filas)

Primera consulta

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

Resultado de la primera 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 de la 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.

Puede notar que hay una diferencia de 83 (= 70265 - 70182) milisegundos que se pueden atribuir fácilmente a la condición exacta del sistema en el momento en que se ejecutan las consultas. También hice una sola carrera, por lo que esta diferencia será más precisa si hago varias carreras y hago un promedio. Si para un conjunto de datos tan grande la diferencia es inferior a 100 milisegundos, entonces podemos concluir fácilmente que las dos consultas no tienen ninguna diferencia de rendimiento exhibida por el motor de SQL Server.

Nota : RAM alcanza casi el 100% de uso en ambas ejecuciones. Reinicié el servicio de SQL Server antes de iniciar ambas ejecuciones.

RBT
fuente
7
SET STATISTICS TIME ON

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

Tiempos de ejecución de SQL Server:
tiempo de CPU = 31 ms, tiempo transcurrido = 36 ms.

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

Tiempos de ejecución de SQL Server:
tiempo de CPU = 46 ms, tiempo transcurrido = 37 ms.

Lo he ejecutado cientos de veces, borrando la memoria caché cada vez. Los resultados varían de vez en cuando a medida que varía la carga del servidor, pero casi siempre count(*)tiene un mayor tiempo de CPU.

Eyal Z.
fuente
14
No puedo reproducir esto. count(*)y count(1)devolver resultados con unos pocos ms de diferencia, incluso cuando cuento una tabla con 4,5 millones de filas, en mi instancia de SQL 2008.
Jeff Atwood
2
A veces, en algunos sistemas, la declaración que se ejecuta primero siempre se ejecuta más rápido ... ¿ha aleatorizado el orden en que se ejecutan?
JosephDoggie
@JosephDoggie siempre se debe reiniciar el servicio de SQL Server antes de ejecutar cada consulta mientras se realizan tales mediciones / estadísticas. Cuando acaba de iniciar el servicio de SQL Server, cada ejecución se vuelve totalmente independiente y, por lo tanto, el orden de consulta no debería importar. Por otro lado, si no reinicia el servicio de SQL Server y el motor realiza algún tipo de almacenamiento en caché de los planes de ejecución, la consulta que se ejecuta más tarde debería ejecutarse más rápido que el primero.
RBT
Los tiempos de ejecución deben tener en cuenta los planes de consulta exactos al hacer comparaciones. Si son diferentes (por ejemplo, agregado de hash versus agregado + agregado de flujo), entonces los resultados no son comparables. Por lo tanto, les pido precaución sacando conclusiones aquí sin más datos.
Conor Cunningham MSFT
3

Hay un artículo que muestra que COUNT(1)en Oracle es solo un alias COUNT(*), con una prueba al respecto.

Citaré algunas partes:

Hay una parte del software de base de datos que se llama "El Optimizador", que se define en la documentación oficial como "Software de base de datos incorporado que determina la forma más eficiente de ejecutar una declaración SQL".

Uno de los componentes del optimizador se llama "el transformador", cuya función es determinar si es ventajoso reescribir la declaración SQL original en una declaración SQL semánticamente equivalente que podría ser más eficiente.

¿Le gustaría ver qué hace el optimizador cuando escribe una consulta usando COUNT (1)?

Con un usuario con ALTER SESSIONprivilegios, se puede poner una tracefile_identifier, que el trazado optimizador y ejecutar la COUNT(1)selección, como: SELECT /* test-1 */ COUNT(1) FROM employees;.

Después de eso, necesita localizar los archivos de rastreo, con qué se puede hacer SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';. Más adelante en el archivo, encontrará:

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

Como puede ver, es solo un alias para COUNT(*).

Otro comentario importante: el COUNT(*)era realmente más rápido hace dos décadas en Oracle, antes de Oracle 7.3:

Count (1) se ha reescrito en count (*) desde 7.3 porque a Oracle le gusta autoajustar declaraciones míticas. En Oracle7 anterior, Oracle tenía que evaluar (1) para cada fila, como una función, antes de que DETERMINISTIC y NO DETERMINISTIC existieran.

Entonces, hace dos décadas, el recuento (*) era más rápido

Para otras bases de datos como Sql Server, debe investigarse individualmente para cada una.

Sé que esta pregunta es específica para SQL Server, pero las otras preguntas sobre SO sobre el mismo tema, sin mencionar la base de datos, se cerraron y se marcaron como duplicadas de esta respuesta.

Dherik
fuente
1

En todos los RDBMS, las dos formas de contar son equivalentes en términos del resultado que producen. Con respecto al rendimiento, no he observado ninguna diferencia de rendimiento en SQL Server, pero vale la pena señalar que algunos RDBMS, por ejemplo , PostgreSQL 11, tienen implementaciones menos óptimas COUNT(1)ya que verifican la nulabilidad de la expresión de argumento como se puede ver en esta publicación .

He encontrado una diferencia de rendimiento del 10% para las filas de 1 millón cuando se ejecuta:

-- Faster
SELECT COUNT(*) FROM t;

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

COUNT (1) no es sustancialmente diferente de COUNT (*), si es que lo hace. En cuanto a la cuestión de COUNTing NULLable COLUMNs, esto puede ser sencillo para demostrar las diferencias entre COUNT (*) y 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
fuente