SQL RANK () versus ROW_NUMBER ()

191

Estoy confundido acerca de las diferencias entre estos. Ejecutar el siguiente SQL me da dos conjuntos de resultados idénticos. ¿Alguien puede explicar las diferencias?

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
fuente

Respuestas:

223

ROW_NUMBER: devuelve un número único para cada fila que comienza con 1. Para las filas que tienen valores duplicados, los números se asignan arbitrariamente.

Clasificación: asigna un número único para cada fila que comienza con 1, excepto las filas que tienen valores duplicados, en cuyo caso se asigna la misma clasificación y aparece un espacio en la secuencia para cada clasificación duplicada.

Ritesh Mengji
fuente
327

Solo verá la diferencia si tiene vínculos dentro de una partición para un valor de pedido particular.

RANKy DENSE_RANKson deterministas en este caso, todas las filas con el mismo valor tanto para las columnas de ordenación como de partición terminarán con un resultado igual, mientras que ROW_NUMBERasignarán arbitrariamente (no determinísticamente) un resultado incremental a las filas vinculadas.

Ejemplo: (Todas las filas tienen lo mismo, StyleIDpor lo que están en la misma partición y dentro de esa partición las primeras 3 filas están vinculadas cuando se ordena 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  

Devoluciones

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

Puede ver que para las tres filas idénticas los ROW_NUMBERincrementos, el RANKvalor permanece igual y luego salta a 4. DENSE_RANKtambién asigna el mismo rango a las tres filas, pero luego al siguiente valor distinto se le asigna un valor de 2.

Martin Smith
fuente
26
¡Genial! ... Gracias por mencionar DENSE_RANK
Sandeep Thomas el
77
Gracias por un gran ejemplo. Me ayudó a darme cuenta de que he estado usando la función RANK () cuando ROW_NUMBER () habría sido mucho más apropiado.
Ales Potocnik Hahonina
2
En serio, esto es asombroso.
Matt Felzani
35

Este artículo cubre una relación interesante entre ROW_NUMBER()yDENSE_RANK() (la RANK()función no se trata específicamente). Cuando necesite un generado ROW_NUMBER()en una SELECT DISTINCTdeclaración, ROW_NUMBER()producirá valores distintos antes de que la DISTINCTpalabra clave los elimine . Por ejemplo, esta consulta

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

... podría producir este resultado ( DISTINCTno tiene efecto):

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

Mientras que esta consulta:

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

... produce lo que probablemente quieras en este caso:

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

Tenga en cuenta que la ORDER BYcláusula de la DENSE_RANK()función necesitará todas las demás columnas de la SELECT DISTINCTcláusula para funcionar correctamente.

La razón de esto es que, lógicamente, las funciones de la ventana se calculan antes de DISTINCTaplicarse .

Las tres funciones en comparación

Usando la sintaxis estándar de 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

... obtendrás:

+---+------------+------+------------+
| 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
fuente
1
Tanto ROW_NUMBER como DENSE_RANK producen valores antes de que se aplique la distinción. En realidad, todas las funciones de clasificación o cualquier función producen resultados antes de aplicar DISTINCT.
Thanasis Ioannidis
1
@ThanasisIoannidis: Absolutamente. He actualizado mi respuesta con un enlace a una publicación de blog, donde he explicado el verdadero orden de las operaciones de SQL
Lukas Eder
1

Consulta simple sin cláusula de partición:

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 

Salida:

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

Mira este ejemplo.

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

Insertar algunos datos

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 los mismos valores para 1

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

Mira todo

SELECT * FROM #TestTable

Mira tus 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

Necesito entender lo diferente

sansalk
fuente
-1

Además, preste atención a ORDER BY en PARTITION (se utiliza Standard AdventureWorks db, por ejemplo) cuando use RANK.

SELECCIONE 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 FROM Sales. SalesOrderId = 43659 ORDER BY SalesOrderDetailId;

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

Pero si cambia el orden por a (use OrderQty:

SELECCIONE 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 as1. SalesOrderId = 43659 ORDER BY OrderQty;

Da:

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 cómo cambia el Rango cuando usamos OrderQty (segunda columna de la derecha) en ORDER BY y cómo cambia cuando usamos SalesOrderDetailID (primera columna de la derecha) en ORDER BY.

usuario2629395
fuente
-1

No he hecho nada con el rango, pero descubrí esto hoy con row_number ().

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

Esto dará como resultado algunos números de fila repetidos ya que en mi caso cada nombre contiene todos los elementos. Cada artículo será ordenado por cuántos se vendieron.

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