it-swarm.com.de

Wählen Sie n zufällige Zeilen aus der SQL Server-Tabelle aus

Ich habe eine SQL Server-Tabelle mit ungefähr 50.000 Zeilen darin. Ich möchte ungefähr 5.000 dieser Zeilen nach dem Zufallsprinzip auswählen. Ich habe an einen komplizierten Weg gedacht, eine temporäre Tabelle mit einer "Zufallszahl" -Spalte zu erstellen, meine Tabelle in diese zu kopieren, die temporäre Tabelle zu durchlaufen und jede Zeile mit Rand() zu aktualisieren, und dann aus dieser Tabelle die Zufallszahlspalte auszuwählen <0,1. Ich suche nach einer einfacheren Methode, wenn möglich in einer einzigen Anweisung.

Dieser Artikel schlägt vor, die Funktion NEWID() zu verwenden. Das sieht vielversprechend aus, aber ich kann nicht erkennen, wie ich einen bestimmten Prozentsatz von Zeilen zuverlässig auswählen kann.

Hat das jemals jemand gemacht? Irgendwelche Ideen?

275
John M Gant
select top 10 percent * from [yourtable] order by newid()

Als Antwort auf den "pure trash" -Kommentar zu großen Tabellen: Sie könnten dies so tun, um die Leistung zu verbessern.

select  * from [yourtable] where [yourPk] in 
(select top 10 percent [yourPk] from [yourtable] order by newid())

Die Kosten hierfür werden die Schlüsselüberprüfung der Werte plus der Verbindungskosten sein, die für eine große Tabelle mit einem kleinen prozentualen Wert angemessen sein sollten.

345

Abhängig von Ihren Anforderungen erhalten Sie mit TABLESAMPLE eine nahezu zufällige und bessere Leistung. _. Dies ist auf MS SQL Server 2005 und höher verfügbar. 

TABLESAMPLE gibt Daten von zufälligen Seiten statt von zufälligen Zeilen zurück und daher ruft deos nicht einmal Daten ab, die nicht zurückgegeben werden.

An einem sehr großen Tisch habe ich getestet 

select top 1 percent * from [tablename] order by newid()

dauerte mehr als 20 minuten.

select * from [tablename] tablesample(1 percent)

dauerte 2 minuten.

Die Leistung verbessert sich auch bei kleineren Samples in TABLESAMPLE, während dies bei newid() nicht der Fall ist.

Bitte beachten Sie, dass dies nicht so zufällig ist wie die newid()-Methode, aber Sie erhalten eine anständige Stichprobe.

Siehe die MSDN-Seite .

70
Patrick Taylor

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).

Um eine bessere Leistung zu erzielen, ist es am besten, Zeilen zufällig herauszufiltern. Das folgende Codebeispiel fand ich im SQL Server-Onlinedokumentation-Artikel EINSCHR&AUML;NKEN VON ERGEBNISMENGEN MITHILFE VON TABLESAMPLE:

Wenn Sie wirklich eine zufällige Auswahl von .__ wünschen. einzelne Zeilen, ändern Sie Ihre Abfrage in Zeilen nach dem Zufallsprinzip anstelle von .__ filtern. mit TABLESAMPLE. Zum Beispiel die Die folgende Abfrage verwendet die NEWID Funktion, um ungefähr eine .__ zurückzugeben. Prozent der Zeilen der Sales.SalesOrderDetail-Tabelle:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float)
              / CAST (0x7fffffff AS int)

Die SalesOrderID-Spalte ist in .__ enthalten. der CHECKSUM-Ausdruck damit NEWID () wird einmal pro Zeile zu .__ ausgewertet. Erzielen einer Stichprobe pro Reihe . Der Ausdruck CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) ergibt einen zufälligen Floatwert zwischen 0 und 1.

Wenn Sie gegen eine Tabelle mit 1.000.000 Zeilen laufen, sind hier meine Ergebnisse:

SET STATISTICS TIME ON
SET STATISTICS IO ON

/* newid()
   rows returned: 10000
   logical reads: 3359
   CPU time: 3312 ms
   elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()

/* TABLESAMPLE
   rows returned: 9269 (varies)
   logical reads: 32
   CPU time: 0 ms
   elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)

/* Filter
   rows returned: 9994 (varies)
   logical reads: 3359
   CPU time: 641 ms
   elapsed time: 627 ms
*/    
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float) 
              / CAST (0x7fffffff AS int)

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

Wenn Sie TABLESAMPLE verwenden können, 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.

37
Rob Boek

Zufällige Auswahl von Zeilen aus einem großen Tisch auf MSDN verfügt über eine einfache, gut artikulierte Lösung, die die großen Leistungsprobleme berücksichtigt.

  SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  Rand()) as int)) % 100) < 10
21
Kyle McClellan

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 liefert, und eine höhere Geschwindigkeit als CHECKSUM wünschen, können Sie sich mit dem Zusammenschluss des TABLESAMPLE- und NEWID () -Methoden wie folgt:

DECLARE @sampleCount int = 50
SET STATISTICS TIME ON

SELECT TOP (@sampleCount) * 
FROM [yourtable] TABLESAMPLE(10 PERCENT)
ORDER BY NEWID()

SET STATISTICS TIME OFF

