SQL RANK () contro ROW_NUMBER ()


191

Sono confuso sulle differenze tra questi. L'esecuzione del seguente SQL mi dà due set di risultati identici. Qualcuno può spiegare le differenze?

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

Risposte:


223

ROW_NUMBER: restituisce un numero univoco per ogni riga che inizia con 1. Per le righe che hanno valori duplicati, i numeri vengono assegnati arbitrariamente.

Classifica: assegna un numero univoco per ogni riga che inizia con 1, ad eccezione delle righe che hanno valori duplicati, nel qual caso viene assegnato lo stesso ranking e viene visualizzato uno spazio nella sequenza per ciascun ranking duplicato.


327

Vedrai la differenza solo se hai legami all'interno di una partizione per un determinato valore di ordinazione.

RANKe DENSE_RANKsono deterministici in questo caso, tutte le righe con lo stesso valore sia per l'ordinamento e colonne di partizionamento finirà con un risultato uguale, che ROW_NUMBERsarà arbitrariamente (non deterministico) assegnare un risultato incremento alle righe legate.

Esempio: (Tutte le righe hanno le stesse StyleIDquindi sono nella stessa partizione e all'interno di quella partizione le prime 3 righe sono legate quando ordinate per 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  

ritorna

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

Puoi vedere che per le tre file identiche gli ROW_NUMBERincrementi, il RANKvalore rimane lo stesso e poi passa a 4. DENSE_RANKassegna anche lo stesso rango a tutte e tre le righe ma al successivo valore distinto viene assegnato un valore di 2.


26
Fantastico! ... Grazie per la menzione di DENSE_RANK
Sandeep Thomas,

7
Grazie per un ottimo esempio. Mi ha aiutato a capire che stavo usando erroneamente la funzione RANK () quando ROW_NUMBER () sarebbe stato molto più appropriato.
Ales Potocnik Hahonina,

2
seriamente, è fantastico.
Matt Felzani,

35

Questo articolo copre un'interessante relazione tra ROW_NUMBER()eDENSE_RANK() (la RANK()funzione non è trattata in modo specifico). Quando hai bisogno di essere generato ROW_NUMBER()su SELECT DISTINCTun'istruzione, ROW_NUMBER()producono valori distinti prima che vengano rimossi dalla DISTINCTparola chiave. Ad esempio questa query

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

... potrebbe produrre questo risultato ( DISTINCTnon ha alcun effetto):

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

Considerando che questa query:

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

... produce ciò che probabilmente vuoi in questo caso:

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

Si noti che la ORDER BYclausola della DENSE_RANK()funzione avrà bisogno di tutte le altre colonne della SELECT DISTINCTclausola per funzionare correttamente.

La ragione di questo è che logicamente, le funzioni delle finestre sono calcolati prima DISTINCTviene applicato .

Tutte e tre le funzioni a confronto

Utilizzando la sintassi standard PostgreSQL / Sybase / SQL ( WINDOWclausola):

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

... otterrai:

+---+------------+------+------------+
| 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 |
+---+------------+------+------------+

1
Sia ROW_NUMBER che DENSE_RANK producono valori prima che venga applicato distinto. In realtà tutte le funzioni di classificazione o qualsiasi funzione producono risultati prima dell'applicazione di DISTINCT.
Thanasis Ioannidis,

1
@ThanasisIoannidis: Assolutamente. Ho aggiornato la mia risposta con un collegamento a un post sul blog, in cui ho spiegato il vero ordine delle operazioni SQL
Lukas Eder


1

Query semplice senza clausola di partizione:

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 

Produzione:

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

0

Guarda questo esempio.

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

Inserisci alcuni dati

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

Ripeti gli stessi valori per 1

INSERT INTO dbo. # TestTable (id, create_date, info1, info2) VALORI (1, '1/1/09', 'Blu', 'Verde')

Guarda tutto

SELECT * FROM #TestTable

Guarda i tuoi risultati

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

Ho bisogno di capire il diverso


-1

Inoltre, prestare attenzione a ORDER BY in PARTITION (ad esempio viene utilizzato Standard AdventureWorks db) quando si utilizza RANK.

SELEZIONA as1.SalesOrderID, as1.SalesOrderDetailID, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.SalesOrderID) ranknoequal, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.SalesOrderDetailId) ranknodiff DA Sales.SalesOrderDet SalesOrderId = 43659 ORDINA PER SalesOrderDetailId;

Dà risultato:

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
43659 12 1 12

Ma se cambia l'ordine da a (usa OrderQty:

SELEZIONA 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 DA Sales.SalesOrderDetail as1 WHERE SalesOrderId = 43659 ORDINA PER OrderQty;

dà:

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

Nota come cambia la classifica quando usiamo OrderQty (seconda tabella della colonna più a destra) in ORDER BY e come cambia quando usiamo SalesOrderDetailID (prima tabella della colonna più a destra) in ORDER BY.


-1

Non ho fatto nulla con il grado, ma l'ho scoperto oggi con row_number ().

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

Ciò comporterà alcuni numeri di riga ripetuti poiché nel mio caso ogni nome contiene tutti gli elementi. Ogni articolo verrà ordinato in base a quanti sono stati venduti.

+--------+------+-----+----+
|glasses |store1|  30 | 1  |
|glasses |store2|  35 | 2  |
|glasses |store3|  40 | 3  |
|shoes   |store2|  10 | 1  |
|shoes   |store1|  20 | 2  |
|shoes   |store3|  22 | 3  |
+--------+------+-----+----+
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.