SQL RANK () проти ROW_NUMBER ()


190

Я розгублений щодо відмінностей між ними. Запуск наступного SQL отримує два ідентичні набори результатів. Може хтось, будь ласка, пояснить відмінності?

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

Відповіді:


222

ROW_NUMBER: Повертає унікальне число для кожного рядка, починаючи з 1. Для рядків, які мають повторювані значення, числа довільно присвоюються.

Ранг: Призначає унікальне число для кожного рядка, починаючи з 1, за винятком рядків, які мають дублюючі значення, і в цьому випадку присвоюється однаковий рейтинг, а в послідовності для кожного дублюючого рейтингу з’являється пробіл.


327

Різницю ви побачите лише за наявності зв’язків у розділі для певного значення замовлення.

RANKі DENSE_RANKдетерміновані в цьому випадку, всі рядки з однаковим значенням як для стовпців, що впорядковують, так і розділяються в кінцевому підсумку з рівним результатом, тоді як ROW_NUMBERдовільно (не детерміновано) присвоєння результату нарощування зв'язаним рядкам.

Приклад: (Усі рядки є однаковими, StyleIDтому знаходяться в одному розділі, і всередині цього розділу перші 3 ряди прив'язуються за замовленням 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  

Повертається

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

Ви можете бачити, що для трьох однакових рядків з ROW_NUMBERкроком RANKзначення залишається тим самим, після чого воно перескакує 4. DENSE_RANKтакож присвоює однаковий ранг усім трьом рядкам, але тоді наступному окремому значенню присвоюється значення 2.


26
Чудово! ... Завдяки згадці про DENSE_RANK
Sandeep Thomas

7
Дякую за чудовий приклад. Допоміг мені зрозуміти, що я неправильно використовував функцію RANK (), коли ROW_NUMBER () був би набагато доречнішим.
Алесь Поточник Гахоніна

2
серйозно, це приголомшливо.
Метт Фельцані

35

Ця стаття охоплює цікавий взаємозв'язок між ROW_NUMBER()іDENSE_RANK() ( RANK()функція не розглядається конкретно). Коли вам потрібен згенерований ROW_NUMBER()на SELECT DISTINCTоператорі ROW_NUMBER()випуск , він видасть чіткі значення, перш ніж вони будуть видалені DISTINCTключовим словом. Наприклад, цей запит

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

... може призвести до цього результату ( DISTINCTне має ефекту):

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

Беручи до уваги цей запит:

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

... виробляє те, що ви, мабуть, хочете в цьому випадку:

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

Слід зазначити , що ORDER BYположення про DENSE_RANK()функції потрібно все решта стовпці з SELECT DISTINCTпункту працювати належним чином.

Причиною цього є те, що логічно функції вікна обчислюються раніше, ніж DISTINCTзастосовуються .

Всі три функції в порівнянні

Використання стандартного синтаксису PostgreSQL / Sybase / SQL WINDOW:

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

... ви отримаєте:

+---+------------+------+------------+
| 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
І ROW_NUMBER, і DENSE_RANK створюють значення перед тим, як застосувати різницю. Насправді вся функція ранжування або будь-яка функція дає результати перед застосуванням DISTINCT.
Танасіс Іоаннідіс

1
@ThanasisIoannidis: Абсолютно. Я оновив свою відповідь посиланням на пост в блозі, де я пояснив справжній порядок операцій SQL
Лукас Едер

3

Трішки:

Ранг ряду - це плюс плюс число рангів, що надходять до відповідного рядка.

Row_number - це чіткий ранг рядків, без розривів у рейтингу.

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


Ах, я думаю, що це мені не вистачало -> Row_number - це чіткий ранг рядків, без розривів у рейтингу.
dotNET Hobbiest

1

Простий запит без розділу розділу:

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 

Вихід:

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

Подивіться цей приклад.

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

Вставте деякі дані

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

Повторіть однакові значення для 1

ВСТАВИТЬСЯ в dbo. # TestTable (id, create_date, info1, info2) ЦІННОСТІ (1, '1/1/09', 'Blue', 'Green')

Подивись усіх

SELECT * FROM #TestTable

Подивіться свої результати

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

Потрібно зрозуміти різні


-1

Також зверніть увагу на ЗАМОВИТИ В ЧАСТИНИ (наприклад, використовується стандартний AdventureWorks db) при використанні RANK.

ВИБІРТЕ as1.SalesOrderID, as1.SalesOrderDetailID, RANK () НАД (РОЗДІЛЬЯ by as1.SalesOrderID ORDER BY as1.SalesOrderID) ranknoequal, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER OF as1.eaeeeee aseee) SalesOrderId = 43659 ЗАМОВЛЕННЯ SalesOrderDetailId;

Дає результат:

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

Але якщо змінити порядок на на (використовуйте OrderQty:

ВИБІРТЕ as1.SalesOrderID, as1.OrderQty, RANK () НАД (РОЗДІЛЕННЯ as1.SalesOrderID ЗАМОВЛЕННЯ as1.SalesOrderID) ranknoequal, RANK () НАДЕЖЕ (ПАРТІЄТЬСЯ as1.SalesOrderID ORDER BY as1.OrderAtye) рангом as1.OrderqtyROM SalesOrderId = 43659 ЗАМОВИТИ ЗАМОВЛЕННЯ Кількість;

Дає:

Замовлення SalesOrderIDQty 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

Зверніть увагу, як змінюється Ранг, коли ми використовуємо OrderQty (друга таблиця правого стовпця) у ORDER BY та як вона змінюється, коли ми використовуємо SalesOrderDetailID (перша таблиця праворуч у першій таблиці) у ORDER BY.


-1

Я нічого не зробив з рангом, але я виявив це сьогодні з row_number ().

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

Це призведе до декількох повторних номерів рядків, оскільки в моєму випадку кожне ім’я містить усі елементи. Кожен товар буде упорядкований за кількістю проданих.

+--------+------+-----+----+
|glasses |store1|  30 | 1  |
|glasses |store2|  35 | 2  |
|glasses |store3|  40 | 3  |
|shoes   |store2|  10 | 1  |
|shoes   |store1|  20 | 2  |
|shoes   |store3|  22 | 3  |
+--------+------+-----+----+
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.