Wie nehme ich eine effiziente einfache Zufallsstichprobe in SQL? Auf der betreffenden Datenbank wird MySQL ausgeführt. Meine Tabelle besteht aus mindestens 200.000 Zeilen, und ich möchte eine einfache Zufallsstichprobe von etwa 10.000.
Die "offensichtliche" Antwort lautet:
SELECT * FROM table ORDER BY RAND() LIMIT 10000
Für große Tabellen ist das zu langsam: Es ruft RAND () für jede Zeile auf (wodurch es bereits auf O (n) gesetzt wird) und sortiert sie, sodass es bestenfalls O (n lg n) ist. Gibt es eine Möglichkeit, dies schneller als O (n) zu tun?
Hinweis : Wie Andrew Mao in den Kommentaren ausführt, sollten Sie bei Verwendung dieses Ansatzes unter SQL Server die T-SQL-Funktion NEWID () verwenden, da RAND () möglicherweise für alle Zeilen denselben Wert zurückgibt .
EDIT: 5 JAHRE SPÄTER
Ich bin mit einer größeren Tabelle erneut auf dieses Problem gestoßen und habe schließlich eine Version der Lösung von @ ignorant mit zwei Verbesserungen verwendet:
- Probieren Sie die Zeilen auf das 2-5-fache meiner gewünschten Stichprobengröße aus, um günstig nach Rang zu bestellen ()
- Speichern Sie das Ergebnis von RAND () bei jeder Einfügung / Aktualisierung in einer indizierten Spalte. (Wenn Ihr Datensatz nicht sehr aktualisierungsintensiv ist, müssen Sie möglicherweise einen anderen Weg finden, um diese Spalte aktuell zu halten.)
Um ein 1000-Elemente-Beispiel einer Tabelle zu entnehmen, zähle ich die Zeilen und probiere das Ergebnis mit der Spalte Frozen_Rand auf durchschnittlich 10.000 Zeilen aus:
SELECT COUNT(*) FROM table; -- Use this to determine rand_low and rand_high
SELECT *
FROM table
WHERE frozen_rand BETWEEN %(rand_low)s AND %(rand_high)s
ORDER BY RAND() LIMIT 1000
(Meine eigentliche Implementierung erfordert mehr Arbeit, um sicherzustellen, dass ich nicht unterabtastet, und um rand_high manuell herumzuwickeln, aber die Grundidee ist, "Ihr N zufällig auf einige Tausend zu reduzieren".)
Dies bringt zwar einige Opfer, ermöglicht es mir jedoch, die Datenbank mithilfe eines Index-Scans herunterzufahren, bis sie klein genug ist, um erneut nach Rang () zu bestellen.
RAND()
bei jedem nachfolgenden Aufruf derselbe Wert zurückgegeben wird.