In meinem Fall ist dies der einfachste Kompromiss zwischen Zufälligkeit (ich weiß es nicht wirklich) und Geschwindigkeit. Variieren Sie den TABLESAMPLE-Prozentsatz (oder die Zeilen) entsprechend - je höher der Prozentsatz, desto zufälliger die Probe, aber Sie erwarten einen linearen Geschwindigkeitsabfall. (Beachten Sie, dass TABLESAMPLE keine Variablen akzeptiert.)

9
Oskar Austegard

Ordnen Sie die Tabelle einfach nach einer Zufallszahl an und erhalten Sie die ersten 5.000 Zeilen mit TOP.

SELECT TOP 5000 * FROM [Table] ORDER BY newid();

UPDATE

Ich habe es einfach ausprobiert und ein newid()-Aufruf ist ausreichend - keine Notwendigkeit für alle Besetzungen und alle Berechnungen.

8

Dieser Link hat 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. 

SELECT TOP 10 PERCENT *
  FROM Table1
  ORDER BY NEWID()

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:

  1. Die Sortieroperation ist normalerweise mit hohen Kosten verbunden. Die Sortierung kann sehr viel Platten-E/A erfordern und lange dauern.
  2. Im schlimmsten Fall kann tempdb nicht mehr genügend Speicherplatz haben. In dem Szenario Im besten Fall kann tempdb sehr viel Speicherplatz beanspruchen , Der ohne einen manuellen Befehl zum Verkleinern niemals wiederhergestellt werden kann.

Was Sie brauchen, ist eine Möglichkeit, Zeilen zufällig auszuwählen, die Tempdb nicht verwenden und nicht viel langsamer werden, wenn die Tabelle größer wird. Hier ist eine neue Idee, wie das geht:

SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  Rand()) as int)) % 100) < 10

Die grundlegende Idee hinter dieser Abfrage ist, dass wir für jede Zeile in der Tabelle eine Zufallszahl zwischen 0 und 99 generieren möchten. Wählen Sie dann alle Zeilen aus, deren Zufallszahl kleiner als der angegebene Prozentsatz ist. In diesem Beispiel möchten wir etwa 10 Prozent der Zeilen nach dem Zufallsprinzip auswählen. Daher wählen wir alle Zeilen aus, deren Zufallszahl kleiner als 10 ist.

Bitte lesen Sie den vollständigen Artikel in MSDN

8
RJardines

In MySQL können Sie Folgendes tun:

SELECT `PRIMARY_KEY`, Rand() FROM table ORDER BY Rand() LIMIT 5000;
4
Jeff Ferland

Dies ist eine Kombination aus der anfänglichen Seed-Idee und einer Prüfsumme, die meiner Meinung nach richtig zufällige Ergebnisse ohne die Kosten von NEWID () liefert:

SELECT TOP [number] 
FROM table_name
ORDER BY Rand(CHECKSUM(*) * Rand())
4
Nanki

Ich habe diese Variation in den Antworten noch nicht ganz gesehen. Ich hatte eine zusätzliche Einschränkung, bei der ich angesichts eines anfänglichen Startwerts jedes Mal dieselbe Reihe von Zeilen auswählen musste.

Für MS SQL:

Minimales Beispiel:

select top 10 percent *
from table_name
order by Rand(checksum(*))

Normalisierte Ausführungszeit: 1.00

NewId () Beispiel:

select top 10 percent *
from table_name
order by newid()

Normalisierte Ausführungszeit: 1.02

NewId() ist unwesentlich langsamer als Rand(checksum(*)), daher möchten Sie es möglicherweise nicht für große Datensatzgruppen verwenden.

Auswahl mit Initial Seed:

declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */

select top 10 percent *
from table_name
order by Rand(checksum(*) % @seed) /* any other math function here */

Wenn Sie denselben Satz mit einem Startwert auswählen müssen, scheint dies zu funktionieren.

2
klyd

Versuche dies:

SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()
2
Ravi Parashar

Es scheint, newid () kann nicht in where-Klausel verwendet werden, daher erfordert diese Lösung eine innere Abfrage:

SELECT *
FROM (
    SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd
    FROM MyTable
) vw
WHERE Rnd % 100 < 10        --10%
0
Hai Phan

Ich habe es in einer Unterabfrage verwendet und es wurden mir dieselben Zeilen in der Unterabfrage zurückgegeben

 SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

dann löste ich mich mit dem Einfügen von Elterntabellenvariablen in wo

SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              Where Mytable.ID>0
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

Beachten Sie die Where-Bedingung

0
VISHMAY

Die verwendete serverseitige Verarbeitungssprache (z. B. PHP, .net usw.) ist nicht angegeben. Wenn es sich jedoch um PHP handelt, greifen Sie die erforderliche Anzahl (oder alle Datensätze) an, und verwenden Sie in der Abfrage die Zufallsfunktion, indem Sie die Shuffle-Funktion von PHP verwenden. Ich weiß nicht, ob .net eine äquivalente Funktion hat, aber wenn dies der Fall ist, verwenden Sie dies, wenn Sie .net verwenden

Bei ORDER BY Rand () kann es je nach Anzahl der Datensätze zu einer Leistungsverschlechterung kommen.

0
SpacePhoenix