MySQL: maneira mais rápida de contar o número de linhas


117

Qual forma de contar várias linhas deve ser mais rápida no MySQL?

Este:

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

Ou a alternativa:

SELECT 1 FROM ... WHERE ...

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

Alguém poderia pensar que o primeiro método deveria ser mais rápido, já que este é claramente o território do banco de dados e o mecanismo de banco de dados deveria ser mais rápido do que qualquer outra pessoa ao determinar coisas como essa internamente.


1
Oh, encontrei uma pergunta semelhante ( stackoverflow.com/questions/1855226/… ). Mas então, eu uso SELECT 1e não SELECT *. Existe alguma diferença?
Franz de

não sei, mas é concebível que essas duas respostas sejam idênticas - o otimizador de consulta mysql pode fazer a mesma coisa em cada uma. que disse que o primeiro é menos ambíguo do que o último. por que você não escreve alguns benchmarks e os testa?
Jesse Cohen de

Uhm, vamos supor que estou tentando melhorar a visibilidade do mecanismo de pesquisa do SO fazendo uma pergunta semelhante com palavras diferentes;)
Franz

1
A diferença é a quantidade de dados enviados para o lado do PHP. Quanto mais colunas você tiver, mais lento será o SELECT * em relação ao SELECT 1, porque todas as colunas são recuperadas em vez de apenas o número 1. Quando você executa mysql_query(), por exemplo, todo o conjunto de resultados é enviado ao PHP a partir do MySQL, independentemente do que você fazer com esses dados.
toon81

Fazer uma pergunta como essa é uma ótima maneira de obter insights ou novas ideias, mas, no final das contas, se você realmente tiver um cenário específico em que deseja mais velocidade, terá que executar testes para ver qual é o mais rápido.
still_dreaming_1

Respostas:


124

Quando você COUNT(*)leva em índices de coluna de contagem, então será o melhor resultado. Mysql com mecanismo MyISAM realmente armazena contagem de linhas, ele não conta todas as linhas cada vez que você tenta contar todas as linhas. (com base na coluna da chave primária)

Usar o PHP para contar linhas não é muito inteligente, porque você tem que enviar dados do mysql para o php. Por que fazer isso quando você pode conseguir o mesmo no lado do mysql?

Se o COUNT(*)for lento, você deve executar EXPLAINa consulta e verificar se os índices são realmente usados ​​e onde devem ser adicionados.


O seguinte não é o caminho mais rápido , mas há um caso em COUNT(*)que realmente não se encaixa - quando você começa a agrupar os resultados, pode ter problemas, ondeCOUNT realmente não conta todas as linhas.

A solução é SQL_CALC_FOUND_ROWS. Isso geralmente é usado quando você está selecionando linhas, mas ainda precisa saber a contagem total de linhas (por exemplo, para paginação). Ao selecionar linhas de dados, basta anexar a SQL_CALC_FOUND_ROWSpalavra - chave após SELECT:

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

Depois de selecionar as linhas necessárias, você pode obter a contagem com esta única consulta:

SELECT FOUND_ROWS();

FOUND_ROWS() deve ser chamado imediatamente após a consulta de seleção de dados.


Em conclusão, tudo se resume a quantas entradas você tem e o que está na instrução WHERE. Você realmente deve prestar atenção em como os índices estão sendo usados, quando há muitas linhas (dezenas de milhares, milhões e mais).


14
Correção: MyISAMarmazena a contagem de linhas. Outros mecanismos de armazenamento InnoDB não armazenam contagens de linhas e contam todas as linhas a cada vez .
The Scrum Meister

1
Você sabe qual será o mais rápido quando quiser apenas saber se há uma linha: SELECT 1 FROM ... LIMIT 1ou SELECT COUNT(*) FROM ...?
Franz,

1
É provavelmente útil observar que se você precisar dos dados de qualquer maneira e quiser apenas uma contagem para paginação / etc. é mais eficiente obter os dados do que contar as linhas em seu programa.
Tyzoid

