Pengantar Fungsi Jendela SQL

Diterbitkan: 2022-03-11

Fitur yang sangat kuat yang Anda sukai untuk dibenci (tetapi perlu diketahui)

Fungsi jendela SQL menyediakan beberapa fitur yang sangat kuat dan berguna. Tetapi bagi banyak orang, karena mereka sangat asing dengan SQL standar, mereka sulit dipelajari dan dipahami, memiliki sintaks yang aneh—dan sangat sering dihindari.

Fungsi jendela dapat dijelaskan secara sederhana sebagai fungsi kalkulasi yang mirip dengan agregasi, tetapi jika agregasi normal melalui klausa GROUP BY digabungkan kemudian menyembunyikan baris individual yang diagregasi, fungsi jendela memiliki akses ke baris individual dan dapat menambahkan beberapa atribut dari baris tersebut ke dalam hasil yang ditetapkan.

Diagram membandingkan fungsi agregat dan fungsi jendela

Dalam tutorial fungsi jendela SQL ini, saya akan membantu Anda memulai dengan fungsi jendela, menjelaskan manfaat dan kapan Anda akan menggunakannya, dan memberi Anda contoh nyata untuk membantu konsep tersebut.

Jendela ke Data Anda

Salah satu fitur yang paling sering digunakan dan penting dalam SQL adalah kemampuan untuk menggabungkan atau mengelompokkan baris data dengan cara tertentu. Namun, dalam beberapa kasus, pengelompokan bisa menjadi sangat kompleks, tergantung pada apa yang diperlukan.

Pernahkah Anda ingin mengulang hasil kueri Anda untuk mendapatkan peringkat, daftar x teratas, atau serupa? Pernahkah Anda memiliki proyek analitik di mana Anda ingin menyiapkan data Anda tepat untuk alat visualisasi, tetapi merasa hampir tidak mungkin atau begitu rumit sehingga tidak sepadan?

Fungsi jendela dapat membuat segalanya lebih mudah. Setelah Anda mendapatkan hasil kueri Anda—yaitu, setelah klausa WHERE dan agregasi standar apa pun, fungsi jendela akan bertindak pada baris yang tersisa ( jendela data) dan memberikan apa yang Anda inginkan.

Beberapa fungsi jendela yang akan kita lihat meliputi:

  • OVER
  • COUNT()
  • SUM()
  • ROW_NUMBER()
  • RANK()
  • DENSE_RANK()
  • LEAD()
  • LAG()

Terlalu mudah

Klausa OVER adalah yang menentukan fungsi jendela dan harus selalu disertakan dalam pernyataan. Default dalam klausa OVER adalah seluruh rowset. Sebagai contoh, mari kita lihat tabel karyawan di database perusahaan dan tunjukkan jumlah total karyawan di setiap baris, beserta info setiap karyawan, termasuk saat mereka mulai bekerja di perusahaan.

 SELECT COUNT(*) OVER() As NumEmployees, firstname, lastname, date_started FROM Employee ORDER BY date_started;
JumlahKaryawan nama depan nama keluarga tanggal dimulai
3 John Smith 01-01-2019 00:00:00.000
3 Sally jones 2019-02-15 00:00:00.000
3 sama Gordon 2019-02-18 00:00:00.000

Di atas, seperti banyak fungsi jendela, juga dapat ditulis dengan cara non-jendela yang lebih familiar—yang, dalam contoh sederhana ini, tidak terlalu buruk:

 SELECT (SELECT COUNT(*) FROM Employee) as NumEmployees, firstname, lastname, date_started FROM Employee ORDER BY date_started;

Tapi sekarang, katakanlah kita ingin menunjukkan jumlah karyawan yang memulai di bulan yang sama dengan karyawan di baris. Kita perlu mempersempit atau membatasi jumlah hanya pada bulan itu untuk setiap baris. Bagaimana itu dilakukan? Kami menggunakan klausa jendela PARTITION , seperti:

 SELECT COUNT(*) OVER (PARTITION BY MONTH(date_started),YEAR(date_started)) As NumPerMonth, DATENAME(month,date_started)+' '+DATENAME(year,date_started) As TheMonth, firstname, lastname FROM Employee ORDER BY date_started;
