MySQL: la forma más rápida de contar el número de filas

117

¿Qué forma de contar una cantidad de filas debería ser más rápida en MySQL?

Esta:

SELECT COUNT(*) FROM ... WHERE ...

O la alternativa:

SELECT 1 FROM ... WHERE ...

// and then count the results with a built-in function, e.g. in PHP mysql_num_rows()

Uno pensaría que el primer método debería ser más rápido, ya que este es claramente el territorio de la base de datos y el motor de la base de datos debería ser más rápido que cualquier otra persona al determinar cosas como esta internamente.

Franz
fuente
1
Oh, encontré una pregunta similar ( stackoverflow.com/questions/1855226/… ). Pero luego, uso SELECT 1y no SELECT *. ¿Hay una diferencia?
Franz
No lo sé, pero es concebible que estas dos respuestas sean idénticas: el optimizador de consultas mysql puede hacer lo mismo en cada una. Dicho esto, el primero es menos ambiguo que el segundo. ¿Por qué no escribes algunos puntos de referencia y los pruebas?
Jesse Cohen
Uhm, supongamos que estoy tratando de mejorar la visibilidad del motor de búsqueda de SO haciendo una pregunta similar en diferentes palabras;)
Franz
1
La diferencia es la cantidad de datos enviados al lado de PHP. Cuantas más columnas tenga, más lento SELECT * se vuelve relativo a SELECT 1, porque todas las columnas se recuperan en lugar de solo el número 1. Cuando ejecuta mysql_query(), por ejemplo, todo el conjunto de resultados se envía a PHP desde MySQL, independientemente de lo que hacer con esos datos.
toon81
Hacer una pregunta como esta es una excelente manera de obtener información o nuevas ideas, pero en última instancia, si realmente tiene un escenario específico en el que desea más velocidad, tendrá que ejecutar pruebas para ver cuál es la más rápida.
still_dreaming_1

Respuestas:

124

Cuando COUNT(*)tenga en cuenta los índices de columna, será el mejor resultado. Mysql con el motor MyISAM en realidad almacena el recuento de filas, no cuenta todas las filas cada vez que intenta contar todas las filas. (basado en la columna de la clave principal)

Usar PHP para contar filas no es muy inteligente, porque tienes que enviar datos de mysql a php. ¿Por qué hacerlo cuando puedes lograr lo mismo en el lado de mysql?

Si COUNT(*)es lento, debe ejecutar EXPLAINla consulta y verificar si los índices se usan realmente y dónde deben agregarse.


La siguiente no es la forma más rápida, pero hay un caso en el COUNT(*)que realmente no encaja: cuando comienza a agrupar los resultados, puede tener un problema, donde COUNTrealmente no cuenta todas las filas.

La solución es SQL_CALC_FOUND_ROWS. Esto generalmente se usa cuando está seleccionando filas pero aún necesita saber el recuento total de filas (por ejemplo, para la paginación). Cuando seleccione filas de datos, simplemente agregue la SQL_CALC_FOUND_ROWSpalabra clave después de SELECT:

SELECT SQL_CALC_FOUND_ROWS [needed fields or *] FROM table LIMIT 20 OFFSET 0;

Una vez que haya seleccionado las filas necesarias, puede obtener el recuento con esta única consulta:

SELECT FOUND_ROWS();

FOUND_ROWS() debe llamarse inmediatamente después de la consulta de selección de datos.


En conclusión, todo se reduce a cuántas entradas tiene y qué hay en la declaración WHERE. Realmente debería prestar atención a cómo se utilizan los índices, cuando hay muchas filas (decenas de miles, millones y más).

Mārtiņš Briedis
fuente
14
Corrección: MyISAMalmacena el recuento de filas. Otros motores de almacenamiento como InnoDB no almacenan recuentos de filas y contarán todas las filas cada vez .
The Scrum Meister
1
¿Sabes cuál será más rápido cuando simplemente quieres saber si hay una fila: SELECT 1 FROM ... LIMIT 1o SELECT COUNT(*) FROM ...?
Franz
1
Probablemente sea útil tener en cuenta que si necesita los datos de todos modos y solo desea un recuento para la paginación / etc. es más eficiente obtener los datos y luego contar las filas en su programa.
Tyzoid
6
Es irrelevante si el motor almacena recuentos de filas. La pregunta dice claramente que hay una WHEREcláusula.
Álvaro González
1
@Franz SELECT COUNT(*) FROM ...puede llevar un tiempo considerable, dependiendo de lo que se deba escanear (por ejemplo, una tabla muy grande o un índice de millones / miles de millones / billones de filas). SELECT 1 FROM ... LIMIT 1regresa inmediatamente porque lo está limitando a la primera fila.
jbo5112
59

