PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Schnelle Möglichkeit, die Zeilenanzahl einer Tabelle in PostgreSQL zu ermitteln

Das Zählen von Zeilen in großen Tabellen ist in PostgreSQL bekanntermaßen langsam. Das MVCC-Modell erfordert eine vollständige Anzahl aktiver Zeilen für eine genaue Zahl. Es gibt Problemumgehungen, um dies dramatisch zu beschleunigen wenn die Zählung nicht tut müssen exakt sein wie es in Ihrem Fall zu sein scheint.

(Denken Sie daran, dass selbst eine "genaue" Zählung möglicherweise bei der Ankunft tot ist!)

Genaue Anzahl

Langsam für große Tabellen.
Bei gleichzeitigen Schreibvorgängen kann es in dem Moment, in dem Sie es erhalten, veraltet sein.

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

Extrem schnell :

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

Typischerweise ist die Schätzung sehr nah. Wie nah, hängt davon ab, ob ANALYZE oder VACUUM genug ausgeführt werden - wobei "genug" durch das Niveau der Schreibaktivität in Ihrer Tabelle definiert wird.

Sicherere Schätzung

Das Obige ignoriert die Möglichkeit mehrerer Tabellen mit demselben Namen in einer Datenbank - in verschiedenen Schemas. Um das zu erklären:

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';

Die Umwandlung in bigint formatiert den real Nummer schön, besonders für große Zählungen.

Bessere Schätzung

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

Schneller, einfacher, sicherer, eleganter. Siehe das Handbuch zu Objektidentifikatortypen.

Ersetzen Sie 'myschema.mytable'::regclass mit to_regclass('myschema.mytable') in Postgres 9.4+, um nichts statt einer Ausnahme für ungültige Tabellennamen zu erhalten. Siehe:

  • Überprüfen, ob eine Tabelle in einem bestimmten Schema existiert

Noch bessere Schätzung (für sehr geringe zusätzliche Kosten)

Wir können das tun, was der Postgres-Planer tut. Zitieren der Zeilenschätzungsbeispiele im Handbuch:

Diese Nummern sind seit dem letzten VACUUM aktuell oder ANALYZE auf dem Tisch. Der Planer ruft dann die tatsächliche aktuelle Anzahl von Seiten in der Tabelle ab (dies ist eine billige Operation, die keinen Tabellenscan erfordert). Wenn sich das von relpages unterscheidet dann reltuples wird entsprechend skaliert, um zu einer aktuellen Schätzung der Zeilenanzahl zu gelangen.

Postgres verwendet estimate_rel_size definiert in src/backend/utils/adt/plancat.c , was auch den Sonderfall ohne Daten in pg_class abdeckt weil die Relation nie gesaugt wurde. Wir können etwas Ähnliches in SQL tun:

Minimalform

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

Sicher und eindeutig

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

Bricht nicht mit leeren Tabellen und Tabellen, die VACUUM noch nie gesehen haben oder ANALYZE . Das Handbuch zu pg_class :

Wenn die Tabelle noch nie geleert oder analysiert wurde, reltuples enthält -1 zeigt an, dass die Zeilenanzahl unbekannt ist.

Wenn diese Abfrage NULL zurückgibt , führen Sie ANALYZE aus oder VACUUM für den Tisch und wiederholen. (Alternativ könnten Sie die Zeilenbreite basierend auf Spaltentypen schätzen, wie es Postgres tut, aber das ist mühsam und fehleranfällig.)

Wenn diese Abfrage 0 zurückgibt , der Tisch scheint leer zu sein. Aber ich würde ANALYZE sichergehen. (Und vielleicht überprüfen Sie Ihr autovacuum Einstellungen.)

Typischerweise block_size ist 8192. current_setting('block_size')::int deckt seltene Ausnahmen ab.

Tabellen- und Schemaqualifikationen machen es immun gegen search_path und Geltungsbereich.

So oder so dauert die Abfrage bei mir durchgehend <0,1 ms.

Weitere Webressourcen:

  • Die Postgres-Wiki-FAQ
  • Die Postgres-Wiki-Seiten für Zählschätzungen und Zählleistung(*)

TABLESAMPLE SYSTEM (n) in Postgres 9.5+

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

Wie @a_horse kommentierte, die hinzugefügte Klausel für den SELECT Befehl kann nützlich sein, wenn Statistiken in pg_class sind aus irgendeinem Grund nicht aktuell genug sind. Zum Beispiel:

  • Kein autovacuum läuft.
  • Sofort nach einem großen INSERT / UPDATE / DELETE .
  • TEMPORARY Tabellen (die nicht von autovacuum abgedeckt werden ).

Dies betrachtet nur ein zufälliges n % (1 im Beispiel) Auswahl von Blöcken und zählt Zeilen darin. Eine größere Stichprobe erhöht die Kosten und reduziert den Fehler, Ihre Wahl. Die Genauigkeit hängt von mehreren Faktoren ab:

  • Verteilung der Zeilengröße. Wenn ein bestimmter Block breitere Zeilen als üblich enthält, ist die Anzahl niedriger als üblich usw.
  • Tote Tupel oder ein FILLFACTOR Platz pro Block einnehmen. Bei einer ungleichmäßigen Verteilung über die Tabelle kann die Schätzung falsch sein.
  • Allgemeine Rundungsfehler.

Typischerweise die Schätzung von pg_class wird schneller und genauer.

Antwort auf die eigentliche Frage

Zuerst muss ich die Anzahl der Zeilen in dieser Tabelle kennen, wenn die Gesamtzahl größer als eine vordefinierte Konstante ist,

Und ob es ...

... in dem Moment möglich ist, in dem die Zählung meinen konstanten Wert überschreitet, wird die Zählung angehalten (und nicht gewartet, bis die Zählung abgeschlossen ist, um mitzuteilen, dass die Zeilenanzahl größer ist).

Ja. Sie können eine Unterabfrage mit LIMIT verwenden :

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

Postgres hört tatsächlich auf zu zählen über das angegebene Limit hinaus erhalten Sie eine genaue und aktuelle bis zu n zählen Zeilen (im Beispiel 500000) und n ansonsten. Nicht annähernd so schnell wie die Schätzung in pg_class , obwohl.