SQL RANK () ve ROW_NUMBER () karşılaştırması


191

Bunlar arasındaki farklar konusunda kafam karıştı. Aşağıdaki SQL'i çalıştırmak bana iki kimlik sonucu kümesi verir. Birisi farklılıkları açıklayabilir mi?

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

Yanıtlar:


223

ROW_NUMBER: 1 ile başlayan her satır için benzersiz bir sayı döndürür. Yinelenen değerlere sahip satırlar için, sayılar rastgele atanır.

Sıralama: Yinelenen değerlere sahip satırlar hariç, 1 ile başlayan her satır için benzersiz bir sayı atar; bu durumda aynı sıralama atanır ve her yinelenen sıralama için sekansta bir boşluk görünür.


327

Farkı, yalnızca belirli bir sipariş değeri için bir bölüm içinde bağlarınız varsa görürsünüz.

RANKve DENSE_RANKbu durumda belirleyicidir, hem sıralama hem de bölümleme sütunları için aynı değere sahip tüm satırlar eşit bir sonuçla sonuçlanırken, ROW_NUMBERbağlı olarak (deterministik olarak) bağlı satırlara artan bir sonuç atayacaktır.

Örnek: (Tüm satırlar aynıdır, StyleIDbu nedenle aynı bölümdedir ve bu bölüm içinde sipariş verildiğinde ilk 3 satır bağlanır 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  

İadeler

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

Üç özdeş satır için, ROW_NUMBERartımların RANKdeğerinin aynı kaldığı ve ardından atlandığı gibi olduğunu görebilirsiniz 4. DENSE_RANKaynı sırayı her üç satıra da atar ancak bir sonraki farklı değere 2 değeri atanır.


26
Harika! ... DENSE_RANK
Sandeep Thomas

7
Harika bir örnek için teşekkürler. ROW_NUMBER () çok daha uygun olduğunda RANK () işlevini yanlış kullandığımı anlamama yardımcı oldu.
Ales Potocnik Hahonina

2
cidden, bu harika.
Matt Felzani

35

Bu makale ROW_NUMBER()ve arasında ilginç bir ilişkiyi kapsamaktadırDENSE_RANK() ( RANK()işlev özel olarak ele alınmamaktadır). Bir oluşturulan gerektiğinde ROW_NUMBER()bir üzerine SELECT DISTINCTaçıklamada, ROW_NUMBER()ayrı değerleri üretecek önce onlar tarafından kaldırılır DISTINCTanahtar kelime. Örneğin bu sorgu

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

... bu sonucu verebilir ( DISTINCTetkisi yoktur):

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

Halbuki bu sorgu:

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

... muhtemelen bu durumda istediğinizi üretir:

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

Not ORDER BYait hükmü DENSE_RANK()işlevinden tüm diğer sütunları gerekir SELECT DISTINCTdüzgün çalışması için fıkra.

Bunun nedeni mantıksal olarak pencere fonksiyonlarının daha önce hesaplanmış DISTINCTolmasıdır .

Karşılaştırmada her üç işlev

PostgreSQL / Sybase / SQL standart sözdizimini ( WINDOWyan tümcesi) kullanma:

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

... alacaksınız:

+---+------------+------+------------+
| 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
Hem ROW_NUMBER hem de DENSE_RANK, farklı uygulanmadan önce değerler üretir. Aslında tüm sıralama fonksiyonu veya herhangi bir fonksiyon DISTINCT uygulanmadan önce sonuç üretir.
Thanasis Ioannidis

1
@ThanasisIoannidis: Kesinlikle. Cevabımı , SQL işlemlerinin gerçek sırasını
Lukas Eder


1

Bölüm yan tümcesi içermeyen basit sorgu:

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 

Çıktı:

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

Bu örneğe bak.

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

Biraz veri ekle

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 için aynı değerleri tekrarla

Dbo'ya INSERT. # TestTable (id, create_date, info1, info2) DEĞERLER (1, '1/1/09', 'Mavi', 'Yeşil')

Tümüne Bak

SELECT * FROM #TestTable

Sonuçlarınıza bakın

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

Farklı anlamak gerekir


-1

Ayrıca, RANK kullanırken PARTITION'daki ORDER BY'a (örneğin, standart AdventureWorks db kullanılır) dikkat edin.

As1.SalesOrderID, as1.SalesOrderDetailID, RANK () OVER (as1.SalesOrderID ORDER BY as1.SalesOrderID) ranknoequal, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.SalesOrderID) ranknoequal, RANK () AŞIRI SalesOrderId = 43659 ORDER BY SalesOrderDetailId;

Sonuç verir:

SalesOrderID SalesOrderDetailID sıralaması_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
4 4
12 11 1146 1139 11365 1139 1 12

Ancak siparişi olarak değiştirirseniz (OrderQty kullanın:

As1.SalesOrderID, as1.OrderQty, RANK () OVER (as1.SalesOrderID ORDER BY as1.SalesOrderID) ranknoequal, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.OrderIDty) ranknoequal, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.OrderQty) rank_orderOty Whale Satışları SalesOrderId = 43659 SİPARİŞ TARAFINDAN SİPARİŞ VER;

verir:

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

ORDER BY içinde OrderQty'yi (en sağdaki sütun ikinci tablosu) kullandığımızda Sıralama'nın nasıl değiştiğine ve ORDER BY içindeki SalesOrderDetailID'yi (en sağdaki sütun ilk tablosu) kullandığımızda nasıl değiştiğine dikkat edin.


-1

Rütbe ile hiçbir şey yapmadım, ama bugün row_number () ile bunu keşfettim.

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

Benim durumumda her isim tüm öğeleri içerdiğinden, bu bazı tekrarlayan satır numaralarına neden olacaktır. Her ürün kaç tane satıldığına göre sipariş edilecektir.

+--------+------+-----+----+
|glasses |store1|  30 | 1  |
|glasses |store2|  35 | 2  |
|glasses |store3|  40 | 3  |
|shoes   |store2|  10 | 1  |
|shoes   |store1|  20 | 2  |
|shoes   |store3|  22 | 3  |
+--------+------+-----+----+
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.