COUNT(*) vs COUNT(1) vs COUNT(pk): ¿cuál es mejor? [duplicar]


Esta pregunta ya tiene una respuesta aquí:

A menudo encuentro estas tres variantes:

SELECT COUNT(*) FROM Foo;
SELECT COUNT(1) FROM Foo;
SELECT COUNT(PrimaryKey) FROM Foo;

Por lo que puedo ver, todos hacen lo mismo, y me encuentro usando los tres en mi base de código. Sin embargo, no me gusta hacer lo mismo de diferentes maneras. ¿A cuál debo adherirme? Es cualquiera de ellos mejor que los otros dos?

Author: zneak, 2010-04-26

6 answers

Línea de fondo

Use COUNT(field) o COUNT(*), y quédese con él constantemente, y si su base de datos lo permite COUNT(tableHere) o COUNT(tableHere.*), use eso.

En resumen, no uses COUNT(1) para nada. Es un pony de un solo truco, que rara vez hace lo que quieres, y en esos raros casos es equivalente a count(*)

Use count(*) para contar

Use * para todas sus consultas que necesitan contar todo, incluso para uniones, use *

SELECT boss.boss_id, COUNT(subordinate.*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Pero no use COUNT(*) para Se une a la izquierda, ya que devolverá 1 incluso si la tabla subordinada no coincide con nada de la tabla padre

SELECT boss.boss_id, COUNT(*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

No se deje engañar por aquellos que aconsejan que cuando se usa * en CUENTA, obtiene toda la fila de su mesa, diciendo que * es lento. El * en SELECT COUNT(*) y SELECT * no tiene relación el uno con el otro, son cosas completamente diferentes, solo comparten un símbolo común, es decir, *.

Una sintaxis alternativa

De hecho, si no está permitido nombrar un campo como al igual que su nombre de tabla, RDBMS language designer podría dar COUNT(tableNameHere) la misma semántica que COUNT(*). Ejemplo:

Para contar filas podríamos tener esto:

SELECT COUNT(emp) FROM emp

Y podrían hacerlo más simple: {[51]]}

SELECT COUNT() FROM emp

Y para la IZQUIERDA se une, podríamos tener esto:

SELECT boss.boss_id, COUNT(subordinate)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Pero no pueden hacer eso (COUNT(tableNameHere)) ya que el estándar SQL permite nombrar un campo con el mismo nombre que el nombre de su tabla:

CREATE TABLE fruit -- ORM-friendly name
(
fruit_id int NOT NULL,
fruit varchar(50), /* same name as table name, 
                and let's say, someone forgot to put NOT NULL */
shape varchar(50) NOT NULL,
color varchar(50) NOT NULL
)

Contando con null

Y también, no es una buena práctica para hacer un campo nullable si su nombre coincide con el nombre de la tabla. Supongamos que tiene valores 'Banana', 'Apple', NULL, 'Pears' en el campo fruit. Esto no contará todas las filas, solo dará 3, no 4

SELECT count(fruit) FROM fruit

Aunque algunos RDBMS hacen ese tipo de principio (para contar las filas de la tabla, acepta el nombre de la tabla como parámetro de CONTEO), esto funcionará en Postgresql (si no hay un campo subordinate en cualquiera de las dos tablas a continuación, es decir, siempre y cuando no haya conflicto de nombres entre el nombre del campo y la tabla nombre):

SELECT boss.boss_id, COUNT(subordinate)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Pero eso podría causar confusión más adelante si agregamos un campo subordinate en la tabla, ya que contará el campo(que podría ser nullable), no las filas de la tabla.

Así que para estar en el lado seguro, use:

SELECT boss.boss_id, COUNT(subordinate.*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

count(1): El pony de un solo truco

En particular para COUNT(1), es un pony de un solo truco , funciona bien solo en una consulta de tabla:

SELECT COUNT(1) FROM tbl

Pero cuando usas joins, ese truco no funcionará en consultas multi-tabla sin su semántica estar confundido, y en particular no se puede escribir:

-- count the subordinates that belongs to boss
SELECT boss.boss_id, COUNT(subordinate.1)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Entonces, ¿cuál es el significado de CONTAR(1) aquí?

SELECT boss.boss_id, COUNT(1)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Es esto...?

-- counting all the subordinates only
SELECT boss.boss_id, COUNT(subordinate.boss_id)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

O esto...?

-- or is that COUNT(1) will also count 1 for boss regardless if boss has a subordinate
SELECT boss.boss_id, COUNT(*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Por un pensamiento cuidadoso, se puede inferir que COUNT(1) es lo mismo que COUNT(*), independientemente del tipo de unión. Pero para el resultado de las uniones izquierdas, no podemos moldear COUNT(1) para trabajar como: COUNT(subordinate.boss_id), COUNT(subordinate.*)

Así que simplemente use cualquiera de los siguientes:

-- count the subordinates that belongs to boss
SELECT boss.boss_id, COUNT(subordinate.boss_id)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Funciona en Postgresql, está claro que desea cuenta la cardinalidad del conjunto

-- count the subordinates that belongs to boss
SELECT boss.boss_id, COUNT(subordinate.*)
FROM boss
LEFT JOIN subordinate on subordinate.boss_id = boss.boss_id
GROUP BY boss.id

Otra forma de contar la cardinalidad del conjunto, muy similar al inglés (simplemente no hagas una columna con un nombre igual que el nombre de su tabla): http://www.sqlfiddle.com/#!1/98515/7

select boss.boss_name, count(subordinate)
from boss
left join subordinate on subordinate.boss_code = boss.boss_code
group by boss.boss_name

No puedes hacer esto: http://www.sqlfiddle.com/#!1/98515/8

select boss.boss_name, count(subordinate.1)
from boss
left join subordinate on subordinate.boss_code = boss.boss_code
group by boss.boss_name

Puedes hacer esto, pero esto produce un resultado incorrecto: http://www.sqlfiddle.com/#!1/98515/9

select boss.boss_name, count(1)
from boss
left join subordinate on subordinate.boss_code = boss.boss_code
group by boss.boss_name
 188
Author: Michael Buen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-06 14:34:01

Dos de ellos siempre producen la misma respuesta:

  • COUNT(*) cuenta el número de filas
  • COUNT (1) también cuenta el número de filas

Asumiendo que 'pk ' es una clave primaria y que no se permiten nulos en los valores, entonces

  • COUNT (pk) también cuenta el número de filas

Sin embargo, si 'pk ' no está limitado a no ser nulo, entonces produce una respuesta diferente:

  • COUNT(possibly_null) cuenta el número de filas con valores no nulos en la columna possibly_null.

  • COUNT (DISTINCT pk) también cuenta el número de filas (porque una clave primaria no permite duplicados).

  • COUNT (DISTINCT possibly_null_or_dup) cuenta el número de valores distintos no nulos en la columna possibly_null_or_dup.

  • COUNT (DISTINCT possibly_duplicated) cuenta el número de valores distintos (necesariamente no nulos) en la columna possibly_duplicated cuando tiene la cláusula NOT NULL en se.

Normalmente, escribo COUNT(*); es la notación original recomendada para SQL. Del mismo modo, con la cláusula EXISTS, normalmente escribo WHERE EXISTS(SELECT * FROM ...) porque esa fue la notación original de recommend. No debería haber ningún beneficio para las alternativas; el optimizador debería ver a través de las notaciones más oscuras.

 47
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-26 01:30:15

Esto dependerá del tipo de base de datos que esté utilizando, así como del tipo de tabla en algunos casos.

Por ejemplo, usando MySQL, count(*) será rápido bajo una tabla MyISAM pero lento bajo un InnoDB. En InnoDB debe usar count(1) o count(pk).

 9
Author: Jarod Elliott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-26 01:14:44

Preguntado y contestado antes...

Libros en línea dice "COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1" es una expresión no nula por lo que es lo mismo que COUNT(*). El optimizador lo reconoce como trivial así que da el mismo plan. Un PK es único y no nulo (al menos en SQL Server) por lo que COUNT(PK) = COUNT ( * )

Este es un mito similar a EXISTS (SELECT * ... o EXISTS (SELECT 1 ...

Y ver el ANSI 92 spec , sección 6.5, Reglas Generales, caso 1

        a) If COUNT(*) is specified, then the result is the cardinality
          of T.

        b) Otherwise, let TX be the single-column table that is the
          result of applying the <value expression> to each row of T
          and eliminating null values. If one or more null values are
          eliminated, then a completion condition is raised: warning-
          null value eliminated in set function.
 6
Author: gbn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:26

Al menos en Oracle son todos iguales: http://www.oracledba.co.uk/tips/count_speed.htm

 5
Author: ZeissS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-26 01:15:08

Siento que las características de rendimiento cambian de DBMS a DBMS. Todo depende de cómo decidan implantarlo. Ya que he trabajado extensamente en Oracle, lo diré desde esa perspectiva.

COUNT (*) - Obtiene toda la fila en el conjunto de resultados antes de pasar a la función count, la función count agregará 1 si la fila no es nula

COUNT (1) - No obtendrá ninguna fila, en su lugar count se llama con un valor constante 1 para cada fila en la tabla cuando el coincidir.

Count(PK) - Los PK de oracle están indexados. Esto significa que Oracle solo tiene que leer el índice. Normalmente una fila en el árbol del Índice B + es muchas veces más pequeña que la fila real. Así que teniendo en cuenta la tasa de IOPS de disco, Oracle puede obtener muchas veces más filas de Índice con una sola transferencia de bloque en comparación con toda la fila. Esto conduce a una mayor througput de la consulta.

A partir de esto puede ver que el primer conteo es el más lento y el último conteo es el más rápido en Oracle.

 -2
Author: arunmur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-26 03:45:36