JumlahPerBulan Bulan Nama depan Nama keluarga
1 Januari 2019 John Smith
2 Februari 2019 Sally jones
2 Februari 2019 sama Gordon

Partisi memungkinkan Anda untuk memfilter jendela menjadi beberapa bagian dengan nilai atau nilai tertentu. Setiap bagian sering disebut bingkai jendela.

Untuk melangkah lebih jauh, katakanlah kami tidak hanya ingin mengetahui berapa banyak karyawan yang memulai di bulan yang sama, tetapi kami ingin menunjukkan dalam urutan mana mereka memulai bulan itu. Untuk itu, kita dapat menggunakan klausa ORDER BY yang sudah dikenal. Namun, dalam fungsi jendela, ORDER BY bertindak sedikit berbeda dari yang dilakukannya di akhir kueri.

 SELECT COUNT(*) OVER (PARTITION BY MONTH(date_started), YEAR(date_started) ORDER BY date_started) As NumThisMonth, DATENAME(month,date_started)+' '+DATENAME(year,date_started) As TheMonth, firstname, lastname, date_started FROM Employee ORDER BY date_started;
AngkaBulan Ini Bulan Nama depan nama keluarga
1 Januari 2019 John Smith
1 Februari 2019 Sally jones
2 Februari 2019 sama Gordon

Dalam hal ini, ORDER BY memodifikasi jendela sehingga dimulai dari awal partisi (dalam hal ini bulan dan tahun saat karyawan memulai) ke baris saat ini. Dengan demikian, penghitungan dimulai ulang di setiap partisi.

Peringkat itu

Fungsi jendela bisa sangat berguna untuk tujuan peringkat. Sebelumnya kami melihat bahwa menggunakan fungsi COUNT agregasi memungkinkan kami untuk melihat dalam urutan apa Karyawan bergabung dengan perusahaan. Kita juga bisa menggunakan fungsi peringkat jendela, seperti ROW_NUMBER() , RANK() , dan DENSE_RANK() .

Perbedaannya bisa dilihat setelah kita menambah pegawai baru pada bulan berikutnya, dan menghapus partisi tersebut:

 SELECT ROW_NUMBER() OVER (ORDER BY YEAR(date_started),MONTH(date_started)) As StartingRank, RANK() OVER (ORDER BY YEAR(date_started),MONTH(date_started)) As EmployeeRank, DENSE_RANK() OVER (ORDER BY YEAR(date_started),MONTH(date_started)) As DenseRank, DATENAME(month,date_started)+' '+DATENAME(year,date_started) As TheMonth, firstname, lastname, date_started FROM Employee ORDER BY date_started;
Peringkat Awal Peringkat Karyawan Peringkat Padat Bulan nama depan nama keluarga tanggal dimulai
1 1 1 Januari 2019 John Smith 01-01-2019
2 2 2 Februari 2019 Sally jones 2019-02-15
3 2 2 Februari 2019 sama Gordon 2019-02-18
4 4 3 Maret 2019 Julie Sanchez 2019-03-19

Anda dapat melihat perbedaannya. ROW_NUMBER() memberikan hitungan berurutan dalam partisi yang diberikan (tetapi dengan tidak adanya partisi, ia melewati semua baris). RANK() memberikan peringkat setiap baris berdasarkan klausa ORDER BY . Ini menunjukkan ikatan, dan kemudian melompati peringkat berikutnya. DENSE_RANK juga menunjukkan ikatan, tetapi kemudian berlanjut dengan nilai berurutan berikutnya seolah-olah tidak ada ikatan.

Fungsi peringkat lainnya termasuk:

  • CUME_DIST – Menghitung peringkat relatif dari baris saat ini dalam sebuah partisi
  • NTILE – Membagi baris untuk setiap partisi jendela secara merata
  • PERCENT_RANK – Persen peringkat dari baris saat ini

Perhatikan juga dalam contoh ini bahwa Anda dapat memiliki beberapa fungsi Window dalam satu kueri—dan baik partisi maupun urutannya dapat berbeda di masing-masing!

