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 vonautovacuum
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.