web-dev-qa-db-fra.com

Méthode rapide pour découvrir le nombre de lignes d'une table dans PostgreSQL

J'ai besoin de connaître le nombre de lignes dans une table pour calculer un pourcentage. Si le nombre total est supérieur à une constante prédéfinie, j'utiliserai la valeur constante. Sinon, je vais utiliser le nombre réel de lignes.

Je peux utiliser SELECT count(*) FROM table. Mais si ma valeur constante est 500 000 et que j'ai 5 000 000 000 lignes dans mon tableau, le comptage de toutes les lignes fera perdre beaucoup de temps.

Est-il possible d'arrêter de compter dès que ma valeur constante est dépassée?

J'ai besoin du nombre exact de lignes uniquement tant qu'il est inférieur à la limite donnée. Sinon, si le nombre dépasse la limite, j'utilise la valeur limite à la place et je veux la réponse aussi rapidement que possible.

Quelque chose comme ça:

SELECT text,count(*), percentual_calculus()  
FROM token  
GROUP BY text  
ORDER BY count DESC;
75
Renato Dinhani

Compter les lignes dans les tables big est connu pour être lent dans PostgreSQL. Pour obtenir un nombre précis, il doit effectuer un décompte complet de lignes en raison de la nature de MVCC . Il existe un moyen de accélérer considérablement cette opération si le compte n'est pas obligatoirement être exact comme cela semble être le cas dans votre cas.

Au lieu d'obtenir le décompte exact (slow avec de grandes tables):

SELECT count(*) AS exact_count FROM myschema.mytable;

Vous obtenez une estimation proche comme ceci (extrêmement rapide):

SELECT reltuples::bigint AS estimate FROM pg_class where relname='mytable';

La proximité de l'estimation dépend de si vous exécutez ANALYZE suffisant. C'est généralement très proche.
Voir le PostgreSQL Wiki FAQ .
Ou la page wiki dédiée au compte (*) performance .

Mieux encore

L'article dans le Wiki PostgreSQL est était un peu bâclé. Il a ignoré la possibilité qu'il puisse y avoir plusieurs tables du même nom dans une base de données, dans des schémas différents. Pour en tenir compte:

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'

Ou mieux encore

SELECT reltuples::bigint AS estimate
FROM   pg_class
WHERE  oid = 'myschema.mytable'::regclass;

Plus rapide, plus simple, plus sûr, plus élégant. Voir le manuel sur Types d'identificateurs d'objet .

Utilisez to_regclass('myschema.mytable') dans Postgres 9.4+ pour éviter les exceptions pour les noms de table invalides:


TABLESAMPLE SYSTEM (n) dans Postgres 9.5+

SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);

Comme dans le commentaire @a_horse commenté , la clause nouvellement ajoutée à la commande SELECT peut être utile si les statistiques dans pg_class ne sont pas suffisamment à jour pour une raison quelconque. Par exemple:

  • Non autovacuum en cours d'exécution.
  • Immédiatement après une grande INSERT ou DELETE.
  • tables TEMPORARY (qui ne sont pas couvertes par autovacuum).

Ceci ne fait que regarder n% (1 dans l'exemple) une sélection aléatoire de blocs et compte les lignes qu'il contient. Un échantillon plus grand augmente le coût et réduit l'erreur, votre choix. La précision dépend de plusieurs facteurs:

  • Distribution de la taille des lignes. Si un bloc donné contient plus de lignes que d'habitude, le nombre est inférieur à la normale, etc.
  • Les tuples morts ou une FILLFACTOR occupent un espace par bloc. Si la répartition est inégale sur le tableau, l'estimation peut être erronée.
  • Erreurs d'arrondissement générales.

Dans la plupart des cas, l'estimation à partir de pg_class sera plus rapide et plus précise.

Réponse à la question réelle

Premièrement, je dois connaître le nombre de lignes dans cette table, si le total le nombre est supérieur à une constante prédéfinie,

Et si ça ...

... est possible au moment où le compte passe ma valeur constante, il le fera arrêter le comptage (et ne pas attendre pour terminer le comptage pour informer le nombre de lignes est plus grand).

Oui. Vous pouvez utiliser une sous-requête avec LIMIT:

SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;

Postgres arrête réellement de compter au-delà de la limite donnée, vous obtenez un décompte exact et actuel pour un maximum de n lignes (500000 dans l'exemple) et n sinon. Pas aussi vite que l'estimation dans pg_class, cependant.

166
Erwin Brandstetter

Je l'ai fait une fois dans une application postgres en exécutant:

EXPLAIN SELECT * FROM foo;

Ensuite, examinez la sortie avec une expression rationnelle ou une logique similaire. Pour un simple SELECT *, la première ligne de sortie devrait ressembler à ceci:

Seq Scan on uids  (cost=0.00..1.21 rows=8 width=75)

Vous pouvez utiliser la valeur rows=(\d+) comme une estimation approximative du nombre de lignes à renvoyer. Vous ne pouvez alors utiliser la fonction SELECT COUNT(*) réelle que si l'estimation est inférieure à 1,5 fois votre seuil (ou le nombre que vous jugez utile pour votre application. ).

Selon la complexité de votre requête, ce nombre peut devenir de moins en moins précis. En fait, dans ma demande, au fur et à mesure que nous ajoutions des jointures et des conditions complexes, il devenait tellement imprécis que c'était totalement inutile, même savoir comment, sur une puissance de 100, combien de lignes nous aurions retournées, nous avons donc dû abandonner cette stratégie.

Mais si votre requête est suffisamment simple pour que Pg puisse prédire, avec une marge d'erreur raisonnable, le nombre de lignes renvoyées, cela fonctionnera peut-être pour vous.

9
Flimzy

Vous pouvez obtenir le nombre par la requête ci-dessous (sans * ni aucun nom de colonne). 

select from table_name;
1
SuperNova

Dans Oracle, vous pouvez utiliser rownum pour limiter le nombre de lignes renvoyées. J'imagine que des constructions similaires existent également dans d'autres instructions SQL. Ainsi, pour l'exemple que vous avez donné, vous pouvez limiter le nombre de lignes renvoyées à 500001 et appliquer un count(*) alors:

SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)
1
Ritesh

Quelle est la largeur de la colonne de texte?

Avec GROUP BY, vous ne pouvez pas faire grand chose pour éviter une analyse de données (au moins une analyse d'index).

Je recommanderais:

  1. Si possible, modifiez le schéma pour supprimer la duplication de données texte. De cette façon, le décompte se fera sur un champ de clé étrangère étroit dans la table 'Plusieurs'.

  2. Vous pouvez également créer une colonne générée avec un hachage du texte, puis GROUP BY de la colonne de hachage . Encore une fois, cela permet de réduire la charge de travail (analyse d'un index de colonne étroit)

Modifier: 

Votre question initiale ne correspond pas tout à fait à votre modification. Je ne sais pas si vous savez que COUNT, lorsqu'il est utilisé avec GROUP BY, renverra le nombre d'éléments par groupe et non le nombre d'éléments de la table entière.

0
Chris Bednarski

Référence tirée de ce blog.

Vous pouvez utiliser ci-dessous pour interroger le nombre de lignes. 

Utilisation de pg_class: 

 SELECT reltuples::bigint AS EstimatedCount
    FROM   pg_class
    WHERE  oid = 'public.TableName'::regclass;

Utilisation de pg_stat_user_tables:

SELECT 
    schemaname
    ,relname
    ,n_live_tup AS EstimatedCount 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC;
0
Anvesh