Después de hablar con mis compañeros, Ricardo nos dijo que la forma más rápida es:

show table status like '<TABLE NAME>' \G

Pero debes recordar que el resultado puede no ser exacto.

También puede usarlo desde la línea de comando:

$ mysqlshow --status <DATABASE> <TABLE NAME>

Más información: http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html

Y puede encontrar una discusión completa en mysqlperformanceblog

MagMax
fuente
2
Para InnoDB, esto es una aproximación.
Martin Tournoij
2
Es bueno saber esto cuando se necesita una idea aproximada del número de filas en tablas muy grandes donde contar (*) puede llevar literalmente horas.
Mark Hansen
Esto me salvó de arrancarme los pelos. COUNT (*) estaba tardando años en contar las 33 millones más filas en mi base de datos. De todos modos, solo quería saber si mi función de eliminar filas en paralelo funcionaba o no. No necesitaba un número exacto.
joemar.ct
1
+1 Usar el estado de la tabla en lugar de "COUNT (*)" debería ser la respuesta correcta a esta pregunta, ya que se trata de "más rápido" y no de "precisión".
lepe
2
Usar SHOW TABLE STATUS(o su equivalente SELECTen information_schema) es rápido, pero no maneja una WHEREcláusula. Es preciso para MyISAM, pero impreciso (a veces con un factor de 2) para InnoDB.
Rick James
29

Gran pregunta, grandes respuestas. Aquí hay una forma rápida de hacer eco de los resultados si alguien está leyendo esta página y falta esa parte:

$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$count = $num["id"];
echo("$count");
Dan Horvat
fuente
5
mysql_query es una función obsoleta a partir de PHP 5.5.0.
Omar Tariq
8
¿Por qué no as count? ides confuso a primera vista.
Orkhan Alikhanov
No responde la pregunta
mentalic
17

Esta consulta (que es similar a la que publicó bayuah ) muestra un buen resumen del recuento de todas las tablas dentro de una base de datos: (versión simplificada del procedimiento almacenado de Ivan Cachicatari que recomiendo encarecidamente).

SELECT TABLE_NAME AS 'Table Name', TABLE_ROWS AS 'Rows' FROM information_schema.TABLES WHERE TABLES.TABLE_SCHEMA = '`YOURDBNAME`' AND TABLES.TABLE_TYPE = 'BASE TABLE'; 

Ejemplo:

+-----------------+---------+
| Table Name      | Rows    |
+-----------------+---------+
| some_table      |   10278 |
| other_table     |     995 |
lepe
fuente
Me da un resultado. Pero los resultados del recuento (1) y este son diferentes. De esta forma siempre se obtiene un número menor que la consulta de recuento. ¿Alguna idea?
Ayyappan Sekar
3
Solo una nota para los lectores. Este método es extremadamente rápido, pero solo es aplicable cuando puede trabajar con un número aproximado de filas, ya que el valor almacenado en information_schemano es el mismo que el devuelto por SELECT count(*) FROMen caso de que se use InnoDB. Si necesita un valor estricto, tenga en cuenta que este método proporciona un valor estricto solo con tablas MyISAM. Con InnoDB, el número de filas es una aproximación aproximada.
Bartosz Firyn
13

Siempre he entendido que lo siguiente me dará los tiempos de respuesta más rápidos.

SELECT COUNT(1) FROM ... WHERE ...
Adarshr
fuente
1
¿No sería SELECT 1 FROM ... WHERE ... incluso más rápido?
patrick
3
@patrick - SELECT 1 ...volverá tantas filas como el WHEREy LIMITpiden, y todos ellos serán "1".
Rick James
1
show table status like '<TABLE NAME>' Esto será mucho más rápido.
profundo
@deep - pero no es relevante si tiene una WHEREcláusula. Y, para InnoDB, es solo una estimación.
Rick James
@RickJames ¡sí, verdad!
profundo
6

Si necesita obtener el recuento de todo el conjunto de resultados, puede adoptar el siguiente enfoque:

