SQL RANK () versus ROW_NUMBER ()

189

Ich bin verwirrt über die Unterschiede zwischen diesen. Wenn ich die folgende SQL ausführe, erhalte ich zwei identische Ergebnismengen. Kann jemand bitte die Unterschiede erklären?

SELECT ID, [Description], RANK()       OVER(PARTITION BY StyleID ORDER BY ID) as 'Rank'      FROM SubStyle
SELECT ID, [Description], ROW_NUMBER() OVER(PARTITION BY StyleID ORDER BY ID) as 'RowNumber' FROM SubStyle
dotNET Hobbiest
quelle

Antworten:

221

ROW_NUMBER: Gibt eine eindeutige Nummer für jede Zeile zurück, die mit 1 beginnt. Bei Zeilen mit doppelten Werten werden Nummern willkürlich zugewiesen.

Rang: Weist jeder Zeile, die mit 1 beginnt, eine eindeutige Nummer zu, mit Ausnahme von Zeilen mit doppelten Werten. In diesem Fall wird dieselbe Rangfolge zugewiesen, und in der Reihenfolge wird für jede doppelte Rangfolge eine Lücke angezeigt.

Ritesh Mengji
quelle
324

Sie sehen den Unterschied nur, wenn Sie innerhalb einer Partition Bindungen für einen bestimmten Bestellwert haben.

RANKund DENSE_RANKin diesem Fall deterministisch sind, erhalten alle Zeilen mit demselben Wert sowohl für die Ordnungs- als auch für die Partitionierungsspalte ein gleiches Ergebnis, während ROW_NUMBERden gebundenen Zeilen willkürlich (nicht deterministisch) ein inkrementierendes Ergebnis zugewiesen wird.

Beispiel: (Alle Zeilen haben die gleichen StyleID, befinden sich also in derselben Partition und innerhalb dieser Partition werden die ersten 3 Zeilen bei Bestellung von gebunden. ID)

WITH T(StyleID, ID)
     AS (SELECT 1,1 UNION ALL
         SELECT 1,1 UNION ALL
         SELECT 1,1 UNION ALL
         SELECT 1,2)
SELECT *,
       RANK() OVER(PARTITION BY StyleID ORDER BY ID)       AS 'RANK',
       ROW_NUMBER() OVER(PARTITION BY StyleID ORDER BY ID) AS 'ROW_NUMBER',
       DENSE_RANK() OVER(PARTITION BY StyleID ORDER BY ID) AS 'DENSE_RANK'
FROM   T  

Kehrt zurück

StyleID     ID       RANK      ROW_NUMBER      DENSE_RANK
----------- -------- --------- --------------- ----------
1           1        1         1               1
1           1        1         2               1
1           1        1         3               1
1           2        4         4               2

Sie können sehen, dass für die drei identischen Zeilen die ROW_NUMBERInkremente der RANKWert derselbe bleibt, zu dem er springt 4. DENSE_RANKWeist allen drei Zeilen den gleichen Rang zu, aber dem nächsten eindeutigen Wert wird der Wert 2 zugewiesen.

Martin Smith
quelle
25
Großartig! ... Dank der Erwähnung über DENSE_RANK
Sandeep Thomas
7
Danke für ein tolles Beispiel. Hat mir geholfen zu erkennen, dass ich die Funktion RANK () fälschlicherweise verwendet habe, wenn ROW_NUMBER () viel besser geeignet gewesen wäre.
Ales Potocnik Hahonina
2
Im Ernst, das ist großartig.
Matt Felzani
35

Dieser Artikel behandelt eine interessante Beziehung zwischen ROW_NUMBER()undDENSE_RANK() (die RANK()Funktion wird nicht speziell behandelt). Wenn Sie eine ROW_NUMBER()für eine SELECT DISTINCTAnweisung generierte Anweisung benötigen , ROW_NUMBER()werden unterschiedliche Werte erzeugt, bevor sie durch das DISTINCTSchlüsselwort entfernt werden . ZB diese Abfrage

