SQL RANK () so với ROW_NUMBER ()


189

Tôi bối rối về sự khác biệt giữa những điều này. Chạy SQL sau đây cho tôi hai tập kết quả chính. Ai đó có thể vui lòng giải thích sự khác biệt?

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

Câu trả lời:


221

ROW_NUMBER: Trả về một số duy nhất cho mỗi hàng bắt đầu bằng 1. Đối với các hàng có giá trị trùng lặp, các số được gán một cách tự nhiên.

Xếp hạng: Chỉ định một số duy nhất cho mỗi hàng bắt đầu bằng 1, ngoại trừ các hàng có giá trị trùng lặp, trong trường hợp đó, cùng một xếp hạng được chỉ định và một khoảng trống xuất hiện trong chuỗi cho mỗi xếp hạng trùng lặp.


324

Bạn sẽ chỉ thấy sự khác biệt nếu bạn có quan hệ trong một phân vùng cho một giá trị đặt hàng cụ thể.

RANKDENSE_RANKcó tính xác định trong trường hợp này, tất cả các hàng có cùng giá trị cho cả cột sắp xếp và phân vùng sẽ có kết quả bằng nhau, trong khi đó ROW_NUMBERsẽ tùy ý (không xác định) gán kết quả tăng cho các hàng được gắn.

