Ich möchte eine zufällige Auswahl von Zeilen in PostgreSQL, ich habe dies versucht:
select * from table where random() < 0.01;
Aber einige andere empfehlen dies:
select * from table order by random() limit 1000;
Ich habe eine sehr große Tabelle mit 500 Millionen Zeilen, ich möchte, dass sie schnell ist.
Welcher Ansatz ist besser? Was sind die Unterschiede? Was ist der beste Weg, um zufällige Zeilen auszuwählen?
sql
performance
postgresql
random
nanounanue
quelle
quelle
Antworten:
Angesichts Ihrer Spezifikationen (plus zusätzliche Informationen in den Kommentaren),
Die folgende Abfrage erfordert keinen sequentiellen Scan der großen Tabelle, sondern nur einen Index-Scan.
Erhalten Sie zunächst Schätzungen für die Hauptabfrage:
Der einzig möglicherweise teure Teil ist der
count(*)
(für große Tische). Angesichts der oben genannten Spezifikationen benötigen Sie es nicht. Ein Kostenvoranschlag reicht völlig aus und ist fast kostenlos erhältlich ( ausführliche Erklärung hier ):Solange
ct
nicht viel kleiner alsid_span
, wird die Abfrage andere Ansätze übertreffen.Generieren Sie Zufallszahlen im
id
Raum. Sie haben "wenige Lücken", also addieren Sie 10% (genug, um die Leerzeichen leicht zu bedecken) zur Anzahl der abzurufenden Zeilen.Jeder
id
kann mehrmals zufällig ausgewählt werden (obwohl dies mit einem großen ID-Bereich sehr unwahrscheinlich ist). Gruppieren Sie daher die generierten Zahlen (oder verwenden Sie sieDISTINCT
).Verbinden Sie die
id
s mit dem großen Tisch. Dies sollte sehr schnell sein, wenn der Index vorhanden ist.Zum Schluss Überschüsse
id
abschneiden, die nicht von Betrügern und Lücken gefressen wurden. Jede Reihe hat die gleiche Chance, ausgewählt zu werden.Kurzfassung
Sie können diese Abfrage vereinfachen . Der CTE in der obigen Abfrage dient nur zu Bildungszwecken:
Mit rCTE verfeinern
Vor allem, wenn Sie sich bei Lücken und Schätzungen nicht so sicher sind.
Wir können mit einem kleineren Überschuss in der Basisabfrage arbeiten. Wenn zu viele Lücken vorhanden sind, sodass in der ersten Iteration nicht genügend Zeilen gefunden werden, iteriert der rCTE weiterhin mit dem rekursiven Term. Wir brauchen noch relativ wenige Lücken im ID-Bereich, oder die Rekursion läuft möglicherweise trocken, bevor das Limit erreicht ist - oder wir müssen mit einem ausreichend großen Puffer beginnen, der dem Zweck der Leistungsoptimierung widerspricht.
Duplikate werden
UNION
im rCTE eliminiert.Das Äußere
LIMIT
lässt den CTE anhalten, sobald wir genügend Zeilen haben.Diese Abfrage wurde sorgfältig entworfen, um den verfügbaren Index zu verwenden, tatsächlich zufällige Zeilen zu generieren und nicht anzuhalten, bis wir das Limit erreicht haben (es sei denn, die Rekursion läuft trocken). Hier gibt es eine Reihe von Fallstricken, wenn Sie es neu schreiben.
In Funktion einwickeln
Bei wiederholter Verwendung mit unterschiedlichen Parametern:
Anruf:
Sie können dieses Generikum sogar für jede Tabelle verwenden: Nehmen Sie den Namen der PK-Spalte und der Tabelle als polymorphen Typ und verwenden Sie
EXECUTE
... Aber das geht über den Rahmen dieser Frage hinaus. Sehen:Mögliche Alternative
WENN Ihre Anforderungen identische Sätze für wiederholte Anrufe zulassen (und wir sprechen über wiederholte Anrufe), würde ich eine materialisierte Ansicht in Betracht ziehen . Führen Sie die obige Abfrage einmal aus und schreiben Sie das Ergebnis in eine Tabelle. Benutzer erhalten eine blitzschnelle Auswahl quasi zufällig. Aktualisieren Sie Ihre zufällige Auswahl in Intervallen oder Ereignissen Ihrer Wahl.
Postgres 9.5 stellt vor
TABLESAMPLE SYSTEM (n)
Wo
n
ist ein Prozentsatz. Das Handbuch:Meine kühne Betonung. Es ist sehr schnell , aber das Ergebnis ist nicht gerade zufällig . Das Handbuch noch einmal:
Die Anzahl der zurückgegebenen Zeilen kann stark variieren. In unserem Beispiel erhalten Sie ungefähr 1000 Zeilen:
Verbunden:
Oder installieren Sie das zusätzliche Modul tsm_system_rows , um die Anzahl der angeforderten Zeilen genau zu ermitteln (sofern genügend vorhanden sind) und die bequemere Syntax zu berücksichtigen :
Siehe Evans Antwort für Details.
Aber das ist immer noch nicht gerade zufällig.
quelle
JOIN bigtbl t
was für stehtJOIN bigtbl AS t
.t
ist ein Tabellenalias fürbigtbl
. Ihr Zweck ist es, die Syntax zu verkürzen, aber in diesem speziellen Fall wäre sie nicht erforderlich. Ich habe die Abfrage in meiner Antwort vereinfacht und eine einfache Version hinzugefügt.Sie können den Ausführungsplan von beiden mithilfe von untersuchen und vergleichen
Ein schneller Test an einer großen Tabelle 1 zeigt, dass
ORDER BY
zuerst die gesamte Tabelle sortiert und dann die ersten 1000 Elemente ausgewählt werden. Beim Sortieren einer großen Tabelle wird nicht nur diese Tabelle gelesen, sondern auch temporäre Dateien gelesen und geschrieben. Daswhere random() < 0.1
einzige scannt die gesamte Tabelle nur einmal.Bei großen Tabellen ist dies möglicherweise nicht das, was Sie möchten, da selbst ein vollständiger Tabellenscan zu lange dauern kann.
Ein dritter Vorschlag wäre
Dieser stoppt den Tabellenscan, sobald 1000 Zeilen gefunden wurden, und kehrt daher früher zurück. Natürlich blockiert dies die Zufälligkeit ein wenig, aber vielleicht ist dies in Ihrem Fall gut genug.
Bearbeiten: Neben diesen Überlegungen können Sie auch die bereits gestellten Fragen dazu überprüfen. Verwenden der Abfrage
[postgresql] random
liefert einige Treffer.Und ein verlinkter Artikel von depez, der mehrere weitere Ansätze beschreibt:
1 "groß" wie in "Die vollständige Tabelle passt nicht in den Speicher".
quelle
random() < 0.02
Liste dann mischen und dann mischenlimit 1000
! Die Sortierung ist in einigen tausend Zeilen (lol) günstiger.postgresql Reihenfolge nach Zufall (), Zeilen in zufälliger Reihenfolge auswählen:
postgresql order by random () mit einem eindeutigen:
postgresql Reihenfolge nach dem Zufallsprinzip eine Zeile:
quelle
select your_columns from your_table ORDER BY random() limit 1
Nehmen Sie sich ~ 2 Minuten Zeit, um in 45-mil-Reihen zu arbeitenAb PostgreSQL 9.5 gibt es eine neue Syntax, mit der zufällige Elemente aus einer Tabelle abgerufen werden können:
In diesem Beispiel erhalten Sie 5% der Elemente aus
mytable
.Weitere Erklärungen finden Sie in diesem Blogbeitrag: http://www.postgresql.org/docs/current/static/sql-select.html
quelle
TABLESAMPLE SYSTEM_ROWS(400)
, um eine Stichprobe von 400 zufälligen Zeilen zu erhalten. Sie müssen die integriertetsm_system_rows
Erweiterung aktivieren , um diese Anweisung verwenden zu können.Der mit dem ORDER BY wird der langsamere sein.
select * from table where random() < 0.01;
geht Datensatz für Datensatz und entscheidet, ob er zufällig gefiltert wird oder nicht. Dies liegt daran,O(N)
dass jeder Datensatz nur einmal überprüft werden muss.select * from table order by random() limit 1000;
wird den gesamten Tisch sortieren und dann die ersten 1000 auswählen. Abgesehen von jeglicher Voodoo-Magie hinter den Kulissen ist die Reihenfolge nachO(N * log N)
.Der Nachteil
random() < 0.01
ist, dass Sie eine variable Anzahl von Ausgabedatensätzen erhalten.Beachten Sie, dass es einen besseren Weg gibt, einen Datensatz zu mischen, als nach dem Zufallsprinzip zu sortieren: Der Fisher-Yates-Shuffle , der ausgeführt wird
O(N)
. Das Implementieren des Shuffle in SQL klingt jedoch nach einer ziemlichen Herausforderung.quelle
Hier ist eine Entscheidung, die für mich funktioniert. Ich denke, es ist sehr einfach zu verstehen und auszuführen.
quelle
ORDER BY random()
sie funktioniert, ist aber möglicherweise nicht effizient, wenn Sie mit einem großen Tisch arbeiten.Wenn Sie wissen, wie viele Zeilen Sie möchten, überprüfen Sie
tsm_system_rows
.tsm_system_rows
Installieren Sie zuerst die Erweiterung
Dann Ihre Anfrage,
quelle
SYSTEM
Methode.tsm_system_rows
undtsm_system_time
Erweiterungen. Soweit ich sehen kann, sind sie für alles andere als eine absolut minimale Auswahl zufälliger Zeilen praktisch nutzlos . Ich wäre Ihnen dankbar, wenn Sie einen kurzen Blick auf die Gültigkeit oder das Gegenteil meiner Analyse werfen und diese kommentieren könnten.Wenn Sie nur eine Zeile möchten, können Sie eine berechnete
offset
aus verwendencount
.quelle
Eine Variation der von Erwin Brandstetter skizzierten materialisierten Ansicht "Mögliche Alternative" ist möglich.
Angenommen, Sie möchten keine Duplikate in den zurückgegebenen zufälligen Werten. Sie müssen also einen booleschen Wert für die Primärtabelle festlegen, der Ihren (nicht randomisierten) Wertesatz enthält.
Angenommen, dies ist die Eingabetabelle:
Füllen Sie die
ID_VALUES
Tabelle nach Bedarf. Erstellen Sie dann, wie von Erwin beschrieben, eine materialisierte Ansicht, die dieID_VALUES
Tabelle einmal randomisiert :Beachten Sie, dass die materialisierte Ansicht nicht die verwendete Spalte enthält, da diese schnell veraltet ist. Die Ansicht muss auch keine anderen Spalten enthalten, die sich möglicherweise in der
id_values
Tabelle befinden.Um (und „verbrauchen“) Zufallswerte zu erhalten, verwenden Sie eine UPDATE-RüCKFüHRUNG auf
id_values
, die Auswahlid_values
vonid_values_randomized
mit einer Verknüpfung, und die gewünschten Kriterien der Anwendung nur relevante Möglichkeiten zu erhalten. Zum Beispiel:Bei
LIMIT
Bedarf ändern - Wenn Sie jeweils nur einen zufälligen Wert benötigen, wechseln SieLIMIT
zu1
.id_values
Ich bin der Meinung, dass UPDATE-RETURNING mit den richtigen Indizes sehr schnell und mit wenig Last ausgeführt werden sollte. Es werden zufällige Werte mit einem Datenbank-Roundtrip zurückgegeben. Die Kriterien für "berechtigte" Zeilen können so komplex wie erforderlich sein. Neue Zeilen können jederzeit zurid_values
Tabelle hinzugefügt werden und sind für die Anwendung zugänglich, sobald die materialisierte Ansicht aktualisiert wird (die wahrscheinlich außerhalb der Spitzenzeiten ausgeführt werden kann). Das Erstellen und Aktualisieren der materialisierten Ansicht ist langsam, muss jedoch nur ausgeführt werden, wenn derid_values
Tabelle neue IDs hinzugefügt werden.quelle
Eine Lektion aus meiner Erfahrung:
offset floor(random() * N) limit 1
ist nicht schneller alsorder by random() limit 1
.Ich dachte, der
offset
Ansatz wäre schneller, weil er die Zeit für das Sortieren in Postgres sparen sollte. Es stellte sich heraus, dass es nicht war.quelle
Fügen Sie eine Spalte mit dem Namen
r
type hinzuserial
. Indexr
.Angenommen, wir haben 200.000 Zeilen, dann generieren wir eine Zufallszahl
n
, wobei 0n
<< = 200.000 .Wählen Sie Zeilen mit aus
r > n
, sortieren Sie sieASC
und wählen Sie die kleinste aus.Code:
Der Code ist selbsterklärend. Die Unterabfrage in der Mitte wird verwendet, um die Anzahl der Tabellenzeilen unter https://stackoverflow.com/a/7945274/1271094 schnell zu schätzen .
In der Anwendungsebene müssen Sie die Anweisung erneut ausführen, wenn
n
> die Anzahl der Zeilen oder mehrere Zeilen ausgewählt werden müssen.quelle
Ich weiß, dass ich etwas spät zur Party komme, aber ich habe gerade dieses großartige Tool namens pg_sample gefunden :
Ich habe dies mit einer 350M-Zeilendatenbank versucht und es war sehr schnell, ich weiß nichts über die Zufälligkeit .
quelle