¿Debo CONTAR(*) o no?


Sé que generalmente es una mala idea hacer consultas como esta:

SELECT * FROM `group_relations`

Pero cuando solo quiero el conteo, debo ir para esta consulta ya que permite que la tabla cambie pero aún produce los mismos resultados.

SELECT COUNT(*) FROM `group_relations`

O el más specfic

SELECT COUNT(`group_id`) FROM `group_relations`

Tengo la sensación de que este último podría ser potencialmente más rápido, pero ¿hay otras cosas que considerar?

Update: Estoy usando InnoDB en este caso, lo siento por no ser más específico.

Author: grapefrukt, 2009-01-19

14 answers

Si la columna en cuestión NO es NULL, ambas consultas son equivalentes. Cuando group_id contiene valores null,

select count(*)

Contará todas las filas, mientras que

select count(group_id)

Solo contará las filas donde group_id no es null.

También, algunos sistemas de bases de datos, como MySQL emplean una optimización cuando se pide count(*) que hace que tales consultas sean un poco más rápidas que la específica.

Personalmente, cuando solo cuento, estoy contando ( * ) para estar en el lado seguro con el nulo.

 100
Author: pilif,
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
2009-03-09 14:59:09

Si lo recuerdo bien, en MYSQL COUNT(*) cuenta todas las filas, mientras que COUNT(column_name) cuenta solo las filas que tienen un valor no NULO en la columna dada.

 22
Author: Sebastian Dietz,
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
2009-01-19 11:10:50

COUNT(*) cuente todas las filas, mientras que COUNT(column_name) contará solo las filas sin valores NULOS en la columna especificada.

Importante tener en cuenta en MySQL:

COUNT() es muy rápido en tablas MyISAM para columnas * o no null, ya que el recuento de filas se almacena en caché. InnoDB no tiene almacenamiento en caché de recuento de filas, por lo que no hay diferencia en el rendimiento para COUNT(*) o COUNT(column_name), independientemente de si la columna puede ser nula o no. Puede leer más sobre las diferencias en este post en MySQL blog de performance.

 11
Author: Eran Galperin,
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
2009-01-19 11:32:24

Si intentas SELECT COUNT(1) FROM group_relations será un poco más rápido porque no intentará recuperar información de tus columnas.

Editar: Acabo de investigar un poco y descubrí que esto solo sucede en algunos db. En sqlserver es lo mismo usar 1 o *, pero en Oracle es más rápido usar 1.

Http://social.msdn.microsoft.com/forums/en-US/transactsql/thread/9367c580-087a-4fc1-bf88-91a51a4ee018/

Aparentemente no hay diferencia entre ellos en mysql, como sqlserver el analizador aparece para cambiar la consulta a select (1). Lo siento si te he engañado de alguna manera.

 8
Author: Sergio,
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
2009-01-19 13:40:00

Yo tenía curiosidad sobre esto. Está bien leer documentación y respuestas teóricas, pero me gusta equilibrarlas con evidencia empírica.

Tengo una tabla MySQL (InnoDB) que tiene 5.607.997 registros en ella. La tabla está en mi propio sandbox privado, así que sé que el contenido es estático y nadie más está usando el servidor. Creo que esto elimina efectivamente todos los efectos externos en el rendimiento. Tengo una tabla con un campo de Clave Primaria (Id) auto_increment que sé que nunca será null que usaré para mi prueba de cláusula where (WHERE Id NO ES NULL).

La única otra falla posible que veo en la ejecución de pruebas es la caché. La primera vez que se ejecuta una consulta siempre será más lenta que las consultas posteriores que utilizan los mismos índices. Me referiré a eso a continuación como la llamada de siembra de caché. Solo para mezclarlo un poco lo ejecuté con una cláusula where que sé que siempre evaluará a verdadero independientemente de cualquier dato (VERDADERO = VERDADERO).

Dicho esto, aquí están mis resultados:

