SQL Server memberi kita sejumlah fungsi jendela yang membantu kita melakukan perhitungan di seluruh rangkaian baris, tanpa perlu mengulang panggilan ke database. Berbeda dengan fungsi agregat standar, fungsi jendela tidak akan mengelompokkan baris menjadi satu baris keluaran, mereka akan mengembalikan nilai agregat tunggal untuk setiap baris, menjaga identitas terpisah untuk baris tersebut. Istilah Window di sini tidak berhubungan dengan sistem operasi Microsoft Windows, ini menjelaskan kumpulan baris yang akan diproses oleh fungsi tersebut.
Salah satu jenis fungsi jendela yang paling berguna adalah Fungsi Jendela Pemeringkatan yang digunakan untuk memberi peringkat pada nilai bidang tertentu dan mengkategorikannya menurut peringkat setiap baris, menghasilkan nilai agregat tunggal untuk setiap baris yang berpartisipasi. Ada empat fungsi jendela peringkat yang didukung di SQL Server; ROW_NUMBER(), RANK(), DENSE_RANK(), dan NTILE(). Semua fungsi ini digunakan untuk menghitung ROWID untuk jendela baris yang disediakan dengan caranya sendiri.
Empat fungsi jendela peringkat menggunakan klausa OVER() yang mendefinisikan kumpulan baris yang ditentukan pengguna dalam kumpulan hasil kueri. Dengan mendefinisikan klausa OVER(), Anda juga dapat menyertakan klausa PARTITION BY yang menentukan kumpulan baris yang akan diproses oleh fungsi jendela, dengan menyediakan kolom atau kolom yang dipisahkan koma untuk mendefinisikan partisi. Selain itu, klausa ORDER BY dapat dimasukkan, yang mendefinisikan kriteria penyortiran dalam partisi yang akan dilalui fungsi melalui baris saat memproses.
Dalam artikel ini, kita akan membahas cara menggunakan empat fungsi jendela peringkat:ROW_NUMBER(), RANK(), DENSE_RANK(), dan NTILE() secara praktis, dan perbedaan di antara keduanya.
Untuk menyajikan demo kami, kami akan membuat tabel sederhana baru dan memasukkan beberapa catatan ke dalam tabel menggunakan skrip T-SQL di bawah ini:
CREATE TABLE StudentScore
(
Student_ID INT PRIMARY KEY,
Student_Name NVARCHAR (50),
Student_Score INT
)
GO
INSERT INTO StudentScore VALUES (1,'Ali', 978)
INSERT INTO StudentScore VALUES (2,'Zaid', 770)
INSERT INTO StudentScore VALUES (3,'Mohd', 1140)
INSERT INTO StudentScore VALUES (4,'Jack', 770)
INSERT INTO StudentScore VALUES (5,'John', 1240)
INSERT INTO StudentScore VALUES (6,'Mike', 1140)
INSERT INTO StudentScore VALUES (7,'Goerge', 885)
Anda dapat memeriksa apakah data berhasil dimasukkan menggunakan pernyataan SELECT berikut:
SELECT * FROM StudentScore ORDER BY Student_Score
Dengan menerapkan hasil yang diurutkan, kumpulan hasil adalah sebagai berikut:
ROW_NUMBER()
Fungsi jendela peringkat ROW_NUMBER() mengembalikan nomor urut unik untuk setiap baris dalam partisi jendela yang ditentukan, mulai dari 1 untuk baris pertama di setiap partisi dan tanpa mengulang atau melewatkan angka dalam hasil peringkat setiap partisi. Jika ada nilai duplikat dalam set baris, nomor ID peringkat akan diberikan secara sewenang-wenang. Jika klausa PARTITION BY ditentukan, nomor baris peringkat akan diatur ulang untuk setiap partisi. Pada tabel yang dibuat sebelumnya, query di bawah ini menunjukkan bagaimana menggunakan fungsi jendela peringkat ROW_NUMBER untuk memberi peringkat pada baris tabel StudentScore sesuai dengan skor setiap siswa:
SELECT *, ROW_NUMBER() OVER( ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore
Jelas dari hasil yang ditetapkan di bawah ini bahwa fungsi jendela ROW_NUMBER memberi peringkat pada baris tabel sesuai dengan nilai kolom Student_Score untuk setiap baris, dengan menghasilkan nomor unik dari setiap baris yang mencerminkan peringkat Student_Score-nya mulai dari nomor 1 tanpa duplikat atau celah dan berurusan dengan semua baris sebagai satu partisi. Anda juga dapat melihat bahwa skor duplikat diberikan ke peringkat yang berbeda secara acak:
Jika kita memodifikasi query sebelumnya dengan memasukkan klausa PARTITION BY agar memiliki lebih dari satu partisi, seperti yang ditunjukkan pada query T-SQL di bawah ini:
SELECT *, ROW_NUMBER() OVER(PARTITION BY Student_Score ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore
Hasilnya akan menunjukkan bahwa fungsi jendela ROW_NUMBER akan membuat peringkat baris tabel sesuai dengan nilai kolom Student_Score untuk setiap baris, tetapi akan menangani baris yang memiliki nilai Student_Score yang sama sebagai satu partisi. Anda akan melihat bahwa nomor unik akan dibuat untuk setiap baris yang mencerminkan peringkat Student_Score-nya, mulai dari nomor 1 tanpa duplikat atau celah dalam partisi yang sama, menyetel ulang nomor peringkat saat berpindah ke nilai Student_Score yang berbeda.
Misalnya, siswa dengan skor 770 akan diberi peringkat dalam skor itu dengan menetapkan nomor peringkat untuk itu. Namun, ketika dipindahkan ke siswa dengan skor 885, nomor awal peringkat akan diatur ulang untuk memulai lagi di 1, seperti yang ditunjukkan di bawah ini:
RANK()
Fungsi jendela peringkat RANK() mengembalikan nomor peringkat unik untuk setiap baris berbeda dalam partisi sesuai dengan nilai kolom yang ditentukan, mulai dari 1 untuk baris pertama di setiap partisi, dengan peringkat yang sama untuk nilai duplikat dan meninggalkan celah di antara peringkat; celah ini muncul dalam urutan setelah nilai duplikat. Dengan kata lain, fungsi jendela peringkat RANK() berperilaku seperti fungsi ROW_NUMBER() kecuali untuk baris dengan nilai yang sama, di mana ia akan diberi peringkat dengan ID peringkat yang sama dan menghasilkan celah setelahnya. Jika kita memodifikasi kueri peringkat sebelumnya untuk menggunakan fungsi peringkat RANK():
SELECT *, RANK () OVER( ORDER BY Student_Score) AS RankRank
FROM StudentScore
Anda akan melihat dari hasil bahwa fungsi jendela RANK akan mengurutkan baris tabel sesuai dengan nilai kolom Student_Score untuk setiap baris, dengan nilai peringkat yang mencerminkan Student_Score dimulai dari angka 1, dan memberi peringkat pada baris yang memiliki Student_Score yang sama dengan nilai peringkat yang sama. Anda juga dapat melihat bahwa dua baris yang memiliki Student_Score sama dengan 770 diberi peringkat dengan nilai yang sama, meninggalkan celah, yaitu nomor 2 yang terlewat, setelah baris peringkat kedua. Hal yang sama terjadi pada baris di mana Student_Score sama dengan 1140 yang diberi peringkat dengan nilai yang sama, meninggalkan celah, yaitu angka 6 yang hilang, setelah baris kedua, seperti yang ditunjukkan di bawah ini:
Memodifikasi kueri sebelumnya dengan memasukkan klausa PARTITION BY agar memiliki lebih dari satu partisi, seperti yang ditunjukkan pada kueri T-SQL di bawah ini:
SELECT *, RANK() OVER(PARTITION BY Student_Score ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore
Hasil pemeringkatan tidak akan ada artinya, karena pemeringkatan akan dilakukan sesuai dengan nilai Student_Score per setiap partisi, dan data akan dipartisi sesuai dengan nilai Student_Score. Dan karena setiap partisi akan memiliki baris dengan nilai Student_Score yang sama, baris dengan nilai Student_Score yang sama di partisi yang sama akan diberi peringkat dengan nilai sama dengan 1. Jadi, ketika pindah ke partisi kedua, peringkat akan direset, dimulai lagi dengan nomor 1, memiliki semua nilai peringkat sama dengan 1 seperti yang ditunjukkan di bawah ini:
DENSE_RANK()
Fungsi jendela peringkat DENSE_RANK() mirip dengan fungsi RANK() dengan menghasilkan nomor peringkat unik untuk setiap baris berbeda dalam partisi sesuai dengan nilai kolom yang ditentukan, mulai dari 1 untuk baris pertama di setiap partisi, memberi peringkat baris dengan nilai yang sama dengan nomor rangking yang sama, kecuali bahwa itu tidak melewati peringkat apa pun, tanpa meninggalkan celah di antara peringkat.
Jika kita menulis ulang kueri peringkat sebelumnya untuk menggunakan fungsi peringkat DENSE_RANK():
Sekali lagi, ubah kueri sebelumnya dengan memasukkan klausa PARTITION BY agar memiliki lebih dari satu partisi, seperti yang ditunjukkan pada kueri T-SQL di bawah ini:
SELECT *, DENSE_RANK() OVER(PARTITION BY Student_Score ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore
Nilai peringkat tidak akan ada artinya, di mana semua baris akan diberi peringkat dengan nilai 1, karena menetapkan nilai duplikat ke nilai peringkat yang sama dan mengatur ulang id awal peringkat saat memproses partisi baru, seperti yang ditunjukkan di bawah ini:
NTILE(N)
Fungsi jendela peringkat NTILE(N) digunakan untuk mendistribusikan baris dalam kumpulan baris ke dalam sejumlah grup tertentu, memberikan setiap baris dalam kumpulan baris dengan nomor grup unik, dimulai dengan nomor 1 yang menunjukkan grup milik baris ini ke, di mana N adalah angka positif, yang menentukan jumlah grup yang Anda perlukan untuk mendistribusikan kumpulan baris.
Dengan kata lain, jika Anda perlu membagi baris data tertentu dari tabel menjadi 3 grup, berdasarkan nilai kolom tertentu, fungsi jendela peringkat NTILE(3) akan membantu Anda mencapainya dengan mudah.
Jumlah baris dalam setiap grup dapat dihitung dengan membagi jumlah baris ke dalam jumlah grup yang diperlukan. Jika kita memodifikasi kueri peringkat sebelumnya untuk menggunakan fungsi jendela peringkat NTILE(4) untuk memberi peringkat tujuh baris tabel menjadi empat grup seperti kueri T-SQL di bawah ini:
SELECT *, NTILE(4) OVER( ORDER BY Student_Score) AS NTILERank
FROM StudentScore
Jumlah baris harus (7/4=1,75) baris ke dalam setiap grup. Menggunakan fungsi NTILE(), SQL Server Engine akan menetapkan 2 baris ke tiga grup pertama dan satu baris ke grup terakhir, agar semua baris disertakan dalam grup, seperti yang ditunjukkan pada hasil yang ditetapkan di bawah ini:
Memodifikasi kueri sebelumnya dengan memasukkan klausa PARTITION BY agar memiliki lebih dari satu partisi, seperti yang ditunjukkan pada kueri T-SQL di bawah ini:
SELECT *, NTILE(4) OVER(PARTITION BY Student_Score ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore
Baris akan didistribusikan ke dalam empat kelompok pada setiap partisi. Misalnya, dua baris pertama dengan Student_Score sama dengan 770 akan berada di partisi yang sama, dan akan didistribusikan dalam grup yang memberi peringkat masing-masing dengan nomor unik, seperti yang ditunjukkan pada hasil yang ditetapkan di bawah ini:
Menggabungkan Semuanya
Agar skenario perbandingan lebih jelas, mari kita potong tabel sebelumnya, tambahkan kriteria klasifikasi lain, yaitu kelas siswa, dan terakhir masukkan tujuh baris baru menggunakan skrip T-SQL di bawah ini:
TRUNCATE TABLE StudentScore
GO
ALTER TABLE StudentScore ADD CLASS CHAR(1)
GO
INSERT INTO StudentScore VALUES (1,'Ali', 978,'A')
INSERT INTO StudentScore VALUES (2,'Zaid', 770,'B')
INSERT INTO StudentScore VALUES (3,'Mohd', 1140,'A')
INSERT INTO StudentScore VALUES (4,'Jack', 879,'B')
INSERT INTO StudentScore VALUES (5,'John', 1240,'C')
INSERT INTO StudentScore VALUES (6,'Mike', 1100,'B')
INSERT INTO StudentScore VALUES (7,'Goerge', 885,'C')
Setelah itu, kami akan memberi peringkat tujuh baris sesuai dengan nilai masing-masing siswa, mempartisi siswa menurut kelasnya. Dengan kata lain, setiap partisi akan mencakup satu kelas, dan setiap kelas siswa akan diberi peringkat sesuai dengan skor mereka dalam kelas yang sama, menggunakan empat fungsi jendela peringkat yang dijelaskan sebelumnya, seperti yang ditunjukkan pada skrip T-SQL di bawah ini:
SELECT *, ROW_NUMBER() OVER(PARTITION BY CLASS ORDER BY Student_Score) AS RowNumberRank,
RANK () OVER(PARTITION BY CLASS ORDER BY Student_Score) AS RankRank,
DENSE_RANK () OVER(PARTITION BY CLASS ORDER BY Student_Score) AS DenseRankRank,
NTILE(7) OVER(PARTITION BY CLASS ORDER BY Student_Score) AS NTILERank
FROM StudentScore
GO
Karena fakta bahwa tidak ada nilai duplikat, empat fungsi jendela peringkat akan bekerja dengan cara yang sama, mengembalikan hasil yang sama, seperti yang ditunjukkan pada hasil yang ditetapkan di bawah ini:
Jika siswa lain termasuk di kelas A dengan skor, yang sudah dimiliki siswa lain di kelas yang sama, gunakan pernyataan INSERT di bawah ini:
INSERT INTO StudentScore VALUES (8,'Faisal', 978,'A')
Tidak ada yang akan berubah untuk fungsi jendela peringkat ROW_NUMBER() dan NTILE(). Fungsi RANK dan DENSE_RANK() akan menetapkan peringkat yang sama untuk siswa dengan skor yang sama, dengan celah di peringkat setelah duplikat peringkat saat menggunakan fungsi RANK dan tidak ada celah di peringkat setelah duplikat peringkat saat menggunakan DENSE_RANK( ), seperti yang ditunjukkan pada hasil di bawah ini:
Skenario Praktis
Fungsi jendela peringkat digunakan secara luas oleh pengembang SQL Server. Salah satu skenario umum untuk penggunaan fungsi peringkat, ketika Anda ingin mengambil baris tertentu dan melewatkan yang lain, menggunakan fungsi jendela peringkat ROW_NUMBER(,) dalam CTE, seperti pada skrip T-SQL di bawah ini yang mengembalikan siswa dengan peringkat antara 2 dan 5 dan lewati yang lain:
WITH ClassRanks AS
(
SELECT *, ROW_NUMBER() OVER( ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore
)
SELECT Student_Name , Student_Score
FROM ClassRanks
WHERE RowNumberRank >= 2 and RowNumberRank <=5
ORDER BY RowNumberRank
Hasilnya akan menunjukkan bahwa hanya siswa dengan peringkat antara 2 dan 5 yang akan dikembalikan:
Mulai dari SQL Server 2012, perintah baru yang berguna, OFFSET FETCH diperkenalkan yang dapat digunakan untuk melakukan tugas sebelumnya yang sama dengan mengambil catatan tertentu dan melewatkan yang lain, menggunakan skrip T-SQL di bawah ini:
WITH ClassRanks AS
(
SELECT *, ROW_NUMBER() OVER( ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore
)
SELECT Student_Name , Student_Score
FROM ClassRanks
ORDER BY
RowNumberRank OFFSET 1 ROWS FETCH NEXT 4 ROWS ONLY;
Mengambil hasil sebelumnya yang sama seperti yang ditunjukkan di bawah ini:
Kesimpulan
SQL Server memberi kami empat fungsi jendela peringkat yang membantu kami memberi peringkat pada baris yang disediakan yang diatur sesuai dengan nilai kolom tertentu. Fungsi-fungsi ini adalah:ROW_NUMBER(), RANK(), DENSE_RANK() dan NTILE(). Semua fungsi peringkat ini melakukan tugas peringkat dengan caranya sendiri, mengembalikan hasil yang sama ketika tidak ada nilai duplikat di baris. Jika ada nilai duplikat dalam kumpulan baris, fungsi RANK akan menetapkan ID peringkat yang sama untuk semua baris dengan nilai yang sama, meninggalkan celah di antara peringkat setelah duplikat. Fungsi DENSE_RANK juga akan menetapkan ID peringkat yang sama untuk semua baris dengan nilai yang sama, tetapi tidak akan meninggalkan celah di antara peringkat setelah duplikat. Kami membahas berbagai skenario dalam artikel ini untuk membahas semua kemungkinan kasus yang membantu Anda memahami fungsi jendela peringkat secara praktis.
Referensi:
- ROW_NUMBER (Transact-SQL)
- RANK (Transact-SQL)
- DENSE_RANK (Transact-SQL)
- NTILE (Transact-SQL)
- Klausul OFFSET FETCH (SQL Server Compact)