Le comptage des lignes dans les grandes tables est connu pour être lent dans PostgreSQL. Le site MVCC nécessite un comptage complet des lignes actives pour obtenir un nombre précis. Il existe des solutions de contournement pour accélérer considérablement le processus si le compte ne no doivent être exacto comme cela semble être le cas dans votre affaire.
(Rappelez-vous que même un comptage "exact" est potentiellement mort à l'arrivée sous une charge d'écriture simultanée).
Compte exact
Lent pour les grandes tables.
Avec les opérations d'écriture simultanées, elle peut être périmée 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 général, l'estimation est très proche. Cette proximité dépend du fait que ANALYZE
o VACUUM
sont exécutés suffisamment - où "suffisamment" est défini par le niveau d'activité d'écriture sur votre table.
Une estimation plus sûre
Ce qui précède ne tient pas compte de la possibilité d'avoir plusieurs tables portant le même nom dans une même 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';
Le casting pour bigint
met en forme le real
bien, surtout pour les grands nombres.
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 Types d'identificateurs d'objets .
Remplacer 'myschema.mytable'::regclass
con to_regclass('myschema.mytable')
dans Postgres 9.4+ pour ne rien obtenir au lieu d'une exception pour les noms de tables invalides. Voir :
Meilleure estimation encore (pour un coût supplémentaire très faible)
Nous pouvons faire ce que le planificateur Postgres fait. En citant le Exemples d'estimation de rangs dans le manuel :
Ces chiffres sont à jour au moment de la dernière VACUUM
o ANALYZE
sur le tableau. Le planificateur va ensuite chercher le nombre actuel de pages dans le tableau. la table (il s'agit d'une opération peu coûteuse, ne nécessitant pas de balayage de la table). Si est différent de relpages
puis reltuples
est mis à l'échelle en conséquence pour obtenir une estimation du nombre de rangs actuels.
Postgres utilise estimate_rel_size
défini dans src/backend/utils/adt/plancat.c
qui couvre également le cas de figure où il n'y a pas de données dans la base de données. 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_catalog.pg_relation_size(c.oid)
/ pg_catalog.current_setting('block_size')::int)
)::bigint
FROM pg_catalog.pg_class c
WHERE c.oid = 'myschema.mytable'::regclass; -- schema-qualified table here
Ne se brise pas avec les tables vides et les tables qui n'ont jamais vu VACUUM
o 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
, courir ANALYZE
o VACUUM
pour le tableau et répétez. (Vous pourriez aussi 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 être vide. Mais je voudrais ANALYZE
pour être sûr. (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 la table et du schéma l'immunisent contre toute search_path
et la portée.
Dans tous les cas, la requête prend systématiquement moins de 0,1 ms pour moi.
Plus de ressources Web :
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
Comme @a_horse a commenté la clause ajoutée pour les SELECT
peut être utile si les statistiques dans pg_class
ne sont pas assez courants pour une raison quelconque. Par exemple :
- Non
autovacuum
en cours d'exécution.
- Immédiatement après un grand
INSERT
/ UPDATE
/ DELETE
.
-
TEMPORARY
(qui ne sont pas couvertes par les autovacuum
).
Cela ne regarde qu'un échantillon aléatoire n % ( 1
dans l'exemple) sélection de blocs et compte des lignes dans celle-ci. Un échantillon plus grand augmente le coût et réduit l'erreur, à vous de choisir. La précision dépend de plus de facteurs :
- Distribution de la taille des rangs. Si un bloc donné contient des rangs plus larges que d'habitude, le nombre de rangs est plus faible que d'habitude, etc.
- Des tuples morts ou un
FILLFACTOR
occupent un espace par bloc. Si la répartition est inégale sur le tableau, l'estimation peut être erronée.
- Erreurs d'arrondi générales.
En général, l'estimation de pg_class
sera plus rapide et plus précis.
Réponse à la question actuelle
Tout d'abord, j'ai besoin de connaître le nombre de lignes dans ce tableau, si le total est supérieur à une constante prédéfinie,
Et que ce soit...
... est possible qu'au moment où le comptage passe ma valeur constante, il va arrêter le comptage (et ne pas attendre la fin du comptage pour informer le que le nombre de lignes est supérieur).
Sí. Vous pouvez utiliser un sous-requête avec LIMIT
:
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres arrête effectivement de compter au-delà de la limite donnée, vous obtenez un exact et actuel compter jusqu'à n rangs (500000 dans l'exemple), et n autrement. Pas aussi rapide que l'estimation dans pg_class
mais