RANK do SQL () versus ROW_NUMBER ()

189

Estou confuso sobre as diferenças entre estes. A execução do SQL a seguir me dá dois conjuntos de resultados idênticos. Alguém pode explicar as diferenças?

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
fonte

Respostas:

221

ROW_NUMBER: Retorna um número único para cada linha começando com 1. Para linhas com valores duplicados, os números são atribuídos arbitrariamente.

Classificação: atribui um número único para cada linha começando com 1, exceto para as linhas que possuem valores duplicados; nesse caso, a mesma classificação é atribuída e um intervalo aparece na sequência para cada classificação duplicada.

Ritesh Mengji
fonte
324

Você verá a diferença apenas se tiver vínculos em uma partição para um valor de pedido específico.

RANKe DENSE_RANKsão determinísticos nesse caso, todas as linhas com o mesmo valor para as colunas de ordenação e particionamento terão um resultado igual, enquanto ROW_NUMBERarbitrariamente (não deterministicamente) atribuirão um resultado incremental às linhas vinculadas.

Exemplo: (Todas as linhas têm o mesmo, StyleIDportanto, estão na mesma partição e nessa partição as 3 primeiras linhas são vinculadas quando ordenadas por 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  

Devoluções

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

Você pode ver que, para as três linhas idênticas, os ROW_NUMBERincrementos, o RANKvalor permanece o mesmo e então passa para 4. DENSE_RANKtambém atribui a mesma classificação a todas as três linhas, mas o próximo valor distinto recebe um valor 2.

Martin Smith
fonte
26
Ótimo! ... Obrigado por mencionar sobre DENSE_RANK
Sandeep Thomas
7
Obrigado por um ótimo exemplo. Ajudou-me a perceber que estava usando incorretamente a função RANK () quando ROW_NUMBER () teria sido muito mais apropriado.
Ales Potocnik Hahonina
2
Sério, isso é incrível.
Matt Felzani
35

Este artigo aborda uma relação interessante entre ROW_NUMBER()eDENSE_RANK() (a RANK()função não é tratada especificamente). Quando você precisar gerar ROW_NUMBER()uma SELECT DISTINCTdeclaração, ROW_NUMBER()ela produzirá valores distintos antes de serem removidos pela DISTINCTpalavra - chave. Por exemplo, esta consulta

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

... pode produzir este resultado ( DISTINCTnão tem efeito):

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

Considerando que esta consulta:

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

... produz o que você provavelmente deseja neste caso:

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

Observe que a ORDER BYcláusula da DENSE_RANK()função precisará de todas as outras colunas da SELECT DISTINCTcláusula para funcionar corretamente.

A razão para isso é que, logicamente, as funções da janela são calculadas antes de DISTINCTserem aplicadas .

Todas as três funções em comparação

Usando a sintaxe padrão do PostgreSQL / Sybase / SQL ( WINDOWcláusula):

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

... você terá:

+---+------------+------+------------+
| 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
fonte
1
ROW_NUMBER e DENSE_RANK produzem valores antes de aplicar distinto. Na verdade, todas as funções de classificação ou qualquer função produzem resultados antes da aplicação de DISTINCT.
Thanasis Ioannidis
1
@ThanasisIoannidis: Absolutamente. Eu atualizei a minha resposta com um link para um blog, onde eu expliquei a verdadeira ordem de operações SQL
Lukas Eder
3

Bastante:

A classificação de uma linha é uma mais o número de classificações que vêm antes da linha em questão.

Row_number é a classificação distinta de linhas, sem nenhuma lacuna na classificação.

http://www.bidn.com/blogs/marcoadf/bidn-blog/379/ranking-functions-row_number-vs-rank-vs-dense_rank-vs-ntile

Eu não
fonte
Ah, acho que era isso que estava faltando -> Row_number é a classificação distinta de linhas, sem nenhuma lacuna na classificação.
dotNET Hobbiest
1

Consulta simples sem cláusula de partição:

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 

Resultado:

    --------|-------|-----------|----------
    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
fonte
0

Veja este exemplo.

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

Inserir alguns dados

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

Repita os mesmos valores para 1

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

Look All

SELECT * FROM #TestTable

Veja seus resultados

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

Precisa entender os diferentes

sansalk
fonte
-1

Além disso, preste atenção a ORDER BY em PARTITION (o Standard AdventureWorks db é usado, por exemplo) ao usar RANK.

SELECT as1.SalesOrderID, as1.SalesOrderDetailID, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.SalesOrderID) ranknoequal, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY como as1.SalesOrderDetailId) ranknodiff WHERE Sales1. SalesOrderId = 43659 PEDIDO POR SalesOrderDetailId;

Dá resultado:

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

Mas se alterar a ordem por para (use 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 FROM Sales.SalesOrderDetail WHERE SalesOrderId = 43659 ORDER BY 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

Observe como a classificação muda quando usamos OrderQty (segunda tabela da coluna da direita) em ORDER BY e como ela muda quando usamos SalesOrderDetailID (primeira tabela da coluna da direita) em ORDER BY.

user2629395
fonte
-1

Não fiz nada com classificação, mas descobri isso hoje com row_number ().

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

Isso resultará em alguns números de linha repetidos, pois no meu caso, cada nome contém todos os itens. Cada item será encomendado por quantos foram vendidos.

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