PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Un moyen rapide de découvrir le nombre de lignes d'une table dans PostgreSQL

Le comptage des lignes dans les grandes tables est connu pour être lent dans PostgreSQL. Le modèle MVCC nécessite un nombre complet de lignes actives pour un nombre précis. Il existe des solutions de contournement pour accélérer considérablement si le décompte ne le fait pas doit être exact comme il semble être dans votre cas.

(Rappelez-vous que même un décompte "exact" est potentiellement mort à l'arrivée !)

Compte exact

Lent pour les grandes tables.
Avec des opérations d'écriture simultanées, il peut être obsolète au moment où vous l'obtenez.

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

Extrêmement rapide :

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

En règle générale, l'estimation est très proche. La proximité dépend de si ANALYZE ou VACUUM sont suffisamment exécutés - où "assez" est défini par le niveau d'activité d'écriture dans votre table.

Estimation plus sûre

Ce qui précède ignore la possibilité de plusieurs tables portant le même nom dans une base de données - dans différents schémas. 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';

Le cast en bigint formate le real chiffre bien, surtout pour les gros comptes.

Meilleure estimation

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 les types d'identificateurs d'objets.

Remplacez 'myschema.mytable'::regclass avec to_regclass('myschema.mytable') dans Postgres 9.4+ pour n'obtenir rien au lieu d'une exception pour les noms de table invalides. Voir :

  • Comment vérifier si une table existe dans un schéma donné

Meilleure estimation encore (pour très peu de coût supplémentaire)

Nous pouvons faire ce que fait le planificateur Postgres. Citant les exemples d'estimation de lignes dans le manuel :

Ces chiffres sont à jour au dernier VACUUM ou ANALYZE sur la table. Le planificateur récupère alors le nombre actuel réel de pages dans la table (il s'agit d'une opération bon marché, ne nécessitant pas de balayage de table). Si c'est différent de relpages puis reltuples est mis à l'échelle en conséquence pour arriver à une estimation actuelle du nombre de lignes.

Postgres utilise estimate_rel_size défini dans src/backend/utils/adt/plancat.c , qui couvre également le cas particulier de l'absence de données dans pg_class parce que la relation n'a jamais été aspirée. Nous pouvons faire quelque chose de similaire en SQL :

Forme minimale

SELECT (reltuples / relpages * (pg_relation_size(oid) / 8192))::bigint
FROM   pg_class
WHERE  oid = 'mytable'::regclass;  -- your table here

Sûr et explicite

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

Ne rompt pas avec les tables vides et les tables qui n'ont jamais vu VACUUM ou ANALYZE . Le manuel sur pg_class :

Si la table n'a encore jamais été aspirée ou analysée, reltuples contient -1 indiquant que le nombre de lignes est inconnu.

Si cette requête renvoie NULL , exécutez ANALYZE ou VACUUM pour la table et répétez. (Vous pouvez également estimer la largeur des lignes en fonction des types de colonnes comme le fait Postgres, mais c'est fastidieux et source d'erreurs.)

Si cette requête renvoie 0 , la table semble vide. Mais je voudrais ANALYZE assurer. (Et peut-être vérifier votre autovacuum paramètres.)

Typiquement, block_size est 8192. current_setting('block_size')::int couvre de rares exceptions.

Les qualifications de table et de schéma le rendent insensible à tout search_path et portée.

Dans tous les cas, la requête prend systématiquement < 0,1 ms pour moi.

Plus de ressources Web :

  • FAQ du wiki Postgres
  • Les pages wiki Postgres pour les estimations de comptage et les performances de comptage(*)

TABLESAMPLE SYSTEM (n) dans Postgres 9.5+

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

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

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

Cela ne regarde qu'un n aléatoire % (1 dans l'exemple) sélection 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 :

  • Répartition de la taille des lignes. Si un bloc donné contient des rangées plus larges que d'habitude, le nombre est inférieur à la normale, etc.
  • Tuples morts ou un FILLFACTOR occuper de l'espace par bloc. Si elle est inégalement répartie dans le tableau, l'estimation peut être faussée.
  • Erreurs d'arrondi générales.

Généralement, l'estimation de pg_class sera plus rapide et plus précis.

Réponse à la vraie question

Tout d'abord, j'ai besoin de connaître le nombre de lignes dans cette table, si le nombre total est supérieur à une constante prédéfinie,

Et si c'est ...

... est possible au moment où le comptage dépasse ma valeur constante, cela arrêtera le comptage (et n'attendra pas la fin du comptage pour informer que le nombre de lignes est supérieur).

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

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

Postgres arrête en fait de compter au-delà de la limite donnée, vous obtenez un exact et actuel compter jusqu'à n lignes (500000 dans l'exemple), et n autrement. Pas aussi rapide que l'estimation dans pg_class , cependant.