SELECT DISTINCT
  v, 
  ROW_NUMBER() OVER (ORDER BY v) row_number
FROM t
ORDER BY v, row_number

... könnte dieses Ergebnis erzeugen ( DISTINCThat keine Auswirkung):

+---+------------+
| V | ROW_NUMBER |
+---+------------+
| a |          1 |
| a |          2 |
| a |          3 |
| b |          4 |
| c |          5 |
| c |          6 |
| d |          7 |
| e |          8 |
+---+------------+

Während diese Abfrage:

SELECT DISTINCT
  v, 
  DENSE_RANK() OVER (ORDER BY v) row_number
FROM t
ORDER BY v, row_number

... produziert, was Sie in diesem Fall wahrscheinlich wollen:

+---+------------+
| V | ROW_NUMBER |
+---+------------+
| a |          1 |
| b |          2 |
| c |          3 |
| d |          4 |
| e |          5 |
+---+------------+

Beachten Sie, dass die ORDER BYKlausel der DENSE_RANK()Funktion alle anderen Spalten der SELECT DISTINCTKlausel benötigt, um ordnungsgemäß zu funktionieren.

Der Grund dafür ist, dass FensterfunktionenDISTINCT logischerweise berechnet werden, bevor sie angewendet werden .

Alle drei Funktionen im Vergleich

Verwenden der PostgreSQL / Sybase / SQL-Standardsyntax ( WINDOWKlausel):

SELECT
  v,
  ROW_NUMBER() OVER (window) row_number,
  RANK()       OVER (window) rank,
  DENSE_RANK() OVER (window) dense_rank
FROM t
WINDOW window AS (ORDER BY v)
ORDER BY v

... Du wirst kriegen:

+---+------------+------+------------+
| V | ROW_NUMBER | RANK | DENSE_RANK |
+---+------------+------+------------+
| a |          1 |    1 |          1 |
| a |          2 |    1 |          1 |
| a |          3 |    1 |          1 |
| b |          4 |    4 |          2 |
| c |          5 |    5 |          3 |
| c |          6 |    5 |          3 |
| d |          7 |    7 |          4 |
| e |          8 |    8 |          5 |
+---+------------+------+------------+
Lukas Eder
quelle
1
Sowohl ROW_NUMBER als auch DENSE_RANK erzeugen Werte, bevor eine eindeutige Anwendung angewendet wird. Tatsächlich führt jede Rangfolgefunktion oder eine beliebige Funktion zu Ergebnissen, bevor DISTINCT angewendet wird.
Thanasis Ioannidis
1
@ThanasisIoannidis: Auf jeden Fall. Ich habe meine Antwort mit einem Link zu einem Blog-Beitrag aktualisiert, in dem ich die wahre Reihenfolge der SQL-Operationen erläutert habe
Lukas Eder
1

Einfache Abfrage ohne Partitionsklausel:

select 
    sal, 
    RANK() over(order by sal desc) as Rank,
    DENSE_RANK() over(order by sal desc) as DenseRank,
    ROW_NUMBER() over(order by sal desc) as RowNumber
from employee 

Ausgabe:

    --------|-------|-----------|----------
    sal     |Rank   |DenseRank  |RowNumber
    --------|-------|-----------|----------
    5000    |1      |1          |1
    3000    |2      |2          |2
    3000    |2      |2          |3
    2975    |4      |3          |4
    2850    |5      |4          |5
    --------|-------|-----------|----------
DSR
quelle
0

Schauen Sie sich dieses Beispiel an.

CREATE TABLE [dbo].#TestTable(
    [id] [int] NOT NULL,
    [create_date] [date] NOT NULL,
    [info1] [varchar](50) NOT NULL,
    [info2] [varchar](50) NOT NULL,
)

Fügen Sie einige Daten ein

