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;
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 (*) .
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'
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:
autovacuum
läuft.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:
FILLFACTOR
belegen Platz pro Block. Bei ungleichmäßiger Verteilung in der Tabelle ist die Schätzung möglicherweise falsch.In den meisten Fällen ist die Schätzung von pg_class
schneller und genauer.
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
.
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.
Sie können die Anzahl durch die folgende Abfrage (ohne * oder Spaltennamen) ermitteln.
select from table_name;
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)
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:
Ä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".
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.
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;