Fungsi window menghitung atas sekumpulan baris yang berdekatan tanpa
menyusutkan barisnya, sehingga setiap baris membawa angka agregat tetangganya
sebagaimana groupby yang tidak memotong. Topik ini menerapkan tiga fungsi
window yang paling sering dipakai analisis, yaitu RANK untuk peringkat, SUM
berjalan untuk kumulatif, dan LAG untuk selisih terhadap baris sebelumnya,
semuanya di atas database latihan analitik. Setelah membacanya, tiga
pertanyaan analisis klasik itu dijawab dalam satu query tanpa subquery
bersarang.
OVER: agregasi tanpa menyusutkan#
Kata OVER membedakan fungsi window dari agregat biasa. Agregat dengan
GROUP BY menyusutkan setiap kelompok menjadi satu baris, sedangkan fungsi
window menambahkan satu kolom pada setiap baris aslinya, dan jendelanya
didefinisikan dengan PARTITION BY untuk membagi baris, ORDER BY untuk
mengurutkan di dalam jendela, atau keduanya.
Perilaku ganda ini adalah sumber nilainya, yaitu baris yang menunjukkan
pengukuran dan kolom yang menunjukkan posisinya di antara tetangganya
dibaca dalam satu tabel. Pada pandas, padanan terdekatnya adalah
transform dan rank, dan pola keduanya identik dengan hasil yang sama.
Latihan topik ini memakai tabel fakta_ukur enam barisnya dari database
latihan yang sama dengan dua topik sebelumnya, dan skrip penyiapannya
disertakan agar setiap query dapat dijalankan berdiri sendiri.
RANK untuk peringkat#
rank() memberi peringkat di dalam jendela, dan PARTITION BY menjadikan
peringkat itu berulang per kelompok, sedangkan ORDER BY di dalam OVER
menyatakan arah urutannya. Kombinasi keduanya menjawab pertanyaan “pengukuran
mana yang tertinggi pada masing-masing stasiun” tanpa satu subquery pun.
# siapkan ulang database latihan agar topik dapat dijalankan sendiri
psql -h /home/kal/my-project/pgsock -U postgres -c "DROP DATABASE IF EXISTS analitik"
psql -h /home/kal/my-project/pgsock -U postgres -c "CREATE DATABASE analitik"
psql -h /home/kal/my-project/pgsock -U postgres -d analitik -c "CREATE TABLE dim_stasiun (id_stasiun serial PRIMARY KEY, nama text NOT NULL, wilayah text NOT NULL)"
psql -h /home/kal/my-project/pgsock -U postgres -d analitik -c "INSERT INTO dim_stasiun (nama, wilayah) VALUES ('Stasiun A','Kawasan Sekolah'), ('Stasiun B','Jalan Utama')"
psql -h /home/kal/my-project/pgsock -U postgres -d analitik -c "CREATE TABLE fakta_ukur (id serial PRIMARY KEY, waktu timestamp NOT NULL, id_stasiun integer REFERENCES dim_stasiun, pm25 numeric NOT NULL, suhu_c numeric, kelembapan numeric)"
psql -h /home/kal/my-project/pgsock -U postgres -d analitik -c "INSERT INTO fakta_ukur (waktu, id_stasiun, pm25, suhu_c, kelembapan) VALUES ('2026-03-01 07:00',1,38.2,25.6,71.0), ('2026-03-01 08:00',1,44.9,26.8,69.4), ('2026-03-01 09:00',1,41.3,27.9,68.7), ('2026-03-01 07:00',2,58.7,24.9,72.3), ('2026-03-01 08:00',2,66.1,25.8,70.9), ('2026-03-01 09:00',2,61.4,26.7,71.5)"DROP DATABASE
CREATE DATABASE
CREATE TABLE
INSERT 0 2
CREATE TABLE
INSERT 0 6# peringkat pengukuran di dalam tiap stasiun
psql -h /home/kal/my-project/pgsock -U postgres -d analitik -c "SELECT s.nama, f.waktu, f.pm25, rank() OVER (PARTITION BY s.nama ORDER BY f.pm25 DESC) AS peringkat FROM fakta_ukur f JOIN dim_stasiun s ON f.id_stasiun = s.id_stasiun ORDER BY s.nama, peringkat" nama | waktu | pm25 | peringkat
-----------+---------------------+------+-----------
Stasiun A | 2026-03-01 08:00:00 | 44.9 | 1
Stasiun A | 2026-03-01 09:00:00 | 41.3 | 2
Stasiun A | 2026-03-01 07:00:00 | 38.2 | 3
Stasiun B | 2026-03-01 08:00:00 | 66.1 | 1
Stasiun B | 2026-03-01 09:00:00 | 61.4 | 2
Stasiun B | 2026-03-01 07:00:00 | 58.7 | 3
(6 rows)Enam baris tetap enam, dan kolom peringkat berulang dari satu pada setiap
stasiun karena PARTITION BY-nya. Perbedaan rank() dengan dense_rank() dan
row_number() muncul saat ada nilai yang sama, yaitu rank melompati
peringkat yang tersangkut, dense_rank tidak melompat, dan row_number
memberi nomor unik tanpa peduli seri.
SUM berjalan untuk kumulatif#
sum() OVER (ORDER BY waktu) menghitung total kumulatif, yaitu jendela yang
membentang dari awal sampai baris saat ini, dan inilah bentuk paling umum
dari metrik berjalan seperti akumulasi emisi atau total transaksi.
# total berjalan PM2.5 Stasiun A
psql -h /home/kal/my-project/pgsock -U postgres -d analitik -c "SELECT waktu, pm25, sum(pm25) OVER (ORDER BY waktu) AS kumulatif FROM fakta_ukur WHERE id_stasiun = 1 ORDER BY waktu" waktu | pm25 | kumulatif
---------------------+------+-----------
2026-03-01 07:00:00 | 38.2 | 38.2
2026-03-01 08:00:00 | 44.9 | 83.1
2026-03-01 09:00:00 | 41.3 | 124.4
(3 rows)Kolom kumulatif menambah pm25 baris saat ini ke total sebelumnya, dan
jendelanya dapat diberi batas eksplisit dengan ROWS BETWEEN bila rata-rata
bergulir yang dimaksud, misalnya tiga baris terakhir. Waktu yang sama pada
beberapa baris menuntut kehati-hatian tambahan pada ORDER BY, karena urutan
yang tidak pasti membuat kumulatifnya ikut tidak pasti.
LAG untuk selisih antar baris#
lag() membaca nilai dari baris sebelumnya di dalam jendela, sehingga
selisih antar pengukuran menjadi satu ekspresi sederhana, dan baris pertama
yang tidak punya tetangga berisi NULL secara jujur.
# perubahan PM2.5 terhadap jam sebelumnya
psql -h /home/kal/my-project/pgsock -U postgres -d analitik -c "SELECT waktu, pm25, pm25 - lag(pm25) OVER (ORDER BY waktu) AS selisih FROM fakta_ukur WHERE id_stasiun = 1 ORDER BY waktu" waktu | pm25 | selisih
---------------------+------+---------
2026-03-01 07:00:00 | 38.2 |
2026-03-01 08:00:00 | 44.9 | 6.7
2026-03-01 09:00:00 | 41.3 | -3.6
(3 rows)lead() adalah kembarannya yang membaca baris berikutnya, dan NULL pada
tepi jendela adalah perilaku bawaan yang jujur, dengan penggantinya dapat
dinyatakan sebagai argumen ketiga bila diperlukan. Selisih antar baris inilah
bahan dasar deteksi lonjakan, dan ambang pada selisihnya dipilih dari domain
data sebagaimana pemeriksaan rentang pada modul pembersihan.
Kesalahan umum#
Kesalahan pertama adalah lupa ORDER BY pada jendela yang berurutan, seperti SUM berjalan dan LAG. Tanpa urutan, hasilnya tidak terdefinisi dan angkanya dapat berubah antar eksekusi, sehingga ORDER BY waktu selalu menyertainya.
Kesalahan kedua adalah menggabungkan fungsi window dengan GROUP BY tanpa memikirkan urutan evaluasi. Window dihitung setelah GROUP BY, sehingga jendelanya beroperasi atas hasil agregasi, dan kebingungan granularitas yang menyusulnya dibereskan dengan menulis tahapannya sebagai subquery atau CTE.
Kesalahan ketiga adalah menyaring hasil window pada WHERE. Saringan dijalankan sebelum window dihitung, sedangkan syarat atas hasilnya ditaruh pada subquery luar, dan pesan error kolom tidak ada menunjuk persis keadaan itu.
Ringkasan#
OVERmenghitung atas jendela baris tanpa menyusutkannya, dengan PARTITION BY membagi dan ORDER BY mengurutkan di dalam jendela.rank()dengan PARTITION BY memberi peringkat per kelompok, dan varian rank, dense_rank, serta row_number berbeda pada nilai yang seri.sum() OVER (ORDER BY ...)menghasilkan total berjalan, danROWS BETWEENmembentuk rata-rata bergulir.lag()danlead()membaca baris tetangga untuk selisih antar pengukuran, dengan NULL yang jujur di tepi jendela.
Referensi#
- Window Functions — PostgreSQL Documentation — tutorial resmi fungsi window beserta contohnya.
- Window Function Calls — PostgreSQL Documentation — rujukan resmi definisi jendela dan bingkainya.