INSERT INTO dbo.#TestTable (id, create_date, info1, info2)
VALUES (1, '1/1/09', 'Blue', 'Green')
INSERT INTO dbo.#TestTable (id, create_date, info1, info2)
VALUES (1, '1/2/09', 'Red', 'Yellow')
INSERT INTO dbo.#TestTable (id, create_date, info1, info2)
VALUES (1, '1/3/09', 'Orange', 'Purple')
INSERT INTO dbo.#TestTable (id, create_date, info1, info2)
VALUES (2, '1/1/09', 'Yellow', 'Blue')
INSERT INTO dbo.#TestTable (id, create_date, info1, info2)
VALUES (2, '1/5/09', 'Blue', 'Orange')
INSERT INTO dbo.#TestTable (id, create_date, info1, info2)
VALUES (3, '1/2/09', 'Green', 'Purple')
INSERT INTO dbo.#TestTable (id, create_date, info1, info2)
VALUES (3, '1/8/09', 'Red', 'Blue')

Wiederholen Sie dieselben Werte für 1

INSERT INTO dbo. # TestTable (id, create_date, info1, info2) VALUES (1, '1/1/09', 'Blue', 'Green')

Schau alle

SELECT * FROM #TestTable

Schauen Sie sich Ihre Ergebnisse an

SELECT Id,
    create_date,
    info1,
    info2,
    ROW_NUMBER() OVER (PARTITION BY Id ORDER BY create_date DESC) AS RowId,
    RANK() OVER(PARTITION BY Id ORDER BY create_date DESC)    AS [RANK]
FROM #TestTable

Müssen die verschiedenen verstehen

Sansalk
quelle
-1

Achten Sie bei Verwendung von RANK auch auf ORDER BY in PARTITION (z. B. Standard AdventureWorks db wird verwendet).

Wählen Sie as1.SalesOrderID, as1.SalesOrderDetailID, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.SalesOrderID) SalesOrderId = 43659 ORDER BY SalesOrderDetailId;

Gibt Ergebnis:

SalesOrderID SalesOrderDetailID rank_same_as_partition rank_salesorderdetailid
43659 1 1 1
43659 2 1 2
43659 3 1 3
43659 4 1 4
43659 5 1 5
43659 6 1 6
43659 7 1 7
43659 8 1 8
43659 9 1 9
43659 10 1 10
43659 11 1 11
43 1 12

Wenn Sie jedoch die Reihenfolge um ändern, verwenden Sie OrderQty:

SELECT as1.SalesOrderID, as1.OrderQty, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.SalesOrderID) ranknoequal, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.OrderQty) rank_orderqty FROMD SalesOrderId = 43659 ORDER BY OrderQty;

Gibt:

SalesOrderID OrderQty rank_salesorderid rank_orderqty
43659 1 1 1
43659 1 1 1
43659 1 1 1
43659 1 1 1
43659 1 1 1
43659 1 1 1
43659 2 1 7
43659 2 1 7
43659 3 1 9
43659 3 1 9
43659 4 1 11
43659 6 1 12

Beachten Sie, wie sich der Rang ändert, wenn wir OrderQty (zweite Tabelle in der rechten Spalte) in ORDER BY verwenden, und wie er sich ändert, wenn wir in ORDER BY SalesOrderDetailID (erste Tabelle in der rechten Spalte) verwenden.

user2629395
quelle
-1

Ich habe nichts mit Rang gemacht, aber ich habe dies heute mit row_number () entdeckt.

select item, name, sold, row_number() over(partition by item order by sold) as row from table_name

Dies führt zu einigen sich wiederholenden Zeilennummern, da in meinem Fall jeder Name alle Elemente enthält. Jeder Artikel wird nach Anzahl der verkauften Artikel sortiert.

+--------+------+-----+----+
|glasses |store1|  30 | 1  |
|glasses |store2|  35 | 2  |
|glasses |store3|  40 | 3  |
|shoes   |store2|  10 | 1  |
|shoes   |store1|  20 | 2  |
|shoes   |store3|  22 | 3  |
+--------+------+-----+----+
SarahLaMont
quelle