Manière rapide de découvrir le nombre de lignes d'une table dans PostgreSQL

j'ai besoin de savoir le nombre de lignes dans un tableau pour calculer un pourcentage. Si le nombre total est supérieur à une constante prédéfinie, j'utiliserai la valeur constante. Sinon, j'utiliserai 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 lignes dans ma table, compter toutes les lignes fera perdre beaucoup de temps.

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

j'ai besoin du nombre exact de lignes seulement tant qu'il est en dessous de la limite donnée. Sinon, si le nombre est supérieur à la limite, j'utilise la valeur limite à la place et je veux la réponse aussi vite que possible.

quelque chose comme ça:

SELECT text,count(*), percentual_calculus()  
FROM token  
GROUP BY text  
ORDER BY count DESC;
66
demandé sur Erwin Brandstetter 2011-10-30 07:58:24

7 réponses

compter les lignes dans gros tables est connu pour être lent dans PostgreSQL. Pour obtenir un nombre précis, il faut faire un compte complet des rangées en raison de la nature de MVCC . Il existe un moyen de accélérer de façon spectaculaire si le comte n' pas être exact comme cela semble être votre cas.

au lieu d'obtenir le exact count ( lent 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 si vous courez ANALYZE assez. Il est généralement très proche.

Voir le PostgreSQL wiki FAQ .

Ou la page wiki dédiée à la performance de count (*) .

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 tenir compte de cela:

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 Vite, plus simple, plus sûr, plus élégant. Voir le manuel sur types D'identificateur D'objet .

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



TABLESAMPLE SYSTEM (n) en Postgres 9.5 +

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

comme @a_horse a commenté , la nouvelle clause ajoutée pour la commande SELECT pourrait être utile si les statistiques dans pg_class ne sont pas assez à jour pour une raison quelconque. Par exemple:

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

cela ne regarde qu'une sélection aléatoire n % ( 1 dans l'exemple) sélection de blocs et compte les lignes dans elle. Un échantillon plus grand augmente le coût et réduit l'erreur, votre choix. L'exactitude dépend de plus de facteurs:

  • la Distribution de la taille de la ligne. Si un bloc donné tient des rangées plus larges que d'habitude, le nombre est plus bas que d'habitude, etc.
  • tuples morts ou un FILLFACTOR occupe un espace par bloc. Si inégalement réparties à travers le tableau, l'estimation peut être désactivé.
  • Général erreurs d'arrondi.

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

la Réponse à la question

tout d'abord, je dois savoir le nombre de lignes dans ce tableau, si le total le nombre est supérieur à certains prédéfinis constant,

et s'il ...

... est possible au moment où le comte passer ma valeur constante, il arrêter le comptage (et ne pas attendre de finir le dépouillement d'informer le le nombre de rangs est plus élevé).

Oui. vous pouvez utiliser un avec LIMIT :

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

Postgres cesse effectivement de compter au-delà de la limite donnée, vous obtenez un exact et actuel compte jusqu'à n lignes (500000 dans l'exemple), et n autrement. Mais pas aussi vite que l'estimation de pg_class .

149
répondu Erwin Brandstetter 2017-05-23 12:03:02

j'ai fait ceci une fois dans une application postgres en lançant:

EXPLAIN SELECT * FROM foo;

examine ensuite la sortie avec un regex, ou une logique similaire. Pour un simple SELECT *, la première ligne de sortie devrait ressembler à quelque chose comme 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 qui seraient retournées, alors ne faire le SELECT COUNT(*) réel si l'estimation est, disons, moins de 1,5 x votre seuil (ou quel que soit le nombre vous jugez logique pour votre application).

Selon la complexité de votre question, ce nombre peut devenir de moins en moins précis. En fait, dans ma demande, comme nous avons ajouté des jointures et des conditions complexes, il est devenu tellement inexact qu'il était complètement inutile, même pour savoir en une puissance de 100 combien de rangs nous aurions retourné, donc nous avons dû abandonner cette stratégie.

mais si votre requête est assez simple que Pg peut prédire dans un certain raisonnable marge d'erreur combien de lignes il va retourner, il peut fonctionner pour vous.

8
répondu Flimzy 2011-10-30 04:19:02

Dans Oracle, vous pouvez utiliser rownum pour limiter le nombre de lignes retournées. Je devine similaire construction existe dans d'autres SQLs. Ainsi, pour l'exemple que vous avez donné, vous pouvez limiter le nombre de lignes renvoyées à 500001 et appliquer un count(*) puis:

SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)
1
répondu Ritesh 2011-10-30 15:59:25

vous pouvez obtenir le nombre par la requête ci-dessous (sans * ou noms de colonne).

select from table_name;
1
répondu Vikram S 2018-08-17 09:53:43

Quelle est la largeur de la colonne de texte?

avec un groupe par il n'y a pas beaucoup que vous pouvez faire pour éviter un balayage de données (au moins un balayage d'index).

je vous recommande:

  1. si possible, changer le schéma pour supprimer la duplication des données de texte. De cette façon, le comptage se fera sur un champ étroit de clé étrangère dans la table 'many'.

  2. alternativement, la création d'un produit généré colonne avec un hachage du texte, puis groupe par la colonne de hachage. Encore une fois, il s'agit de diminuer la charge de travail (balayage à travers un indice de colonne étroite)

Edit:

votre question originale ne correspondait pas tout à fait à votre édition. Je ne suis pas sûr si vous êtes conscient que le compte, lorsqu'il est utilisé avec un groupe par, retournera le nombre d'articles par groupe et non le nombre d'articles dans la table entière.

0
répondu Chris Bednarski 2011-10-30 05:28:20

pour SQL Server (2005 ou au-dessus) une méthode rapide et fiable est:

SELECT SUM (row_count)
FROM sys.dm_db_partition_stats
WHERE object_id=OBJECT_ID('MyTableName')   
AND (index_id=0 or index_id=1);

détails à propos de sys.dm_db_partitional_stats are explained in MSDN "151970920

la requête ajoute des lignes de toutes les parties d'une table (éventuellement) partitionnée.

index_id=0 est une table non ordonnée (Heap) et index_id=1 est une table ordonnée (clustered index)

encore plus rapide (mais les méthodes sont détaillées ici.

0
répondu DrKoch 2014-10-29 08:45:56

référence tirée de ce Blog.

vous pouvez utiliser ci-dessous pour rechercher le nombre de lignes.

utilisant pg_class:

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

utilisant pg_stat_user_tables:

SELECT 
    schemaname
    ,relname
    ,n_live_tup AS EstimatedCount 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC;
0
répondu Anvesh 2016-02-13 07:58:00