SQL RANK () versus ROW_NUMBER ()


191

Saya bingung tentang perbedaan antara ini. Menjalankan SQL berikut memberi saya dua set hasil identitas. Bisakah seseorang menjelaskan perbedaannya?

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

Jawaban:


223

ROW_NUMBER: Mengembalikan angka unik untuk setiap baris dimulai dengan 1. Untuk baris yang memiliki nilai duplikat, angka ditetapkan secara arbitrase.

Rangking: Menetapkan angka unik untuk setiap baris yang dimulai dengan 1, kecuali untuk baris yang memiliki nilai duplikat, dalam hal ini peringkat yang sama diberikan dan celah muncul dalam urutan untuk setiap peringkat rangkap.


327

Anda hanya akan melihat perbedaannya jika Anda memiliki ikatan di dalam partisi untuk nilai pemesanan tertentu.

RANKdan DENSE_RANKbersifat deterministik dalam hal ini, semua baris dengan nilai yang sama untuk kolom urutan dan partisi akan berakhir dengan hasil yang sama, sedangkan ROW_NUMBERakan secara sewenang-wenang (non deterministik) memberikan hasil yang meningkat ke baris terikat.

Contoh: (Semua baris memiliki yang sama StyleIDsehingga berada di partisi yang sama dan di dalam partisi itu 3 baris pertama diikat ketika dipesan oleh 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  

Kembali

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

Anda dapat melihat bahwa untuk tiga baris identik ROW_NUMBERkenaikan, RANKnilainya tetap sama dengan lompatannya 4. DENSE_RANKjuga memberikan peringkat yang sama untuk ketiga baris tetapi kemudian nilai berbeda selanjutnya diberi nilai 2.


26
Hebat! ... Terima kasih untuk menyebutkan tentang DENSE_RANK
Sandeep Thomas

7
Terima kasih untuk contoh yang bagus. Bantu saya menyadari bahwa saya salah menggunakan fungsi RANK () ketika ROW_NUMBER () akan jauh lebih tepat.
Ales Potocnik Hahonina

2
serius, ini luar biasa.
Matt Felzani

35

Artikel ini membahas hubungan yang menarik antara ROW_NUMBER()danDENSE_RANK() ( RANK()fungsi tidak diperlakukan secara khusus). Saat Anda membutuhkan yang dihasilkan ROW_NUMBER()pada SELECT DISTINCTpernyataan, ROW_NUMBER()akan menghasilkan nilai yang berbeda sebelum dihilangkan oleh DISTINCTkata kunci. Misalnya kueri ini

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

... mungkin menghasilkan hasil ini ( DISTINCTtidak berpengaruh):

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

Sedangkan kueri ini:

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

... menghasilkan apa yang mungkin Anda inginkan dalam hal ini:

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

Perhatikan bahwa ORDER BYklausa DENSE_RANK()fungsi akan membutuhkan semua kolom lain dari SELECT DISTINCTklausa untuk berfungsi dengan baik.

Alasan untuk ini adalah bahwa secara logis, fungsi jendela dihitung sebelum DISTINCTditerapkan .

Ketiga fungsi tersebut dibandingkan

Menggunakan sintaks standar PostgreSQL / Sybase / SQL ( WINDOWklausa):

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

... Anda akan mendapatkan:

+---+------------+------+------------+
| 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
Baik ROW_NUMBER dan DENSE_RANK menghasilkan nilai sebelum perbedaan diterapkan. Sebenarnya semua fungsi peringkat atau fungsi apa pun menghasilkan hasil sebelum DISTINCT diterapkan.
Thanasis Ioannidis

1
@HanasisIoannidis: Tentu saja. Saya telah memperbarui jawaban saya dengan tautan ke posting blog, tempat saya menjelaskan urutan sebenarnya dari operasi SQL
Lukas Eder


1

Permintaan sederhana tanpa klausa partisi:

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 

Keluaran:

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

Lihat contoh ini.

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

Masukkan beberapa data

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

Ulangi Nilai yang sama untuk 1

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

Lihat Semua

SELECT * FROM #TestTable

Lihatlah hasil Anda

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

Perlu memahami perbedaannya


-1

Juga, perhatikan ORDER OLEH di PARTITION (Standard AdventureWorks db digunakan misalnya) saat menggunakan RANK.

PILIH as1.SalesOrderID, as1.SalesOrderDetailID, RANK () OVER (PARTITION BY as1.SalesOrderID ORDER BY as1.SalesOrderID) diberi peringkat tidak sama dengan, RANK () LEBIH DARI (PARTISI DENGAN as1.SalesOrderID ORDER BY as1.SalesOrderDetaildetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailDetailIdetail SalesOrderId = 43659 ORDER OLEH SalesOrderDetailId;

Memberikan hasil:

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

Tetapi jika ubah pesanan menjadi (gunakan OrderQty:

PILIH as1.SalesOrderID, as1.OrderQty, RANK () LEBIH (PARTISI DENGAN AS1.SalesOrderID ORDER DENGAN as1.SalesOrderID) peringkat tidak sama, PERINGKAT () OVER (PARTISI DENGAN as1.SalesOrderID ORDER DENGAN as1.OrderQty) peringkat_OrderDan SETIAP Penjualan. SalesOrderId = 43659 ORDER DENGAN PesananQty;

Memberi:

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

Perhatikan bagaimana Peringkat berubah ketika kita menggunakan OrderQty (tabel kedua kolom paling kanan) di ORDER BY dan bagaimana perubahan ketika kita menggunakan SalesOrderDetailID (tabel pertama kolom paling kanan) di ORDER BY.


-1

Saya belum melakukan apa pun dengan peringkat, tetapi saya menemukan ini hari ini dengan row_number ().

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

Ini akan menghasilkan beberapa nomor baris berulang karena dalam kasus saya setiap nama memegang semua item. Setiap barang akan dipesan berapa banyak yang terjual.

+--------+------+-----+----+
|glasses |store1|  30 | 1  |
|glasses |store2|  35 | 2  |
|glasses |store3|  40 | 3  |
|shoes   |store2|  10 | 1  |
|shoes   |store1|  20 | 2  |
|shoes   |store3|  22 | 3  |
+--------+------+-----+----+
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.