Baris dan Rentang dan Bingkai, Oh My

Untuk lebih menentukan atau membatasi bingkai jendela Anda dalam klausa OVER() , Anda dapat menggunakan ROWS dan RANGE . Dengan klausa ROWS , Anda dapat menentukan baris yang disertakan dalam partisi Anda seperti baris sebelumnya atau setelah baris saat ini.

 SELECT OrderYear, OrderMonth, TotalDue, SUM(TotalDue) OVER(ORDER BY OrderYear, OrderMonth ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS 'LaggingRunningTotal' FROM sales_products;

Dalam contoh ini, bingkai jendela beralih dari baris pertama ke baris saat ini dikurangi 1, dan ukuran jendela terus meningkat untuk setiap baris.

Rentang bekerja sedikit berbeda dan kami mungkin mendapatkan hasil yang berbeda.

 SELECT OrderYear, OrderMonth, TotalDue, SUM(TotalDue) OVER(ORDER BY OrderYear, OrderMonth RANGE BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS 'LaggingRunningTotal' FROM sales_products;

Rentang akan menyertakan baris dalam bingkai jendela yang memiliki nilai ORDER BY yang sama dengan baris saat ini. Dengan demikian, Anda mungkin bisa mendapatkan duplikat dengan RANGE jika ORDER BY tidak unik.

Beberapa menggambarkan ROWS sebagai operator fisik sementara RANGE adalah operator logis. Bagaimanapun, nilai default untuk ROWS dan RANGE selalu UNBOUNDED PRECEDING AND CURRENT ROW .

Apa lagi?

Sebagian besar fungsi agregat standar bekerja dengan fungsi Window. Kami telah melihat COUNT dalam contoh. Lainnya termasuk SUM , AVG , MIN , MAX , dll.

Dengan fungsi jendela, Anda juga dapat mengakses catatan sebelumnya dan catatan berikutnya menggunakan LAG dan LEAD , dan FIRST_VALUE dan LAST_VALUE . Misalnya, katakanlah Anda ingin menunjukkan pada setiap baris angka penjualan untuk bulan ini, dan perbedaan antara angka penjualan bulan lalu. Anda mungkin melakukan sesuatu seperti ini:

 SELECT id, OrderMonth, OrderYear, product, sales, sales - LAG(sales,1) OVER (PARTITION BY product ORDER BY OrderYear, OrderMonth) As sales_change FROM sales_products WHERE sale_year = 2019;

Pada dasarnya, Fungsi SQL Window Sangat Kuat

Meskipun ini adalah pengantar singkat untuk fungsi jendela SQL, semoga ini akan memicu minat Anda untuk melihat semua yang dapat mereka lakukan. Kami mempelajari bahwa fungsi jendela melakukan perhitungan yang mirip dengan cara fungsi agregasi, tetapi dengan manfaat tambahan bahwa mereka memiliki akses ke data dalam baris individual, yang membuatnya cukup kuat. Mereka selalu berisi klausa OVER , dan mungkin berisi PARTITION BY , ORDER BY , dan sejumlah agregasi ( SUM , COUNT , dll.) dan fungsi posisi lainnya ( LEAD , LAG ). Kami juga belajar tentang bingkai jendela dan bagaimana mereka merangkum bagian data.

Perhatikan bahwa rasa SQL yang berbeda dapat mengimplementasikan fungsi jendela secara berbeda, dan beberapa mungkin tidak mengimplementasikan semua fungsi atau klausa jendela. Pastikan untuk memeriksa dokumentasi untuk platform yang Anda gunakan.

Jika, sebagai pengembang SQL, Anda tertarik untuk menyetel kinerja basis data SQL, lihat Penyetelan Kinerja Basis Data SQL untuk Pengembang .

Selamat berjendela!

Untuk informasi lebih lanjut tentang implementasi spesifik, lihat:

  • Dokumentasi Fungsi Jendela PostgreSQL untuk implementasi PostgreSQL.
  • SELECT - OVER Clause (Transact-SQL) dokumen oleh Microsoft.
  • Fungsi Jendela di SQL Server untuk gambaran umum yang bagus tentang implementasi SQL Server, dan bagian 2.