Preciso saber o número de linhas em uma tabela para calcular uma porcentagem. Se a contagem total for maior do que alguma constante predefinida, usarei o valor da constante. Caso contrário, usarei o número real de linhas.
Eu posso usar SELECT count(*) FROM table
. Mas se meu valor constante for 500.000 e eu tiver 5.000.000.000 de linhas em minha tabela, contar todas as linhas levará muito tempo.
É possível parar de contar assim que meu valor constante for ultrapassado?
Preciso do número exato de linhas apenas enquanto estiver abaixo do limite fornecido. Caso contrário, se a contagem estiver acima do limite, uso o valor limite e quero a resposta o mais rápido possível.
Algo assim:
SELECT text,count(*), percentual_calculus()
FROM token
GROUP BY text
ORDER BY count DESC;
sql
postgresql
count
row
Renato Dinhani
fonte
fonte
Respostas:
A contagem de linhas em tabelas grandes é conhecida por ser lenta no PostgreSQL. Para obter um número preciso, é necessário fazer uma contagem completa de linhas devido à natureza do MVCC . Há uma maneira de acelerar isso drasticamente se a contagem não precisar ser exata como parece ser no seu caso.
Em vez de obter a contagem exata ( lento com tabelas grandes):
Você obtém uma estimativa aproximada como esta ( extremamente rápido ):
A proximidade da estimativa depende de você correr o
ANALYZE
suficiente. Geralmente é muito próximo.Veja o FAQ do PostgreSQL Wiki .
Ou a página wiki dedicada para contagem (*) de desempenho .
Melhor ainda
O artigo do PostgreSQL Wiki
éfoi um pouco desleixado . Ele ignorou a possibilidade de haver várias tabelas com o mesmo nome em um banco de dados - em esquemas diferentes. Para explicar isso:Ou melhor ainda
Mais rápido, simples, seguro e elegante. Veja o manual sobre Tipos de Identificadores de Objetos .
Use
to_regclass('myschema.mytable')
no Postgres 9.4+ para evitar exceções para nomes de tabela inválidos:TABLESAMPLE SYSTEM (n)
no Postgres 9.5+Como @a_horse comentou , a cláusula recém-adicionada para o
SELECT
comando pode ser útil se as estatísticas empg_class
não forem atualizadas o suficiente por algum motivo. Por exemplo:autovacuum
correr.INSERT
ouDELETE
.TEMPORARY
tabelas (que não são cobertas porautovacuum
).Isso só olha para uma seleção aleatória de n % (
1
no exemplo) de blocos e conta as linhas nela. Uma amostra maior aumenta o custo e reduz o erro, sua escolha. A precisão depende de mais fatores:FILLFACTOR
espaço de ocupação por bloco. Se distribuída de forma desigual pela tabela, a estimativa pode estar errada.Na maioria dos casos, a estimativa de
pg_class
será mais rápida e precisa.Resposta à pergunta real
E se isso ...
Sim. Você pode usar uma subconsulta com
LIMIT
:Na verdade, o Postgres para de contar além do limite fornecido, você obtém uma contagem exata e atual de até n linhas (500.000 no exemplo) e n caso contrário. Não tão rápido quanto a estimativa
pg_class
, no entanto.fonte
tablesample
cláusula: egselect count(*) * 100 as cnt from mytable tablesample system (1);
SELECT count(*) FROM (Select * from (SELECT 1 FROM token) query) LIMIT 500000) limited_query;
(Eu pergunto porque estou tentando obter uma contagem de uma consulta arbitrária que pode já ter uma cláusula de limite)ORDER BY something
while não pode usar um índice ou com funções de agregação). Além disso, apenas o número limitado de linhas da subconsulta é processado.Fiz isso uma vez em um aplicativo postgres executando:
Em seguida, examinar a saída com um regex ou lógica semelhante. Para um SELECT * simples, a primeira linha de saída deve ser semelhante a esta:
Você pode usar o
rows=(\d+)
valor como uma estimativa aproximada do número de linhas que seriam retornadas e, em seguida, faça o valor realSELECT COUNT(*)
se a estimativa for, digamos, menos de 1,5x o seu limite (ou qualquer número que você considere adequado para sua aplicação).Dependendo da complexidade da sua consulta, esse número pode se tornar cada vez menos preciso. Na verdade, em meu aplicativo, à medida que adicionamos junções e condições complexas, ele se tornou tão impreciso que era completamente inútil, mesmo saber quantas linhas teríamos retornado com uma potência de 100, então tivemos que abandonar essa estratégia.
Mas se sua consulta for simples o suficiente para que o Pg possa prever com alguma margem de erro razoável quantas linhas ela retornará, pode funcionar para você.
fonte
Referência retirada deste blog.
Você pode usar a seguir para consultar a contagem de linhas.
Usando pg_class:
Usando pg_stat_user_tables:
fonte
No Oracle, você pode usar
rownum
para limitar o número de linhas retornadas. Eu estou supondo que uma construção semelhante existe em outros SQLs também. Portanto, para o exemplo que você deu, você poderia limitar o número de linhas retornadas a 500001 e aplicar umcount(*)
então:fonte
count(*)
com rownum, 1 s sem o uso de rownum). Sim,SELECT count(*) cnt FROM table
sempre retornará 1 linha, mas com a condição LIMIT, retornará "500001" quando o tamanho da mesa for maior que 500000 e <tamanho> quando o tamanho da mesa for <= 500000.Qual é a largura da coluna de texto?
Com um GROUP BY, não há muito o que fazer para evitar uma varredura de dados (pelo menos uma varredura de índice).
Eu recomendo:
Se possível, altere o esquema para remover a duplicação de dados de texto. Desta forma, a contagem acontecerá em um campo estreito de chave estrangeira na tabela 'muitos'.
Como alternativa, crie uma coluna gerada com um HASH do texto e, em seguida, GROUP BY com a coluna hash. Novamente, isso é para diminuir a carga de trabalho (faça uma varredura em um índice de coluna estreito)
Editar:
Sua pergunta original não corresponde exatamente à sua edição. Não tenho certeza se você está ciente de que COUNT, quando usado com um GROUP BY, retornará a contagem de itens por grupo e não a contagem de itens em toda a tabela.
fonte
Você pode obter a contagem pela consulta abaixo (sem * ou quaisquer nomes de coluna).
fonte
count(*)
.Para SQL Server (2005 ou superior), um método rápido e confiável é:
Detalhes sobre sys.dm_db_partition_stats são explicados no MSDN
A consulta adiciona linhas de todas as partes de uma tabela particionada (possivelmente).
index_id = 0 é uma tabela não ordenada (Heap) e index_id = 1 é uma tabela ordenada (índice clusterizado)
Métodos ainda mais rápidos (mas não confiáveis) são detalhados aqui.
fonte