it-swarm.com.de

Schneller Weg, um die Zeilenanzahl einer Tabelle in PostgreSQL zu ermitteln

Ich muss die Anzahl der Zeilen in einer Tabelle kennen, um einen Prozentsatz zu berechnen. Wenn die Gesamtanzahl größer als eine vordefinierte Konstante ist, werde ich den konstanten Wert verwenden. Ansonsten werde ich die tatsächliche Anzahl der Zeilen verwenden.

Ich kann SELECT count(*) FROM table verwenden. Aber wenn mein konstanter Wert 500.000 ist und ich 5.000.000.000 Zeilen in meiner Tabelle habe, wird das Zählen aller Zeilen viel Zeit kosten.

Kann ich mit dem Zählen aufhören, sobald mein konstanter Wert überschritten wird?

Ich brauche nur die genaue Anzahl von Zeilen, solange sie unterhalb der angegebenen Grenze liegt. Wenn die Zählung über dem Grenzwert liegt, verwende ich ansonsten den Grenzwert und möchte die Antwort so schnell wie möglich.

Etwas wie das:

SELECT text,count(*), percentual_calculus()  
FROM token  
GROUP BY text  
ORDER BY count DESC;
75
Renato Dinhani

Das Zählen von Zeilen in big -Tabellen ist in PostgreSQL als langsam bekannt. Um eine genaue Anzahl zu erhalten, muss aufgrund der Beschaffenheit von MVCC eine vollständige Anzahl von Zeilen durchgeführt werden. Es gibt eine Möglichkeit, beschleunigt dies dramatisch, wenn der Zähler nichtgenau sein muss, wie es in Ihrem Fall zu sein scheint.

Anstelle von exact count (slow bei großen Tabellen):

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

Sie erhalten eine genaue Schätzung wie folgt (extrem schnell):

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

Wie nahe die Schätzung liegt, hängt davon ab, ob Sie ANALYZE genug ausführen. Es ist normalerweise sehr nahe.
Siehe das PostgreSQL Wiki FAQ .
Oder die dedizierte Wiki-Seite für die Leistung von count (*) .

Besser noch

Der Artikel im PostgreSQL-Wiki ist war etwas schlampig. Dabei wurde die Möglichkeit ignoriert, dass sich in einer Datenbank mehrere Tabellen mit demselben Namen befinden können - in verschiedenen Schemata. Um das zu berücksichtigen:

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'

Oder noch besser

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

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

Verwenden Sie to_regclass('myschema.mytable') in Postgres 9.4+, um Ausnahmen für ungültige Tabellennamen zu vermeiden:


TABLESAMPLE SYSTEM (n) in Postgres 9.5+

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

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

  • Keine autovacuum läuft.
  • Direkt nach einer großen INSERT oder DELETE.
  • TEMPORARY -Tabellen (die nicht von autovacuum abgedeckt werden).

Dies betrifft nur eine zufällige Auswahl von Blöcken aus n% (1 im Beispiel) und die Anzahl der darin enthaltenen Zeilen. Ein größeres Muster erhöht die Kosten und reduziert den Fehler. Die Genauigkeit hängt von mehreren Faktoren ab:

  • Verteilung der Zeilengröße Wenn ein bestimmter Block breiter als üblich ist, ist die Anzahl niedriger als üblich usw.
  • Tote Tupel oder eine FILLFACTOR belegen Platz pro Block. Bei ungleichmäßiger Verteilung in der Tabelle ist die Schätzung möglicherweise falsch.
  • Allgemeine Rundungsfehler.

In den meisten Fällen ist die Schätzung von pg_class schneller und genauer.

Antwort auf die eigentliche Frage

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

Und ob es ...

