ROW_NUMBER vs RANK vs DENSE_RANK
Pertanyaan
"Ini tabel gaji karyawan. Tanpa menjalankan query, tebak isi kolom
row_num,rnk, dandense_rnkuntuk setiap baris. Setelah itu jelaskan kapan kamu pakai masing-masing fungsi."
Tabel karyawan:
| nama | gaji |
|---|---|
| Andi | 12000000 |
| Budi | 9500000 |
| Citra | 12000000 |
| Dewi | 8000000 |
| Eko | 9500000 |
| Fajar | 15000000 |
| Gita | 9500000 |
SELECT nama, gaji, ROW_NUMBER() OVER (ORDER BY gaji DESC, nama) AS row_num, RANK() OVER (ORDER BY gaji DESC) AS rnk, DENSE_RANK() OVER (ORDER BY gaji DESC) AS dense_rnk FROM karyawan ORDER BY gaji DESC, nama;
Jawaban Singkat
Ketiganya memberi nomor urut, bedanya ada di cara menangani nilai yang seri. ROW_NUMBER selalu unik (1, 2, 3, ...) walaupun nilainya sama. RANK memberi angka yang sama untuk yang seri lalu melompat (1, 2, 2, 4). DENSE_RANK juga memberi angka sama untuk yang seri tapi tidak melompat (1, 2, 2, 3).
Pembahasan
Urutkan dulu gajinya dari besar ke kecil: 15 juta (Fajar), 12 juta (Andi, Citra), 9,5 juta (Budi, Eko, Gita), 8 juta (Dewi). Ada dua kelompok seri.
ROW_NUMBER: tinggal nomori 1 sampai 7. Di query ini adanamasebagai kunci kedua, jadi di antara yang seri, nama yang abjadnya lebih dulu dapat nomor lebih kecil (Andi 2, Citra 3). Tanpanama, urutan di antara yang seri bebas dipilih database dan bisa berubah tiap dijalankan.RANK: Fajar 1. Andi dan Citra sama-sama 2. Baris berikutnya adalah baris ke-4, jadi Budi, Eko, Gita dapat 4 (angka 3 dilewati). Dewi adalah baris ke-7, jadi dapat 7. Rumus gampangnya:RANK= 1 + jumlah baris yang nilainya lebih besar.DENSE_RANK: Fajar 1, Andi dan Citra 2, Budi, Eko, Gita 3, Dewi 4. Rumusnya:DENSE_RANK= 1 + jumlah nilai berbeda yang lebih besar.
Perhatikan nilai terakhir tiap kolom: ROW_NUMBER dan RANK sama-sama berakhir di 7 (jumlah baris), sedangkan DENSE_RANK berakhir di 4 (jumlah nilai gaji yang berbeda).
Kapan pakai yang mana:
| Kebutuhan | Fungsi |
|---|---|
| Ambil tepat 1 baris per grup (dedup, data terbaru) | ROW_NUMBER |
| Peringkat lomba, yang seri berbagi posisi dan posisi berikutnya dilewati | RANK |
| "Nilai tertinggi ke-N" (gaji tertinggi kedua, ketiga) | DENSE_RANK |
Query
SELECT nama, gaji, ROW_NUMBER() OVER (ORDER BY gaji DESC, nama) AS row_num, RANK() OVER (ORDER BY gaji DESC) AS rnk, DENSE_RANK() OVER (ORDER BY gaji DESC) AS dense_rnk FROM karyawan ORDER BY gaji DESC, nama;
Contoh Output
| nama | gaji | row_num | rnk | dense_rnk |
|---|---|---|---|---|
| Fajar | 15000000 | 1 | 1 | 1 |
| Andi | 12000000 | 2 | 2 | 2 |
| Citra | 12000000 | 3 | 2 | 2 |
| Budi | 9500000 | 4 | 4 | 3 |
| Eko | 9500000 | 5 | 4 | 3 |
| Gita | 9500000 | 6 | 4 | 3 |
| Dewi | 8000000 | 7 | 7 | 4 |
Jebakan Umum
- Menebak
RANKuntuk Budi sebagai 3.RANKmelompat setelah seri, jadi angkanya 4. - Mengira
ROW_NUMBERdi antara baris seri itu pasti urut sesuai tabel. Tanpa kunci kedua, urutannya tidak dijamin. - Memakai
ROW_NUMBERuntuk soal "gaji tertinggi kedua". Kalau dua orang sama-sama bergaji tertinggi,ROW_NUMBER = 2malah mengembalikan gaji tertinggi. - Lupa
DESC. DefaultORDER BYadalah ascending, jadi gaji terkecil yang dapat peringkat 1.
Follow-up dari Interviewer
- "Kalau ada gaji NULL, dapat peringkat berapa?" Tergantung urutan NULL di engine. Di PostgreSQL, NULL dianggap paling besar, jadi dengan
DESCNULL muncul paling atas dan dapat peringkat 1. DuckDB menaruh NULL di akhir untuk ASC maupun DESC. TulisORDER BY gaji DESC NULLS LASTsupaya hasilnya sama di semua engine. - "Mau peringkat per departemen?" Tambahkan
PARTITION BY departemendi dalamOVER (...). Penomoran mulai lagi dari 1 di tiap departemen. - "Ada fungsi ranking lain?"
PERCENT_RANK()(posisi relatif 0 sampai 1),CUME_DIST()(porsi baris yang nilainya kurang dari atau sama dengan), danNTILE(n)(bagi ke n kelompok).
Yang Sebenarnya Diuji
Apakah kamu paham perilaku ketiga fungsi ranking saat ada seri, dan bisa memilih fungsi yang tepat untuk tiap kebutuhan. Ini fondasi untuk soal top-N, dedup, dan "nilai ke-N".
Catatan Dialect
- Ketiga fungsi tersedia dengan sintaks sama di DuckDB, PostgreSQL, BigQuery, dan MySQL 8.0 ke atas. MySQL 5.7 belum punya window function.
- Urutan NULL default berbeda: PostgreSQL menganggap NULL paling besar, MySQL dan BigQuery menganggap NULL paling kecil, DuckDB selalu menaruh NULL di akhir. Tulis
NULLS FIRST/LASTsecara eksplisit (MySQL belum mendukungnya, pakaiORDER BY gaji IS NULL, gaji DESC).