Ví dụ: (Tất cả các hàng có cùng StyleIDmột phân vùng và trong phân vùng đó, 3 hàng đầu tiên được buộc khi được sắp xếp theo 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  

Trả về

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

Bạn có thể thấy rằng đối với ba hàng giống nhau, số ROW_NUMBERgia tăng, RANKgiá trị vẫn giữ nguyên sau đó nó nhảy tới 4. DENSE_RANKcũng gán cùng một thứ hạng cho cả ba hàng nhưng sau đó giá trị riêng biệt tiếp theo được gán giá trị là 2.


25
Tuyệt vời! ... Cảm ơn đã đề cập về DENSE_RANK
Sandeep Thomas

7
Cảm ơn một ví dụ tuyệt vời. Đã giúp tôi nhận ra rằng tôi đã sử dụng sai hàm RANK () khi ROW_NUMBER () sẽ phù hợp hơn nhiều.
Ales Potocnik Hahonina

2
Nghiêm túc mà nói, điều này thật tuyệt vời.
Matt Felzani

35

Bài viết này đề cập đến một mối quan hệ thú vị giữa ROW_NUMBER()DENSE_RANK() ( RANK()chức năng không được xử lý cụ thể). Khi bạn cần một câu lệnh được tạo ROW_NUMBER()trên một SELECT DISTINCTcâu lệnh, ROW_NUMBER()nó sẽ tạo ra các giá trị riêng biệt trước khi chúng bị xóa bởi DISTINCTtừ khóa. Ví dụ: truy vấn này

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

... Có thể tạo ra kết quả này ( DISTINCTkhông có hiệu lực):

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

Trong khi đó truy vấn này:

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

... tạo ra những gì bạn có thể muốn trong trường hợp này:

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

Lưu ý rằng ORDER BYmệnh đề của DENSE_RANK()hàm sẽ cần tất cả các cột khác từ SELECT DISTINCTmệnh đề để hoạt động đúng.

Lý do cho điều này là về mặt logic, các chức năng cửa sổ được tính toán trước khi DISTINCTđược áp dụng .

Cả ba chức năng so sánh

Sử dụng cú pháp ( WINDOWmệnh đề) tiêu chuẩn PostgreSQL / Sybase / SQL :

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

... bạn sẽ nhận được:

+---+------------+------+------------+
| 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
Cả ROW_NUMBER và DENSE_RANK đều tạo ra các giá trị trước khi áp dụng riêng biệt. Trên thực tế tất cả các chức năng xếp hạng hoặc bất kỳ chức năng nào tạo ra kết quả trước khi DISTINCT được áp dụng.
Thanocation Ioannidis

1
@ThanocationIoannidis: Hoàn toàn đúng. Tôi đã cập nhật câu trả lời của mình bằng một liên kết đến một bài đăng trên blog, nơi tôi đã giải thích thứ tự thực sự của các hoạt động SQL
Lukas Eder

3

Một chút thôi:

Thứ hạng của một hàng là một cộng với số thứ hạng xuất hiện trước hàng được đề cập.

Row_number là thứ hạng riêng biệt của các hàng, không có bất kỳ khoảng cách nào trong bảng xếp hạng.

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


À, tôi nghĩ đây là thứ tôi đã thiếu -> Row_number là thứ hạng khác biệt của các hàng, không có bất kỳ khoảng cách nào trong bảng xếp hạng.
dotNET Hobbiest

1

Truy vấn đơn giản không có mệnh đề phân vùng:

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 

Đầu ra:

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

Nhìn ví dụ này.

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

Chèn một số dữ liệu

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

Lặp lại cùng một giá trị cho 1

XÁC NHẬN VÀO dbo. # TestTable (id, created_date, info1, info2) GIÁ TRỊ (1, '1/1/09', 'Blue', 'Green')

Nhìn tất cả

SELECT * FROM #TestTable

Nhìn kết quả của bạ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

Cần hiểu khác


-1

Ngoài ra, hãy chú ý ĐẶT HÀNG THEO PHẦN THAM GIA (ví dụ sử dụng db AdventureWorks tiêu chuẩn) khi sử dụng RANK.

CHỌN as1.SalesOrderID, as1.SalesOrderDetailID, RANK () QUÁ (THAM GIA B asNG as1.SalesOrderID ĐẶT HÀNG B asNG as1.SalesOrderID) Ranknoequal, RANK () QUÁ TRÌNH B asNG asS.SalesOrderID ORTER BY SalesOrderId = 43659 ĐẶT HÀNG B SalesNG SalesOrderDetailId;

Đưa ra kết quả:

SalesOrderID SalesOrderDetailID rank_same_as_partition rank_salesorderdetailid
43.659 1 1 1
43.659 2 1 2
43.659 3 1 3
43.659 4 1 4
43.659 5 1 5
43.659 6 1 6
43.659 7 1 7
43.659 8 1 8
43.659 9 1 9
43.659 10 1 10
43.659 11 1 11
43.659 12 1 12

Nhưng nếu thay đổi thứ tự theo (sử dụng OrderQty:

CHỌN as1.SalesOrderID, as1.OrderQty, RANK () QUÁ (THAM GIA B asNG as1.SalesOrderID ĐẶT HÀNG B asNG as1.SalesOrderID) xếp hạng, RANK () QUÁ (TÌM HIỂU CỦA As1.SalesOrderID ĐẶT HÀNG as1.Order SalesOrderId = 43659 ĐẶT HÀNG B OrderNG OrderQty;

Cung cấp:

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 2 1 7
43.659 2 1 7
43.659 3 1 9
43.659 3 1 9
43.659 4 1 11
43.659 6 1 12

Lưu ý cách Xếp hạng thay đổi khi chúng tôi sử dụng OrderQty (bảng thứ hai của cột ngoài cùng bên phải) trong ORDER BY và cách nó thay đổi khi chúng tôi sử dụng SalesOrderDetailID (bảng đầu tiên của cột ngoài cùng bên phải) trong ORDER BY.


-1

Tôi chưa làm gì với thứ hạng, nhưng tôi đã phát hiện ra điều này ngày hôm nay với row_number ().

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

Điều này sẽ dẫn đến một số số hàng lặp lại vì trong trường hợp của tôi, mỗi tên chứa tất cả các mục. Mỗi mặt hàng sẽ được đặt hàng theo số lượng đã được bán.

+--------+------+-----+----+
|glasses |store1|  30 | 1  |
|glasses |store2|  35 | 2  |
|glasses |store3|  40 | 3  |
|shoes   |store2|  10 | 1  |
|shoes   |store1|  20 | 2  |
|shoes   |store3|  22 | 3  |
+--------+------+-----+----+
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.