... ist in dem Moment möglich, in dem die Zählung meinen konstanten Wert passiert, wird es Stoppen Sie die Zählung (und warten Sie nicht, um die Zählung zu beenden, um die Zeilenzahl .__ zu informieren, die 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 auf, über die angegebene Grenze hinaus zu zählen, Sie erhalten einen genauen und aktuellen - Zähler für bis zu n Zeilen (im Beispiel 500000) und ansonsten n. Nicht annähernd so schnell wie die Schätzung in pg_class.

166

Ich habe dies einmal in einer Postgres-App gemacht, indem ich Folgendes ausgeführt habe:

EXPLAIN SELECT * FROM foo;

Untersuchen Sie dann die Ausgabe mit einem Regex oder einer ähnlichen Logik. Bei einem einfachen SELECT * sollte die erste Zeile der Ausgabe ungefähr so ​​aussehen:

Seq Scan on uids  (cost=0.00..1.21 rows=8 width=75)

Sie können den rows=(\d+)-Wert als eine grobe Schätzung der Anzahl der zurückgegebenen Zeilen verwenden. Führen Sie dann nur die tatsächliche SELECT COUNT(*) aus, wenn die Schätzung beispielsweise weniger als das 1,5-fache Ihres Schwellenwerts (oder die für Ihre Anwendung sinnvolle Anzahl) beträgt ).

Je nach Komplexität Ihrer Abfrage kann diese Zahl immer weniger genau werden. Tatsächlich wurde es in meiner Bewerbung, als wir Verknüpfungen und komplexe Bedingungen hinzufügten, so ungenau, dass es völlig wertlos war, sogar zu wissen, wie viele Zeilen wir innerhalb einer Potenz von 100 zurückgegeben hätten, also mussten wir diese Strategie aufgeben.

Wenn Ihre Abfrage jedoch so einfach ist, dass Pg innerhalb eines angemessenen Fehlerbereichs vorhersagen kann, wie viele Zeilen sie zurückgeben wird, kann dies für Sie funktionieren.

9
Flimzy

Sie können die Anzahl durch die folgende Abfrage (ohne * oder Spaltennamen) ermitteln. 

select from table_name;
1
SuperNova

In Oracle können Sie rownum verwenden, um die Anzahl der zurückgegebenen Zeilen zu begrenzen. Ich vermute, dass ein ähnliches Konstrukt auch in anderen SQL existiert. In dem von Ihnen angegebenen Beispiel können Sie die Anzahl der zurückgegebenen Zeilen auf 500001 beschränken und dann eine count(*) anwenden:

SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)
1
Ritesh

Wie breit ist die Textspalte?

Mit GROUP BY können Sie nicht viel tun, um einen Daten-Scan (zumindest einen Index-Scan) zu vermeiden.

Ich würde empfehlen:

  1. Ändern Sie nach Möglichkeit das Schema, um die Duplizierung von Textdaten zu entfernen. Auf diese Weise erfolgt die Zählung in einem engen Fremdschlüsselfeld in der Tabelle "Viele".

  2. Alternativ erstellen Sie eine generierte Spalte mit einem HASH des Texts und dann GROUP BY die Hash-Spalte . Dies wiederum verringert die Arbeitslast (Durchsuchen eines engen Spaltenindex).

Bearbeiten: 

Ihre ursprüngliche Frage stimmte nicht genau mit Ihrer Bearbeitung überein. Ich bin nicht sicher, ob Sie wissen, dass COUNT bei Verwendung mit GROUP BY die Anzahl der Elemente pro Gruppe und nicht die Anzahl der Elemente in der gesamten Tabelle zurückgibt.

0
Chris Bednarski

Referenz aus diesem Blog.

Sie können unten zum Abfragen verwenden, um die Zeilenanzahl zu ermitteln. 

Pg_class verwenden: 

 SELECT reltuples::bigint AS EstimatedCount
    FROM   pg_class
    WHERE  oid = 'public.TableName'::regclass;

Verwenden von pg_stat_user_tables:

SELECT 
    schemaname
    ,relname
    ,n_live_tup AS EstimatedCount 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC;
0
Anvesh