PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Maneira rápida de descobrir a contagem de linhas de uma tabela no PostgreSQL


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 último VACUUM ou ANALYZE 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 de relpages então reltuples é 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 por autovacuum ).

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.