Ich habe eine SQL Server-Tabelle mit ungefähr 50.000 Zeilen. Ich möchte ungefähr 5.000 dieser Zeilen zufällig auswählen. Ich habe mir einen komplizierten Weg überlegt, eine temporäre Tabelle mit einer Spalte "Zufallszahl" zu erstellen, meine Tabelle in diese zu kopieren, die temporäre Tabelle zu durchlaufen und jede Zeile mit zu aktualisieren RAND()
und dann aus dieser Tabelle die Zufallszahlenspalte <auszuwählen 0,1. Ich suche nach einem einfacheren Weg, wenn möglich in einer einzigen Aussage.
In diesem Artikel wird die Verwendung der NEWID()
Funktion vorgeschlagen. Das sieht vielversprechend aus, aber ich kann nicht sehen, wie ich einen bestimmten Prozentsatz von Zeilen zuverlässig auswählen kann.
Hat das schon mal jemand gemacht? Irgendwelche Ideen?
quelle
Antworten:
Als Antwort auf den Kommentar "Pure Trash" zu großen Tabellen: Sie können dies so tun, um die Leistung zu verbessern.
Die Kosten hierfür sind der Schlüssel-Scan der Werte zuzüglich der Verbindungskosten, die für eine große Tabelle mit einer kleinen prozentualen Auswahl angemessen sein sollten.
quelle
[yourPk]
sich das? EDIT: Nvm, habe es herausgefunden ... Primärschlüssel. Durrrnewid()
Sortierschätzung, sind die E / A-Kosten sehr hoch und wirken sich auf die Leistung aus.Je nach Ihren Anforderungen
TABLESAMPLE
erhalten Sie eine nahezu ebenso zufällige und bessere Leistung. Dies ist auf MS SQL Server 2005 und höher verfügbar.TABLESAMPLE
gibt Daten von zufälligen Seiten anstelle von zufälligen Zeilen zurück und daher ruft deos nicht einmal Daten ab, die nicht zurückgegeben werden.Auf einem sehr großen Tisch habe ich getestet
dauerte mehr als 20 Minuten.
dauerte 2 Minuten.
Die Leistung wird sich auch bei kleineren Samples verbessern,
TABLESAMPLE
während dies bei nicht der Fall istnewid()
.Bitte denken Sie daran, dass dies nicht so zufällig ist wie die
newid()
Methode, aber Sie eine anständige Stichprobe erhalten.Siehe die MSDN-Seite .
quelle
newid () / order by funktioniert, ist jedoch für große Ergebnismengen sehr teuer, da für jede Zeile eine ID generiert und anschließend sortiert werden muss.
TABLESAMPLE () ist vom Standpunkt der Leistung aus gut, aber Sie erhalten eine Zusammenfassung der Ergebnisse (alle Zeilen auf einer Seite werden zurückgegeben).
Für eine bessere Zufallsstichprobe mit besserer Leistung ist es am besten, Zeilen zufällig herauszufiltern. Ich habe das folgende Codebeispiel im SQL Server Books Online-Artikel Einschränken von Ergebnismengen mithilfe von TABLESAMPLE gefunden :
Wenn ich gegen eine Tabelle mit 1.000.000 Zeilen laufe, sind hier meine Ergebnisse:
Wenn Sie mit TABLESAMPLE durchkommen, erhalten Sie die beste Leistung. Verwenden Sie andernfalls die Methode newid () / filter. newid () / order by sollte der letzte Ausweg sein, wenn Sie eine große Ergebnismenge haben.
quelle
NewID()
nur einmal ausgewertet wird, anstatt pro Zeile, was mir nicht gefällt ...Das zufällige Auswählen von Zeilen aus einer großen Tabelle in MSDN bietet eine einfache, gut artikulierte Lösung, die die großen Leistungsprobleme berücksichtigt.
quelle
RAND()
nicht für jede Zeile der gleiche Wert zurückgegeben wird (was dieBINARY_CHECKSUM()
Logik zunichte machen würde). Liegt es daran, dass es in einer anderen Funktion aufgerufen wird, anstatt Teil der SELECT-Klausel zu sein?rand()
oder eine Kombination der oben genannten sein - aber ich habe mich aus diesem Grund von dieser Lösung abgewandt. Auch die Anzahl der Ergebnisse variierte von 1 bis 5, so dass dies in einigen Szenarien möglicherweise auch nicht akzeptabel ist.RAND()
Gibt für jede Zeile den gleichen Wert zurück (weshalb diese Lösung schnell ist). Bei Zeilen mit binären Prüfsummen, die sehr nahe beieinander liegen, besteht jedoch ein hohes Risiko, dass ähnliche Prüfsummenergebnisse generiert werden, was zu Verklumpungen führt, wenn sieRAND()
klein sind. ZB(ABS(CAST((BINARY_CHECKSUM(111,null,null) * 0.1) as int))) % 100
==SELECT (ABS(CAST((BINARY_CHECKSUM(113,null,null) * 0.1) as int))) % 100
. Wenn Ihre Daten unter diesem ProblemBINARY_CHECKSUM
leiden , multiplizieren Sie mit 9923.Dieser Link bietet einen interessanten Vergleich zwischen Orderby (NEWID ()) und anderen Methoden für Tabellen mit 1, 7 und 13 Millionen Zeilen.
Wenn in Diskussionsgruppen Fragen zur Auswahl zufälliger Zeilen gestellt werden, wird häufig die NEWID-Abfrage vorgeschlagen. Es ist einfach und funktioniert sehr gut für kleine Tische.
Die NEWID-Abfrage hat jedoch einen großen Nachteil, wenn Sie sie für große Tabellen verwenden. Die ORDER BY-Klausel bewirkt, dass alle Zeilen in der Tabelle in die Tempdb-Datenbank kopiert werden, wo sie sortiert werden. Dies verursacht zwei Probleme:
Was Sie brauchen, ist eine Möglichkeit, Zeilen zufällig auszuwählen, die kein Tempdb verwenden und nicht viel langsamer werden, wenn die Tabelle größer wird. Hier ist eine neue Idee, wie das geht:
Die Grundidee hinter dieser Abfrage ist, dass wir für jede Zeile in der Tabelle eine Zufallszahl zwischen 0 und 99 generieren und dann alle Zeilen auswählen möchten, deren Zufallszahl kleiner als der Wert des angegebenen Prozentsatzes ist. In diesem Beispiel möchten wir, dass ungefähr 10 Prozent der Zeilen zufällig ausgewählt werden. Daher wählen wir alle Zeilen aus, deren Zufallszahl kleiner als 10 ist.
Bitte lesen Sie den vollständigen Artikel in MSDN .
quelle
Wenn Sie (im Gegensatz zum OP) eine bestimmte Anzahl von Datensätzen benötigen (was den CHECKSUM-Ansatz schwierig macht) und eine zufälligere Stichprobe wünschen, als TABLESAMPLE selbst bietet, und auch eine bessere Geschwindigkeit als CHECKSUM wünschen, können Sie mit einer Fusion des Datensatzes auskommen TABLESAMPLE- und NEWID () -Methoden wie folgt:
In meinem Fall ist dies der einfachste Kompromiss zwischen Zufälligkeit (ich weiß nicht wirklich) und Geschwindigkeit. Variieren Sie den Prozentsatz (oder die Zeilen) der TABLESAMPLE entsprechend - je höher der Prozentsatz, desto zufälliger die Stichprobe, aber erwarten Sie einen linearen Geschwindigkeitsabfall. (Beachten Sie, dass TABLESAMPLE keine Variable akzeptiert.)
quelle
Ordnen Sie die Tabelle einfach nach einer Zufallszahl und erhalten Sie die ersten 5.000 Zeilen mit
TOP
.AKTUALISIEREN
Ich habe es einfach versucht und ein
newid()
Anruf ist ausreichend - keine Notwendigkeit für alle Besetzungen und alle Mathematik.quelle
Dies ist eine Kombination aus der ursprünglichen Startidee und einer Prüfsumme, die meiner Meinung nach ohne die Kosten von NEWID () richtig zufällige Ergebnisse liefert:
quelle
In MySQL können Sie dies tun:
quelle
Ich habe diese Variation in den Antworten noch nicht ganz gesehen. Ich hatte eine zusätzliche Einschränkung, bei der ich bei einem anfänglichen Startwert jedes Mal denselben Satz von Zeilen auswählen musste.
Für MS SQL:
Minimales Beispiel:
Normalisierte Ausführungszeit: 1,00
NewId () Beispiel:
Normalisierte Ausführungszeit: 1.02
NewId()
ist unwesentlich langsamer alsrand(checksum(*))
, daher möchten Sie es möglicherweise nicht für große Datensatzgruppen verwenden.Auswahl mit Initial Seed:
Wenn Sie denselben Satz für einen Startwert auswählen müssen, scheint dies zu funktionieren.
quelle
Versuche dies:
quelle
Es scheint, dass newid () nicht in der where-Klausel verwendet werden kann, daher erfordert diese Lösung eine innere Abfrage:
quelle
Ich habe es in Unterabfragen verwendet und es hat mir dieselben Zeilen in Unterabfragen zurückgegeben
dann löste ich mit der Einbeziehung der übergeordneten Tabellenvariablen in wo
Beachten Sie den Where-Zustand
quelle
Die verwendete serverseitige Verarbeitungssprache (z. B. PHP, .net usw.) ist nicht angegeben. Wenn es sich jedoch um PHP handelt, greifen Sie auf die erforderliche Anzahl (oder alle Datensätze) zu und verwenden Sie anstelle der Zufallsgenerierung in der Abfrage die Zufallsfunktion von PHP. Ich weiß nicht, ob .net eine äquivalente Funktion hat, aber wenn dies der Fall ist, verwenden Sie diese, wenn Sie .net verwenden
ORDER BY RAND () kann je nach Anzahl der Datensätze erhebliche Leistungseinbußen nach sich ziehen.
quelle
Das funktioniert bei mir:
quelle
select top 10 percent from table_name order by rand()
, aber das funktioniert auch nicht, da rand () für alle Zeilen den gleichen Wert zurückgibt.