2013-09-04 14 views
7

Ich habe SQL in paar Datenbank-Engines von Zeit zu Zeit mehrere Jahre verwendet, aber habe wenig theoretisches Wissen, so meine Frage könnte sehr "noobish" für einige von Ihnen sein. Aber jetzt ist es wichtig für mich, also muss ich fragen.Ist ORDER BY und ROW_NUMBER() deterministisch?

Stellen Sie sich vor Tabelle Urls mit nicht eindeutigen Spalte status. Und für die Frage angenommen, dass wir eine große Anzahl von Zeilen haben und der Status in jedem Datensatz den gleichen Wert hat.

Und stellen wir oft Abfrage ausführen:

SELECT * FROM Urls ORDER BY status 
  1. bekommen wir jedes Mal die gleiche Zeile Ordnung oder nicht? Was machen wir, wenn wir neue Zeilen hinzufügen? Ändert es die Reihenfolge oder werden neue Datensätze an das Ende der Ergebnisse angehängt? Und wenn wir nicht die gleiche Reihenfolge bekommen - unter welchen Bedingungen hängt diese Reihenfolge ab?

  2. Wird ROW_NUMBER() OVER (ORDER BY status) die gleiche Reihenfolge wie die Abfrage oben zurückgeben oder basiert es auf einem anderen Bestellmechanismus?

+4

1. Nein 2. Der Mechanismus ist derselbe, aber das Ergebnis kann unterschiedlich sein Die tatsächliche Reihenfolge der Reihenfolge der ungeordneten Zeilen hängt von den Entscheidungen des Abfrageoptimierers ab und physisches Layout von Daten/Index – Arvo

Antwort

9

Es ist sehr einfach. Wenn Sie eine Bestellung wünschen, auf die Sie sich verlassen können, müssen Sie genügend Spalten in Ihre ORDER BY-Klausel einfügen, sodass die Kombination aller dieser Spalten für jede Zeile eindeutig ist. Nichts anderes ist garantiert.

Für eine einzelne Tabelle können Sie in der Regel erhalten, was Sie wollen, indem Sie die Spalten auflisten, nach denen "interessant" sortiert werden soll, und anschließend die Primärschlüsselspalte (n) einfügen. Da die PK für sich allein die Eindeutigkeit garantiert, ist auch gewährleistet, dass die gesamte Kombination eindeutig die Reihenfolge definiert, z. Wenn die Urls Tabelle einen Primärschlüssel von {Site, Page, Ordinal} hat dann wäre die folgende gibt Ihnen ein zuverlässiges Ergebnis:

SELECT * FROM Urls ORDER BY status, Site, Page, Ordinal 
+0

+1 Offensichtlich, wenn Sie JOINs haben Sie müssen die PK aller Tabellen kombinieren, und wenn Sie UNIONs haben, können Sie für jede UNION ein neues Feld mit den Werten 1, 2, 3 hinzufügen. – xanatos

7

ORDER BY in SQL Server nicht stabil ist (noch in einer anderen Datenbank, soweit ich weiß). Eine stabile Sortierung ist eine, die Datensätze in derselben Reihenfolge zurückgibt, in der sie in der Tabelle gefunden werden.

Der High-Level-Grund ist ziemlich einfach. Tabellen sind Sätze. Sie haben keine Reihenfolge. Eine "stabile" Art macht also keinen Sinn.

Die untergeordneten Gründe sind wahrscheinlich wichtiger. Die Datenbank könnte einen parallelen Sortieralgorithmus implementieren. Solche Algorithmen sind standardmäßig nicht stabil.

Wenn Sie eine stabile Sortierung wünschen, fügen Sie eine Schlüsselspalte in die Sortierung ein.

Diese in der documentation erwähnt wird:

Die zugrunde liegenden Daten, dass durch verwendet wird:

stabile Ergebnisse zwischen Abfrageanforderungen zu erreichen, Offset- und FETCH, müssen folgende Bedingungen erfüllt sein Die Abfrage darf nicht geändert werden. Das heißt, entweder die von der Abfrage berührten Zeilen werden nicht aktualisiert, oder alle Anforderungen für Seiten aus der Abfrage werden in einer einzelnen Transaktion unter Verwendung entweder der Snapshot- oder serialisierbaren Transaktionsisolation ausgeführt. Weitere Informationen zu diesen Transaktionsisolationsstufen finden Sie unter SET TRANSACTION ISOLATION LEVEL (Transact-SQL).

Die ORDER BY-Klausel enthält eine Spalte oder Kombination von Spalten, die garantiert eindeutig sind.

+0