Tipo de consulta

      |  w/o WHERE          | where id is not null |  where true=true

COUNT ()

      |  9 min 30.13 sec ++ | 6 min 16.68 sec ++   | 2 min 21.80 sec ++
      |  6 min 13.34 sec    | 1 min 36.02 sec      | 2 min 0.11 sec 
      |  6 min 10.06 se     | 1 min 33.47 sec      | 1 min 50.54 sec

COUNT(Id)

      |  5 min 59.87 sec    | 1 min 34.47 sec      | 2 min 3.96 sec 
      |  5 min 44.95 sec    | 1 min 13.09 sec      | 2 min 6.48 sec

COUNT (1)

      | 6 min 49.64 sec    | 2 min 0.80 sec       | 2 min 11.64 sec
      | 6 min 31.64 sec    | 1 min 41.19 sec      | 1 min 43.51 sec

++Esto se considera la llamada de siembra de caché. Se espera que sea más lento que el resto.

Yo diría que los resultados hablan por sí mismos. COUNT (Id) generalmente elimina a los demás. Agregar una cláusula Where reduce drásticamente el tiempo de acceso, incluso si es una cláusula que sabes que se evaluará como verdadera. El punto dulce parece ser COUNT (Id)... DONDE Id NO ES NULL.

Me encantaría ver otro resultados de las personas, tal vez con tablas más pequeñas o con cláusulas where contra campos diferentes al campo que estás contando. Estoy seguro de que hay otras variaciones que no he tenido en cuenta.

 5
Author: Chris,
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
2009-03-19 21:56:17

Buscar alternativas

Como has visto, cuando las tablas crecen, COUNT las consultas se vuelven lentas. Creo que lo más importante es considerar la naturaleza del problema que estás tratando de resolver. Por ejemplo, muchos desarrolladores utilizan consultas COUNT al generar paginación para grandes conjuntos de registros con el fin de determinar el número total de páginas en el conjunto de resultados.

Sabiendo que COUNT las consultas crecerán lentamente, podría considerar una forma alternativa de mostrar controles de paginación que simplemente le permite dejar de lado la consulta lenta. La paginación de Google es un excelente ejemplo.

Desnormalizar

Si es absolutamente necesario conocer el número de registros que coinciden con un recuento específico, considere la técnica clásica de desnormalización de datos. En lugar de contar el número de filas en el momento de la búsqueda, considere aumentar un contador en la inserción de registros y disminuir ese contador en la eliminación de registros.

Si decide hacer esto, considere usar idempotent, transactional operaciones para mantener sincronizados esos valores desnormalizados.

BEGIN TRANSACTION;
INSERT INTO  `group_relations` (`group_id`) VALUES (1);
UPDATE `group_relations_count` SET `count` = `count` + 1;
COMMIT;

Alternativamente, puede usar disparadores de base de datos si su RDBMS los admite.

Dependiendo de su arquitectura, podría tener sentido usar una capa de almacenamiento en caché como memcached para almacenar, incrementar y disminuir el valor desnormalizado, y simplemente pasar a la consulta de CONTEO lento cuando falta la clave de caché. Esto puede reducir la contención de escritura general si tiene datos muy volátiles, aunque en casos como este, desea considerar soluciones para el efecto pila de perros.

 4
Author: Duncan Beevers,
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
2009-07-08 06:40:54

Las tablas MySQL ISAM deben tener optimización para COUNT(*), omitiendo el análisis completo de la tabla.

 2
Author: dmajkic,
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
2009-01-19 11:39:31

Un asterisco en COUNT no tiene relación con asterisk para seleccionar todos los campos de la tabla. Es pura basura decir que COUNT(*) es más lento que COUNT(field)

Intuyo que seleccionar CONTEO(*) es más rápido que seleccionar CONTEO(campo). Si el RDBMS detecta que se especifica "*" en el RECUENTO en lugar de campo, no es necesario evaluar nada para incrementar el recuento. Mientras que si especifica campo en CONTEO, el RDBMS siempre evaluará si su campo es nulo o no contarlo.