SELECT SQL_CALC_FOUND_ROWS * FROM table_name LIMIT 5;
SELECT FOUND_ROWS();

Normalmente, esto no es más rápido que el uso, COUNTaunque uno podría pensar que es el caso contrario porque está haciendo el cálculo internamente y no envía los datos al usuario, por lo que se sospecha una mejora en el rendimiento.

Hacer estas dos consultas es bueno para la paginación para obtener totales, pero no particularmente para usar WHEREcláusulas.

Alex Rashkov
fuente
Interesante. ¿Funciona eso en los sistemas de bases de datos más comunes? MySQL, Postgres, SQLite ...?
Franz
4
En realidad, esto a menudo no es más rápido que usar COUNT (*) en absoluto. Ver stackoverflow.com/questions/186588/…
toon81
2
Debe tener MUCHO cuidado al utilizar esta función. Su uso imprudente una vez detuvo todo nuestro entorno de producción. Es MUY intensivo en recursos, así que utilícelo con cuidado.
Janis Peisenieks
6

Hice algunos puntos de referencia para comparar el tiempo de ejecución deCOUNT(*) vs COUNT(id)(id es la clave principal de la tabla, indexada).

Número de pruebas: 10 * 1000 consultas

Resultados: COUNT(*) es más rápido 7%

VER GRÁFICO: gráfico de referencia

Mi consejo es utilizar: SELECT COUNT(*) FROM table

SamuelCarreira
fuente
1
Para su información, también hay una forma común de contar COUNT(1), sería interesante ver algunos puntos de referencia allí ...
Sliq
4

Prueba esto:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";
Bayuah
fuente
@lepe lo siento. Quiero decir, es muy bueno si alguien que votó negativamente da una explicación de por qué hace eso, para que todos puedan aprender algo al respecto.
bayuah
1
Esto le dará una respuesta aproximada rápidamente. Si necesita una respuesta exacta, necesita realizar select count(*) from table_nameo algo más. dba.stackexchange.com/questions/151769/…
Programster
@Programster Gracias. Es mejor que dejarme en la oscuridad durante casi un año.
bayuah
1
@bayuah No estoy seguro de lo que quiso decir con su último comentario. Solo puedo asumir que piensas que soy yo quien rechazó tu respuesta, lo cual no es así.
Programster
1
@Programster No, lo siento, no quise decir eso. Quise decir gracias por su explicación, así que puedo conjeturar lo que quizás pensó Downvoter cuando hizo eso.
bayuah
3

Quizás desee considerar hacer un SELECT max(Id) - min(Id) + 1. Esto solo funcionará si sus ID son secuenciales y las filas no se eliminan. Sin embargo, es muy rápido.

sky-dev
fuente
3
Tenga cuidado: los servidores a veces usan un valor de incremento automático> 1 (por razones de respaldo), por lo que esta solución es buena, pero primero debe verificar la configuración de su base de datos.
Alex
1

EXPLAIN SELECT id FROM ....hizo el truco para mí. y pude ver el número de filas debajo de la rowscolumna del resultado.

ssrp
fuente
0

Manejé tablas para el gobierno alemán con a veces 60 millones de registros.

Y necesitábamos saber muchas veces el total de filas.

Entonces, los programadores de bases de datos decidimos que en cada tabla hay un registro siempre el registro en el que se almacena el número total de registros. Actualizamos este número, dependiendo de INSERT o DELETE filas.

Intentamos todas las demás formas. Esta es, con mucho, la forma más rápida.

Scoobeedo Cool
fuente
1
y ¿cuáles son los detalles de cómo actualizó esa fila? Lo que significa que hay un diseño defectuoso en una mesa, donde todas las filas requerirían un int desperdiciado para el viaje.
Drew
5
Sí, eso es realmente estúpido jaja. Con cada consulta, debe ignorar la primera fila. Simplemente crearía una tabla de totales y la completaría en función de un disparador. Tabla de usuarios al insertar, actualizar tabla de totales. Tabla de usuarios en eliminar, actualizar tabla de totales.
HTMLGuy
-1

Una declaración count (*) con una condición where en la clave principal devolvió el recuento de filas mucho más rápido para mí evitando el escaneo completo de la tabla.

SELECT COUNT(*) FROM ... WHERE <PRIMARY_KEY> IS NOT NULL;

Esto fue mucho más rápido para mí que

SELECT COUNT(*) FROM ...
ayakout
fuente