6
É irrelevante se o mecanismo armazena contagens de linhas. A pergunta afirma claramente que há uma WHEREcláusula.
Álvaro González

1
@Franz SELECT COUNT(*) FROM ...pode levar um tempo considerável, dependendo do que precisa ser verificado (por exemplo, uma tabela muito grande ou índice de milhões / bilhões / trilhões de linhas). SELECT 1 FROM ... LIMIT 1retorna imediatamente porque você o está limitando à primeira linha.
jbo5112

59

Depois de falar com meus companheiros de equipe, Ricardo nos disse que a maneira mais rápida é:

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

Mas é preciso lembrar que o resultado pode não ser exato.

Você também pode usá-lo na linha de comando:

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

Mais informações: http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html

E você pode encontrar uma discussão completa em mysqlperformanceblog


2
Para InnoDB, esta é uma aproximação.
Martin Tournoij

2
Isso é ótimo saber quando é necessário ter uma ideia aproximada do número de linhas em tabelas muito grandes onde count (*) pode literalmente levar horas!
Mark Hansen

Isso me salvou de arrancar todos os meus cabelos. COUNT (*) estava demorando muito para contar todas as 33 milhões de linhas ou mais em meu banco de dados. De qualquer forma, eu só queria saber se minha função de exclusão de linhas paralelizadas estava funcionando ou não. Eu não precisava de um número exato.
joemar.ct

1
+1 Usar o status da tabela em vez de "COUNT (*)" deve ser a resposta correta para esta pergunta, pois é sobre "mais rápido" e não "precisão".
lepe de

2
Usar SHOW TABLE STATUS(ou equivalente SELECTem information_schema) é rápido, mas não lida com uma WHEREcláusula. É preciso para MyISAM, mas impreciso (às vezes errado por um fator de 2) para InnoDB.
Rick James

29

Ótima pergunta, ótimas respostas. Esta é uma maneira rápida de repetir os resultados se alguém estiver lendo esta página e perder aquela parte:

$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$count = $num["id"];
echo("$count");

5
mysql_query é uma função obsoleta no PHP 5.5.0.
Omar Tariq

8
Porque não as count? idé confuso à primeira vista.
Orkhan Alikhanov,

Não responde à pergunta
mentalic

17

Esta consulta (que é semelhante ao que bayuah postou ) mostra um bom resumo de todas as contagens de tabelas dentro de um banco de dados: (versão simplificada do procedimento armazenado por Ivan Cachicatari que eu recomendo fortemente).

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'; 

Exemplo:

+-----------------+---------+
| Table Name      | Rows    |
+-----------------+---------+
| some_table      |   10278 |
| other_table     |     995 |

Isso me dá um resultado. Mas os resultados da contagem (1) e deste são diferentes. Dessa forma, sempre dá um número menor do que a consulta de contagem. Alguma ideia?
Ayyappan Sekar,

3
Apenas uma nota aos leitores. Este método é extremamente rápido, mas só é aplicável quando você pode trabalhar com um número aproximado de linhas, já que o valor armazenado em information_schemanão é o mesmo que aquele retornado por SELECT count(*) FROMcaso o InnoDB seja usado. Se você precisa de um valor estrito, tenha em mente que este método fornece um valor estrito somente com tabelas MyISAM. Com InnoDB, o número de linhas é uma aproximação aproximada.
Bartosz Firyn

13

Sempre entendi que o que segue me dará os tempos de resposta mais rápidos.

SELECT COUNT(1) FROM ... WHERE ...

1
SELECT 1 FROM ... WHERE ... não seria ainda mais rápido?
patrick

3
@patrick - SELECT 1 ...retornará quantas linhas WHEREe LIMITsolicitar, e todas serão "1".
Rick James

1
show table status like '<TABLE NAME>' Isso será muito mais rápido.
profundo em

@deep - mas não é relevante se você tiver uma WHEREcláusula. E, para InnoDB, é apenas uma estimativa.
Rick James

@RickJames yes true!
profundo

6

Se você precisar obter a contagem de todo o conjunto de resultados, poderá seguir a seguinte abordagem:

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

