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
ouANALYZE
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 derelpages
puisreltuples
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 parautovacuum
).
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.