2017-09-03 1 views
0

Ich möchte eine ziemlich große Tabelle (Millionen von Zeilen) abfragen, die einen Startwert liefert, so dass eine zufällige Reihenfolge garantiert wird - aber eine, die über mehrere Abfragen stabil bleibt, solange der selbe Startwert verwendet wird.Performante stabile zufällige Sortierung in SQL Server?

Das Beste, was ich mit so habe kommen weit ist

SELECT TOP n * 
     FROM tbl t 
    ORDER BY t.int_column % seed, t.int_column 

Ist dies ein brauchbarer Ansatz, sowohl aus anwendungstechnischer Sicht und eine etwas gleichmäßige Verteilung von Ergebniszeilen über verschiedene Samen?

Edit:

Für Kontext, die stabile Art wegen mehrerer gebraucht wird - möglicherweise verschachtelte - WHERE NOT IN Abfragen, die auf dem gleichen Datenbestand arbeiten; z.B.

SELECT * 
    FROM tbl t 
WHERE t.some_criteria = 'some_value' 
    AND t.id NOT IN 
(
    SELECT TOP n t.id 
      FROM tbl t 
     WHERE t.some_other_criteria = 'some_other_value' 
     ORDER BY t.int_column % seed, t.int_column 
) 
    AND t.id NOT IN 
(
    # etc. 
) 

Wenn die Reihenfolge der Subselects ist zufällig, aber nicht stabil (d.h. NEWID(), TABLESAMPLE()) schwanken die Ergebniszeilen wild zwischen Ausführungen.

+0

Wo werden die Daten konsumiert? Wenn Sie Daten nicht an eine andere gespeicherte Prozedur oder einen anderen In-Database-Code übergeben, ist das Ordnen von Zeilen nach nicht-trivialen Kriterien ein Problem auf View-Ebene und sollte nicht in Ihrem Datenbankcode, sondern in Ihrer Anwendung enthalten sein Code. – Dai

+0

@Dai - große Frage. Bitte sehen Sie meine Bearbeitung. – vzwick

+0

Sie werden immer alle Datensätze sortieren, die die Kriterien erfüllen, was ziemlich viel ist. Sind Sie sicher, dass Sie alle diese "NOT IN" -Klauseln benötigen? Vielleicht können Sie die Abfrage etwas vereinfachen. Was genau willst du erreichen? –

Antwort

1

Wenn Sie eine zufällige Reihenfolge wünschen, können Sie dies mit HASHBYTES und einigen Daten aus der von Ihnen ausgewählten Zeile tun.

SELECT TOP 100 * 
    FROM tbl t 
    ORDER BY HASHBYTES('SHA1', CONCAT(STR(t.int_column), 'seed string')) 

nun die Leistung hierfür ist eine große Frage. Moderne CPUs tun SHA1 sehr schnell, also könnte dies gut genug für Ihre Bedürfnisse sein.

Wenn Sie mehr über die Leistung und weniger über „gute Zufälligkeit“, die Sie in einer einfachen linear congruential generator als Transformationsfunktion fallen könnten:

SET ARITHABORT OFF; 
SET ARITHIGNORE ON; 
SET ANSI_WARNINGS OFF; 

SELECT TOP 100 * 
    FROM tbl t 
    ORDER BY ((t.int_column + seed_number) * 1103515245 + 12345) 

Dies wird schneller, aber weniger zufällig.

+0

Die 'HASHBYTES()' Route ist leider um 90% schlechter im Vergleich zu 'modulo'. Ich konnte die lineare Kongruenzgeneratorperformance nicht ganz testen, weil sie einen Int-Überlauf verursacht. – vzwick

+0

Den int Überlauf ('CAST AS bigint' hat geholfen), aber * weniger zufällig * ist ein bisschen eine Untertreibung; in der Tat ist die Reihenfolge _very_ stable;) – vzwick

+1

Die "Zufälligkeit" von LC kommt von den Fällen, in denen die ganze Zahl um einen Modulo gewickelt wird (normalerweise geschieht dies bei dem Maximum eines ganzzahligen Datentyps, den Sie verwenden). Ich weiß eigentlich nicht, wie SQL Server Mathe umwickelt. –

0

Nur ein Gedanke ... Sie könnten eine "RamdomSort" -Spalte zu Ihrer Tabelle hinzufügen. Auf diese Weise wird die Sortierreihenfolge wirklich zufällig sein, aber wiederholbar wiederholbar bleiben, bis Sie die Tabelle mit neuen Werten aktualisieren. Etwas in diese Richtung ...

ALTER TABLE dbo.MyTable ADD RandomSort INT NOT NULL 
CONSTRAINT df_MyTable_RandomSort DEFAULT(0); 


UPDATE mt SET 
    mt.RandomSort = ABS(CHECKSUM(NEWID())) % 100000 + 1 
FROM 
    dbo.MyTable mt; 

SELECT 
    * 
FROM 
    dbo.MyTable mt 
ORDER BY 
    mt.SomeValue; 

Wenn die Situation es rechtfertigt, man kann sogar eine Abdeckung, nicht gruppierten Index fügen Sie den Sortiervorgang zu beseitigen.