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

Resuelto zneak asked hace 14 años • 4 respuestas

A menudo encuentro estas tres variantes:

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

Hasta donde puedo ver, todos hacen lo mismo y me encuentro usando los tres en mi código base. Sin embargo, no me gusta hacer lo mismo de diferentes maneras. ¿A cuál debería apegarme? ¿Alguno de ellos es mejor que los otros dos?

zneak avatar Apr 26 '10 08:04 zneak
Aceptado

Línea de fondo

Utilice COUNT(field)o COUNT(*)y manténgalo de forma constante, y si su base de datos lo permite COUNT(tableHere), COUNT(tableHere.*)utilícelo.

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

Usar count(*)para contar

Úselo *para todas sus consultas que necesiten 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 lo use COUNT(*)para uniones IZQUIERDA, ya que devolverá 1 incluso si la tabla subordinada no coincide con nada de la tabla principal.

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 le advierten que cuando se usa *en COUNT, recupera la fila completa de su tabla, diciendo que *es lento. On *y no tienen relación SELECT COUNT(*)entre SELECT *sí, son cosas completamente diferentes, solo comparten un token común, es decir *.

Una sintaxis alternativa

De hecho, si no está permitido nombrar un campo con el mismo nombre que el nombre de su tabla, el diseñador del lenguaje RDBMS podría proporcionar 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 sencillo:

SELECT COUNT() FROM emp

Y para LEFT JOIN, 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 nulo

Además, no es una buena práctica convertir un campo en anulable si su nombre coincide con el nombre de la tabla. Supongamos que tiene los valores 'Banana', 'Apple', NULL, 'Pears' en el fruitcampo. Esto no contará todas las filas, solo producirá 3, no 4

SELECT count(fruit) FROM fruit

Aunque algunos RDBMS siguen ese tipo de principio (para contar las filas de la tabla, aceptan el nombre de la tabla como parámetro de COUNT), esto funcionará en Postgresql (si no hay ningún subordinatecampo en ninguna de las dos tablas siguientes, es decir, siempre que no haya conflicto de nombre entre el nombre del campo y el nombre de la tabla):

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 subordinatecampo en la tabla, ya que contará el campo (que podría aceptar valores NULL), no las filas de la tabla.

Entonces, para estar 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 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 combinaciones, ese truco no funcionará en consultas de varias tablas sin que su semántica se confunda y, en particular, no puedes 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 COUNT(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

Si lo piensa detenidamente, puede inferir que COUNT(1)es lo mismo que COUNT(*), independientemente del tipo de unión. Pero para obtener el resultado LEFT JOIN, no podemos moldearlo COUNT(1)para que funcione como COUNT(subordinate.boss_id):COUNT(subordinate.*)

Entonces 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 quieres contar 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 parecida a la inglesa (simplemente no haga una columna con el mismo nombre que el nombre de la 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

Puede hacer esto, pero 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
Michael Buen avatar Apr 26 '2010 01:04 Michael Buen

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

Suponiendo que pkes una clave principal y que no se permiten valores nulos en los valores, entonces

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

Sin embargo, si pkno está obligado a ser no nulo, se 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_duplicatedcuando tiene la NOT NULLcláusula.

Normalmente escribo COUNT(*); es la notación original recomendada para SQL. De manera similar, con la EXISTScláusula, normalmente escribo WHERE EXISTS(SELECT * FROM ...)porque esa era la notación recomendada original. Las alternativas no deberían generar ningún beneficio; el optimizador debería ver a través de las notaciones más oscuras.

Jonathan Leffler avatar Apr 26 '2010 01:04 Jonathan Leffler

Preguntado y respondido 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 y ofrece el mismo plan. Una PK es única y no nula (al menos en SQL Server), por lo que COUNT(PK)= COUNT(*)

Este es un mito similar EXISTS (SELECT * ...oEXISTS (SELECT 1 ...

Y consulte la especificación ANSI 92 , 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.
gbn avatar Apr 27 '2010 19:04 gbn

Siento que las características de rendimiento cambian de un DBMS a otro. Todo depende de cómo elijan implementarlo. Como he trabajado extensamente en Oracle, lo contaré desde esa perspectiva.

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

COUNT(1)- No recuperará ninguna fila; en su lugar, se llama al recuento con un valor constante de 1 para cada fila de la tabla cuando coincide WHERE.

COUNT(PK)- La PK en Oracle está indexada. Esto significa que Oracle tiene que leer sólo el índice. Normalmente, una fila en el árbol del índice B+ es muchas veces más pequeña que la fila real. Entonces, considerando la tasa de IOPS del disco, Oracle puede recuperar muchas veces más filas del índice con una sola transferencia en bloque en comparación con una fila completa. Esto conduce a un mayor rendimiento de la consulta.

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

arunmur avatar Apr 26 '2010 03:04 arunmur