A contagem de linhas em tabelas grandes é conhecida por ser lenta no PostgreSQL. O modelo MVCC requer uma contagem completa de linhas ativas para um número preciso. Existem soluções alternativas para acelerar drasticamente se a contagem não tem que ser exato como parece ser no seu caso.
(Lembre-se de que mesmo uma contagem "exata" está potencialmente morta na chegada!)
Contagem exata
Lento para tabelas grandes.
Com operações de gravação simultâneas, ele pode estar desatualizado no momento em que você o obtém.
SELECT count(*) AS exact_count FROM myschema.mytable;
Estimativa
Extremamente rápido :
SELECT reltuples AS estimate FROM pg_class where relname = 'mytable';
Normalmente, a estimativa é muito próxima. Quão perto, depende se
ANALYZE
ou VACUUM
são executados o suficiente - onde "suficiente" é definido pelo nível de atividade de gravação em sua tabela. Estimativa mais segura
O acima ignora a possibilidade de várias tabelas com o mesmo nome em um banco de dados - em esquemas diferentes. Para dar conta disso:
SELECT c.reltuples::bigint AS estimate
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'mytable'
AND n.nspname = 'myschema';
A conversão para
bigint
formata o real
número bem, especialmente para grandes contagens. Melhor estimativa
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'myschema.mytable'::regclass;
Mais rápido, mais simples, mais seguro, mais elegante. Consulte o manual sobre Tipos de identificador de objeto.
Substitua
'myschema.mytable'::regclass
com to_regclass('myschema.mytable')
no Postgres 9.4+ para não obter nada em vez de uma exceção para nomes de tabelas inválidos. Ver:- Como verificar se uma tabela existe em um determinado esquema
Melhor estimativa ainda (por muito pouco custo adicional)
Podemos fazer o que o planejador do Postgres faz. Citando os Exemplos de estimativa de linha no manual:
Esses números são atuais desde o últimoVACUUM
ouANALYZE
na mesa. O planejador então busca o número atual real de páginas na tabela (esta é uma operação barata, que não requer uma varredura na tabela). Se isso for diferente derelpages
entãoreltuples
é dimensionado de acordo para chegar a uma estimativa atual do número de linhas.
Postgres usa
estimate_rel_size
definido em src/backend/utils/adt/plancat.c
, que também cobre o caso de nenhum dado em pg_class
porque a relação nunca foi aspirada. Podemos fazer algo semelhante no SQL:Forma mínima
SELECT (reltuples / relpages * (pg_relation_size(oid) / 8192))::bigint
FROM pg_class
WHERE oid = 'mytable'::regclass; -- your table here
Seguro e explícito
SELECT (CASE WHEN c.reltuples < 0 THEN NULL -- never vacuumed
WHEN c.relpages = 0 THEN float8 '0' -- empty table
ELSE c.reltuples / c.relpages END
* (pg_relation_size(c.oid) / pg_catalog.current_setting('block_size')::int)
)::bigint
FROM pg_class c
WHERE c.oid = 'myschema.mytable'::regclass; -- schema-qualified table here
Não quebra com tabelas vazias e tabelas que nunca viram
VACUUM
ou ANALYZE
. O manual sobre pg_class
:
Se a tabela ainda não foi limpa ou analisada,reltuples
contém-1
indicando que a contagem de linhas é desconhecida.
Se esta consulta retornar
NULL
, execute ANALYZE
ou VACUUM
para a mesa e repita. (Como alternativa, você pode estimar a largura da linha com base nos tipos de coluna como o Postgres faz, mas isso é tedioso e propenso a erros.) Se esta consulta retornar
0
, a tabela parece estar vazia. Mas eu ANALYZE
para ter a certeza. (E talvez verifique seu autovacuum
definições.) Normalmente,
block_size
é 8192. current_setting('block_size')::int
abrange raras exceções. As qualificações de tabela e esquema o tornam imune a qualquer
search_path
e escopo. De qualquer forma, a consulta leva consistentemente <0,1 ms para mim.
Mais recursos da Web:
- Perguntas frequentes sobre o Postgres Wiki
- As páginas wiki do Postgres para estimativas de contagem e desempenho de contagem(*)
TABLESAMPLE SYSTEM (n)
no Postgres 9.5+
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
Como @a_horse comentou, a cláusula adicionada para o
SELECT
pode ser útil se as estatísticas em pg_class
não são atuais o suficiente por algum motivo. Por exemplo:- Sem
autovacuum
em execução. - Imediatamente após um grande
INSERT
/UPDATE
/DELETE
. TEMPORARY
tabelas (que não são cobertas porautovacuum
).
Isso só analisa um n aleatório % (
1
no exemplo) seleção de blocos e conta linhas nele. Uma amostra maior aumenta o custo e reduz o erro, sua escolha. A precisão depende de mais fatores:- Distribuição do tamanho da linha. Se um determinado bloco tiver linhas mais largas do que o normal, a contagem será menor do que o normal etc.
- Tuplas mortas ou um
FILLFACTOR
ocupar espaço por bloco. Se distribuído de forma desigual pela tabela, a estimativa pode estar errada. - Erros gerais de arredondamento.
Normalmente, a estimativa de
pg_class
será mais rápido e preciso. Resposta à pergunta real
Primeiro, preciso saber o número de linhas nessa tabela, se o totalcount for maior que alguma constante predefinida,
E se é...
... é possível no momento em que a contagem passar do meu valor constante, ela irá parar a contagem (e não esperar terminar a contagem para informar que a contagem de linhas é maior).
Sim. Você pode usar uma subconsulta com
LIMIT
:SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres na verdade para de contar além do limite determinado, você obtém um valor exato e atual contar até n linhas (500000 no exemplo) e n por outro lado. Não tão rápido quanto a estimativa em
pg_class
, no entanto.