Ich verwende SQL seit fast 20 Jahren und musste die Definition einer STABLE-Sortierung nachschlagen. Es ist irgendwie verwirrend. Wenn Sie über eine Datenbank sprechen, denken Sie zuerst an ACID. Daher muss alles konsistent (stabil) sein. In diesem Zusammenhang sagen Sie, dass die Daten auf den Seiten in der gleichen Reihenfolge wie die Ergebnisse sind (dh - stabile Sortierung). Dies bedeutet nicht, dass ORDER BY mit einem korrekten ISOLATION LEVEL nicht die korrekten Ergebnisse liefert. –

+0

Ich bin verwirrt; die Frage fragt nach "deterministisch", diese Frage beantwortet "stabil"; sind das nicht [zwei verschiedene Dinge?] (http://stackoverflow.com/questions/2313940/what-is-a-deterministic-quicksort#comment2282232_2314031) –

0

Ich liebe wirklich diese Art von Fragen, da Sie in Performance-Analyse tun können.

Zunächst erstellen wir eine Beispieldatenbank [test] mit einer [urls] -Tabelle mit einer Million zufälliger Datensätze.

Siehe Code unten.

-- Switch databases 
USE [master]; 
go 

-- Create simple database 
CREATE DATABASE [test]; 
go 

-- Switch databases 
USE [test]; 
go 

-- Create simple table 
CREATE TABLE [urls] 
    (
     my_id INT IDENTITY(1, 1) 
       PRIMARY KEY , 
     my_link VARCHAR(255) , 
     my_status VARCHAR(15) 
    ); 
go 

-- http://stackoverflow.com/questions/1393951/what-is-the-best-way-to-create-and-populate-a-numbers-table 

-- Load table with 1M rows of data 
; 
WITH PASS0 
      AS (SELECT 1 AS C 
       UNION ALL 
       SELECT 1 
      ),   --2 rows 
     PASS1 
      AS (SELECT 1 AS C 
       FROM  PASS0 AS A , 
         PASS0 AS B 
      ), --4 rows 
     PASS2 
      AS (SELECT 1 AS C 
       FROM  PASS1 AS A , 
         PASS1 AS B 
      ), --16 rows 
     PASS3 
      AS (SELECT 1 AS C 
       FROM  PASS2 AS A , 
         PASS2 AS B 
      ), --256 rows 
     PASS4 
      AS (SELECT 1 AS C 
       FROM  PASS3 AS A , 
         PASS3 AS B 
      ), --65536 rows 
     PASS5 
      AS (SELECT 1 AS C 
       FROM  PASS4 AS A , 
         PASS4 AS B 
      ), --4,294,967,296 rows 
     TALLY 
      AS (SELECT ROW_NUMBER() OVER (ORDER BY C) AS Number 
       FROM  PASS5 
      ) 
    INSERT INTO urls 
      (my_link , 
       my_status 
      ) 
      SELECT 
     -- top 10 search engines + me 
        CASE (Number % 11) 
         WHEN 0 THEN 'www.ask.com' 
         WHEN 1 THEN 'www.bing.com' 
         WHEN 2 THEN 'www.duckduckgo.com' 
         WHEN 3 THEN 'www.dogpile.com' 
         WHEN 4 THEN 'www.webopedia.com' 
         WHEN 5 THEN 'www.clusty.com' 
         WHEN 6 THEN 'www.archive.org' 
         WHEN 7 THEN 'www.mahalo.com' 
         WHEN 8 THEN 'www.google.com' 
         WHEN 9 THEN 'www.yahoo.com' 
         ELSE 'www.craftydba.com' 
        END AS my_link , 

     -- ratings scale 
        CASE (Number % 5) 
         WHEN 0 THEN 'poor' 
         WHEN 1 THEN 'fair' 
         WHEN 2 THEN 'good' 
         WHEN 3 THEN 'very good' 
         ELSE 'excellent' 
        END AS my_status 
      FROM TALLY AS T 
      WHERE Number <= 1000000 
go 

Zweitens möchten wir immer die Puffer löschen und zwischenspeichern, wenn wir in unserer Testumgebung eine Leistungsanalyse durchführen. Außerdem möchten wir Statistik-I/O und Zeit aktivieren, um die Ergebnisse zu vergleichen.

Siehe Code unten.

-- Show time & i/o 
SET STATISTICS TIME ON 
SET STATISTICS IO ON 
GO 

-- Remove clean buffers & clear plan cache 
CHECKPOINT 
DBCC DROPCLEANBUFFERS 
DBCC FREEPROCCACHE 
GO 

Drittens möchten wir die erste TSQL-Anweisung versuchen. Sehen Sie sich den Ausführungsplan an und erfassen Sie die Statistiken.

-- Try 1 
SELECT * FROM urls ORDER BY my_status 

/* 
Table 'urls'. Scan count 5, logical reads 4987, physical reads 1, read-ahead reads 4918, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
SQL Server Execution Times: 
CPU time = 3166 ms, elapsed time = 8130 ms. 
*/ 

enter image description here

Viertens wollen wir die zweite TSQL Aussage versuchen. Vergessen Sie nicht, den Cache und die Puffer des Abfrageplans zu löschen. Wenn Sie dies nicht tun, dauert die Abfrage weniger als 1 Sekunde, da sich die meisten Informationen im Speicher befinden. Sehen Sie sich den Ausführungsplan an und erfassen Sie die Statistiken.

-- Try 2 
SELECT ROW_NUMBER() OVER (ORDER BY my_status) as my_rownum, * FROM urls 

/* 
Table 'urls'. Scan count 5, logical reads 4987, physical reads 1, read-ahead reads 4918, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
SQL Server Execution Times: 
CPU time = 3276 ms, elapsed time = 8414 ms. 
*/ 

enter image description here

Last but not least, hier ist der spaßige Teil, die Leistungsanalyse.

1 - Wir können sehen, dass der zweite Plan ein Super-Set der ersten ist. Beide Pläne scannen also den Clustered Index und sortieren die Daten. Parallelität wird verwendet, um die Ergebnisse zusammenzufassen.

2 - Der zweite Plan/Abfrage muss die Zeilennummer berechnen. Er segmentiert die Daten und berechnet diesen Skalar. Daher enden zwei weitere Operatoren im Plan.

Es ist nicht verwunderlich, dass der erste Plan in 8130 ms und der zweite Plan in 8414 ms läuft.

Schauen Sie sich immer den Abfrageplan an. Sowohl geschätzt als auch aktuell. Sie sagen, dass Sie wollen, dass der Motor plant und was er tatsächlich tut.

In diesem Beispiel haben zwei verschiedene TSQL-Anweisungen fast identische Pläne.

Mit freundlichen Grüßen

John

www.craftydba.com

+1

Ich empfehle den Aufwand, aber es beantwortet nicht wirklich die Frage ob ORDER BY (ob für ein in einer ROW_NUMBER-Funktion gesetztes Ergebnis) deterministisch ist und welche Faktoren es nicht beeinflussen, und nicht einen Leistungsvergleich zwischen ROW_NUMBER und ORDER BY. Ich würde jedoch hinzufügen, dass ORWING BY ROW_NUMBER übertrifft. Ein einziger Test mit einer Abweichung von 3,37% erscheint mir jedoch nicht als überzeugender Beweis! – GarethD

+0

Die Frage erneut lesen, ich denke, ich habe das Zeichen verfehlt. Der Benutzer wollte wirklich zwei Fragen wissen; 1 - hinzugefügte Datensätze erscheinen in den Ergebnissen und 2- ist ORDER BY anders als ROW_NUMBER, wenn es um die Transaktionsmaschine geht. Die erste Frage hängt vom ISOLATION-Level ab. Die zweite Frage wird von meiner Abfrageanalyse beantwortet. Sie verwenden offensichtlich einen ähnlichen Plan. –

+0

Nun, ich stimme dir zu @ user2577687, dass das Testen von selbst eine gute Praxis viele Male ist. Aber es gibt auch Fehler. Manchmal ist das Fragen viel besser. Zum Beispiel können Sie eine Frage schreiben und in einigen Minuten auf eine Antwort warten und der Test könnte viel länger dauern. Und die Zeit ist manchmal sehr wichtig. Vor allem, wenn du etwas nicht für dich, sondern für den Arbeitgeber tust :-) Ich führe eine Prozedur, die in kleineren Paketen über 200 Millionen Datensätze aktualisiert. Es läuft langsamer und langsamer. In der Zwischenzeit versuche ich einen besseren zu finden. Das Testen wird mich noch mehr verlangsamen, weil es die aktuelle Abfrage stoppen muss. – user1875438

0

Die allgemeine Antwort auf jede SQL-Frage „welcher Reihenfolge diese Ausgabe hat in“ ist „, was auch immer der Server sich anfühlt, und es kann nicht die gleiche sein von Abfrage zur Abfrage "es sei denn, Sie haben speziell eine Bestellung angefordert.

Sogar etwas Einfaches wie 'Select Top 1000 MyColumn von myTable' kann mit beliebigen Reihen in beliebiger Reihenfolge zurückkommen; zB kann der Server parallele Threads verwenden und der erste Thread beginnt mit der Rückgabe von Ergebnissen, die in der Mitte der Tabelle eingelesen wurden, oder es wurde ein Index verwendet, der myColumn enthielt, also die Zeilen mit dem alphabetisch ersten productName (dieses Mal; Index hatte verschiedene Statistiken, so dass es einen anderen Index ausgewählt und gab Ihnen die 1000 ältesten Transaktionen) ...

Es ist sogar theoretisch möglich für den Server zu sagen "Ich hatte diese 10 Seiten in meinem Speicher-Cache, die Ihre Abfrage entsprechen, Ich gebe Ihnen diese, während ich warte, bis die Platte den Rest zurückgibt ...

Verwandte Themen