Normalmente, isso não é mais rápido do que usar, COUNTembora se possa pensar o contrário, porque está fazendo o cálculo internamente e não envia os dados de volta ao usuário, portanto, suspeita-se de uma melhoria de desempenho.

Fazer essas duas consultas é bom para paginação para obter totais, mas não particularmente para usar WHEREcláusulas.


Intersting. Isso funciona nos sistemas de banco de dados mais comuns? MySQL, Postgres, SQLite ...?
Franz

4
Na verdade, isso geralmente não é mais rápido do que usar COUNT (*). Consulte stackoverflow.com/questions/186588/…
toon81

2
Você deve ter MUITO cuidado ao usar esta função. Seu uso imprudente uma vez interrompeu todo o nosso ambiente de produção. É MUITO intensivo de recursos, então use com cuidado.
Janis Peisenieks

6

Fiz alguns benchmarks para comparar o tempo de execução de COUNT(*)vs COUNT(id)(id é a chave primária da tabela - indexada).

Número de testes: 10 * 1000 consultas

Resultados: COUNT(*) é mais rápido 7%

VER GRÁFICO: benchmarkgraph

Meu conselho é usar: SELECT COUNT(*) FROM table


1
Para sua informação, também há uma maneira comum de contar COUNT(1), seria interessante ver alguns benchmarks lá ...
Sliq

4

Experimente isto:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";

@lepe, sinto muito. Quero dizer, é muito bom se alguém que fez downvoting der alguma explicação por que ele / ela faz isso, para que todos possam aprender algo sobre isso.
bayuah

1
Isso lhe dará uma resposta aproximada rapidamente. Se você precisa de uma resposta exata, precisa executar select count(*) from table_nameou algo mais. dba.stackexchange.com/questions/151769/…
Programster

@Programster Obrigado. É melhor do que me deixar no escuro por quase um ano.
bayuah

1
@bayuah Não tenho certeza do que você quis dizer com seu último comentário. Só posso presumir que você pensa que fui eu quem votou contra sua resposta, o que não fui.
Programster

1
@Programster Não, desculpe, não foi isso que quis dizer. Eu quis dizer obrigado por sua explicação, para que eu possa conjeturar o que talvez Downvoter pensasse quando fez isso.
bayuah

3

Talvez você queira considerar fazer a SELECT max(Id) - min(Id) + 1. Isso só funcionará se seus IDs forem sequenciais e as linhas não forem excluídas. No entanto, é muito rápido.


3
Cuidado: às vezes, os servidores usam um valor de incremento automático> 1 (por motivos de backup), portanto, essa solução é boa, mas você deve verificar a configuração do banco de dados primeiro.
Alex

1

EXPLAIN SELECT id FROM ....fez o truque para mim. e pude ver o número de linhas sob a rowscoluna do resultado.


0

Lidei com tabelas para o governo alemão com às vezes 60 milhões de registros.

E precisávamos saber muitas vezes o total de linhas.

Assim, nós, programadores de banco de dados, decidimos que em cada tabela o registro um é sempre o registro no qual o número total de registros está armazenado. Atualizamos esse número, dependendo das linhas INSERT ou DELETE.

Tentamos de todas as outras maneiras. Esta é de longe a maneira mais rápida.


1
e quais são os detalhes de como você atualizou essa linha? O que significa um design defeituoso para uma mesa, onde todas as linhas exigiriam um int desperdiçado para vir junto.
Desenhou em

5
Sim, isso é realmente estúpido haha. Com cada consulta, você deve ignorar a primeira linha. Gostaria apenas de criar uma tabela de totais e preenchê-la com base em um gatilho. Tabela de usuários na inserção, atualização da tabela de totais. Tabela de usuários na exclusão, atualização da tabela de totais.
HTMLGuy

-1

Uma instrução count (*) com uma condição where na chave primária retornou a contagem de linhas muito mais rápido para mim, evitando a varredura completa da tabela.

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

Isso foi muito mais rápido para mim do que

SELECT COUNT(*) FROM ...
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.