Pero si su campo es nullable, especifique el campo en COUNT.

 2
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
2009-01-19 12:00:07

CUENTA ( * ) hechos y mitos:

MITO : "InnoDB no maneja bien las consultas count (*)":

La mayoría de las consultas count(*) se ejecutan de la misma manera por todos los motores de almacenamiento si tiene una cláusula WHERE, de lo contrario InnoDB tendrá que realizar un análisis completo de la tabla.

FACT: InnoDB no optimiza las consultas count(*) sin la cláusula where

 2
Author: Charles Faiga,
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
2009-03-09 20:56:11

Es mejor contar por una columna indexada como una clave primaria.

SELECT COUNT(`group_id`) FROM `group_relations`
 2
Author: pjanaway,
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
2009-06-22 22:49:55

Debe depender de lo que realmente estás tratando de lograr, como ya ha dicho Sebastian, es decir, ¡dejar claras tus intenciones! Si es simplemente contando las filas, entonces vaya por el CONTEO (*), o contando una sola columna vaya por el CONTEO(columna).

Podría valer la pena revisar su proveedor de bases de datos también. Cuando solía usar Informix tenía una optimización para COUNT (*) que tenía un costo de ejecución del plan de consulta de 1 en comparación con el conteo de columnas simples o múltiples que resultaría en una cifra superior

 1
Author: tddmonkey,
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
2009-01-19 11:14:51

Si intenta SELECCIONAR COUNT(1) DESDE group_relations será un poco más rápido porque no intentará recuperar información de sus columnas.

COUNT(1) solía ser más rápido que COUNT(*), pero eso ya no es cierto, ya que los DBM modernos son lo suficientemente inteligentes como para saber que no quieres saber sobre columnas

 1
Author: Rafael Mueller,
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
2009-01-19 11:44:07

El consejo que recibí de MySQL sobre cosas como esta es que, en general, tratar de optimizar una consulta basada en trucos como este puede ser una maldición a largo plazo. Hay ejemplos en la historia de MySQL donde la técnica de alto rendimiento de alguien que se basa en cómo funciona el optimizador termina siendo el cuello de botella en la próxima versión.

Escriba la consulta que responde a la pregunta que está haciendo if si desea un recuento de todas las filas, use COUNT(*). Si desea un recuento de columnas no nulas, use COUNT (col) DONDE col NO ES NULL. Indexe apropiadamente y deje la optimización al optimizador. Intentar hacer sus propias optimizaciones a nivel de consulta a veces puede hacer que el optimizador incorporado sea menos efectivo.

Dicho esto, hay cosas que puede hacer en una consulta para que sea más fácil para el optimizador acelerarlo, pero no creo que COUNT sea una de ellas.

Editar: Las estadísticas en la respuesta anterior son interesantes, sin embargo. No estoy seguro de si realmente hay algo en trabajar en el optimizador en este caso. Solo estoy hablando de optimizaciones a nivel de consulta en general.

 1
Author: Jon,
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
2009-03-27 19:28:37

Sé que generalmente es una mala idea hacer consultas como esta:

SELECT * FROM `group_relations`

Pero cuando solo quiero el conde, debería Voy para esta consulta ya que permite la tabla a cambiar pero sigue rindiendo los mismos resultados.

SELECT COUNT(*) FROM `group_relations`

Como su pregunta implica, la razón SELECT * no es aconsejable es que los cambios en la tabla podrían requerir cambios en su código. Eso no se aplica a COUNT(*). Es bastante raro querer el comportamiento especializado que SELECT COUNT('group_id') le da-típicamente quieres saber el número de registros. Para eso es COUNT(*), así que úsalo.

 0
Author: Carl Manaster,
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
2009-06-22 23:01:59