SQL RANK () กับ ROW_NUMBER ()


191

ฉันสับสนเกี่ยวกับความแตกต่างระหว่างสิ่งเหล่านี้ การรัน 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

คำตอบ:


223

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 กำหนดอันดับเดียวกันให้กับทั้งสามแถวด้วยเช่นกัน แต่ค่าที่แตกต่างกันถัดไปจะได้รับการกำหนดค่าเป็น 24DENSE_RANK


26
เยี่ยมมาก! ... ขอบคุณที่พูดถึง DENSE_RANK
Sandeep Thomas

7
ขอบคุณสำหรับตัวอย่างที่ดี ช่วยฉันรู้ว่าฉันใช้ RANK () ฟังก์ชั่นผิดเมื่อ ROW_NUMBER () เหมาะสมกว่ามาก
Ales Potocnik Hahonina

2
จริงจังนี่มันยอดเยี่ยม
Matt Felzani

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

1
@ThanasisIoannidis: แน่นอน ผมได้ปรับปรุงคำตอบของฉันมีลิงก์ไปยังโพสต์บล็อกที่ฉันได้อธิบายเป็นคำสั่งที่แท้จริงของการดำเนินงาน SQL
Lukas เอ๊ด

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

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

และให้ความสนใจกับ ORDER BY ในพาร์ทิชัน (มาตรฐาน AdventureWorks db ใช้เป็นตัวอย่าง) เมื่อใช้ RANK

เลือก as1SalesOrderID, as1.SalesOrderDetailID, RANK () OVER (พาร์ติชันโดย as1.SalesOrderID เรียงตามลำดับ as1.SalesOrderID) Ranknoequal อันดับ RANK () OVER (พาร์ติชันโดย as1.SalesOrderID ลำดับที่ขายลดลง 1 ตำแหน่งขายตามลำดับ) SalesOrderId = 43659 สั่งซื้อโดย SalesOrderDetailId;

ให้ผลลัพธ์:

ขาย OrderID ขาย OrderDetailID อันดับ same_as_partition อันดับ salesorderdetailid
43659 1 1 1
43659 2 1 2
43659 3 1 3
43659 4 1 4
43659 5 1 4
43659 6 1 6
43659 7 1 7
43659 9 1 4
43659 9 1 4
43659 9
1
43659 1 12

แต่ถ้าเปลี่ยนลำดับโดยเป็น (ใช้ OrderQty

เลือก as1.SalesOrderID, as1.OrderQty, RANK () OVER (พาร์ติชันโดย as1.SalesOrderID เรียงตามลำดับ as1.SalesOrderID) Ranknoequal อันดับ RANK () OVER (พาร์ติชันโดย as1SalesOrderID ลำดับ) SalesOrderId = 43659 สั่งซื้อโดย OrderQty;

ให้:

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

สังเกตว่าอันดับเปลี่ยนไปอย่างไรเมื่อเราใช้ 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  |
+--------+------+-----+----+
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.