Running Max dan Min dengan Window Function SQL
TL;DR
Running max dan running min di SQL dihitung pakai window function MAX() OVER (ORDER BY ...) dan MIN() OVER (ORDER BY ...). Fungsi ini ngasih nilai tertinggi atau terendah dari baris pertama sampai baris saat ini, jadi kamu bisa lacak rekor berjalan tanpa self-join. Tambahin PARTITION BY buat menghitung rekor terpisah per grup seperti per cabang atau per produk.
Running max dan running min di SQL dihitung pakai window function MAX() OVER (ORDER BY ...) dan MIN() OVER (ORDER BY ...), yang ngasih nilai tertinggi atau terendah dari baris pertama sampai baris saat ini.
Ini berguna banget buat lacak rekor. Rekor omzet harian tertinggi sepanjang tahun, harga saham terendah sampai hari ini, atau suhu maksimum yang pernah tercatat. Tanpa window function, kamu bakal ribet pakai subquery atau self-join.
Di tutorial ini kamu bakal belajar sintaks running max dan min dari dasar, cara pakai frame clause, dan cara reset per grup pakai PARTITION BY. Semua pakai contoh dataset toko Indonesia.
Apa itu running max dan running min di SQL?
Running max adalah nilai tertinggi yang tercatat dari baris pertama sampai baris saat ini. Running min kebalikannya, nilai terendah sampai titik itu. Beda sama MAX biasa yang ngasih satu angka buat seluruh data, running max ngasih nilai berjalan di tiap baris sambil tetap nampilin semua baris asli.
Contoh gampangnya. Kalau omzet harian toko naik turun, running max nunjukin rekor tertinggi yang pernah dicapai sampai tiap tanggal. Begitu ada hari yang lebih tinggi dari rekor lama, running max-nya naik. Kalau gak, dia bertahan.
Ini termasuk keluarga window function, kelompok fungsi SQL yang ngitung sesuatu di sekumpulan baris tanpa nge-grup datanya jadi satu.
Gimana sintaks window function buat running max?
Sintaks dasarnya taruh MAX(kolom) lalu tambahin OVER (ORDER BY kolom_urutan). Bagian ORDER BY di dalam OVER yang bikin MAX-nya jadi berjalan, karena dia nentuin urutan baris dihitung dari mana ke mana.
SELECT
tgl,
omzet,
MAX(omzet) OVER (ORDER BY tgl) AS rekor_tertinggi,
MIN(omzet) OVER (ORDER BY tgl) AS rekor_terendah
FROM penjualan_harian
ORDER BY tgl;
Hasilnya tiap baris nampilin omzet hari itu, plus rekor tertinggi dan terendah sampai tanggal tersebut. Perhatiin jumlah barisnya gak berkurang, beda sama kalau kamu pakai GROUP BY.
| tgl | omzet | rekor_tertinggi | rekor_terendah |
|---|---|---|---|
| 01 Des | 2.400.000 | 2.400.000 | 2.400.000 |
| 02 Des | 1.750.000 | 2.400.000 | 1.750.000 |
| 03 Des | 3.100.000 | 3.100.000 | 1.750.000 |
| 04 Des | 1.200.000 | 3.100.000 | 1.200.000 |
Lihat kolom rekor_tertinggi di baris 03 Des naik jadi 3,1 juta karena hari itu pecah rekor. Di 04 Des dia bertahan walau omzetnya turun.
Gimana cara kerja frame clause?
Waktu kamu pakai ORDER BY di dalam OVER, SQL diam-diam nambahin frame default: ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Artinya window-nya ngitung dari baris pertama sampai baris saat ini. Inilah yang bikin MAX jadi running max.
MAX(omzet) OVER (
ORDER BY tgl
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS rekor_tertinggi
Query di atas hasilnya sama persis dengan versi tanpa frame, karena itu memang default-nya. Tapi nulis eksplisit bikin maksudmu jelas.
Kalau kamu mau moving max, misalnya nilai tertinggi dari 3 hari terakhir aja, tinggal ubah frame-nya:
MAX(omzet) OVER (
ORDER BY tgl
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS max_3_hari
Sekarang window-nya cuma nengok 3 baris terakhir, bukan dari awal. Frame clause ini yang ngasih window function keluwesan besar.
Contoh kasus: melacak rekor omzet toko_berkah
Ambil dataset toko_berkah dengan 90 hari data omzet harian. Aku mau tau kapan aja toko ini pecah rekor omzet, dan berapa rekornya di tiap titik. Query running max langsung ngejawab.
SELECT
tgl,
omzet,
MAX(omzet) OVER (ORDER BY tgl) AS rekor_berjalan,
CASE
WHEN omzet = MAX(omzet) OVER (ORDER BY tgl)
THEN 'REKOR BARU'
ELSE ''
END AS status
FROM penjualan_harian
ORDER BY tgl;
Kolom CASE nandain hari yang pecah rekor. Dari 90 hari data, ternyata cuma 7 hari yang pecah rekor omzet, dan 4 di antaranya jatuh di minggu menjelang Lebaran. Insight kayak gini susah dapet tanpa running max.
Angka ini juga ngasih tau kapan momentum penjualan lagi kuat. Kalau rekor berjalan mandek berminggu-minggu, itu sinyal penjualan lagi stagnan.
Running max per grup dengan PARTITION BY
Kalau toko_berkah punya beberapa cabang, kamu pasti mau rekor terpisah per cabang. Tambahin PARTITION BY cabang di dalam OVER. Hitungan running max bakal reset tiap ganti cabang.
SELECT
cabang,
tgl,
omzet,
MAX(omzet) OVER (
PARTITION BY cabang
ORDER BY tgl
) AS rekor_cabang
FROM penjualan_harian
ORDER BY cabang, tgl;
Sekarang tiap cabang punya rekor berjalannya sendiri. Cabang Bekasi gak nyampur sama cabang Depok. Ini cara paling bersih buat bandingin performa antar toko tanpa nulis query terpisah buat masing-masing.
Buat teknik window function lain yang mirip, baca cara hitung running total di SQL.
Kesalahan umum pakai running max dan min
Window function powerful tapi ada beberapa jebakan yang sering bikin hasil salah.
- Lupa ORDER BY di dalam OVER. Tanpa ORDER BY, MAX OVER ngasih nilai tertinggi seluruh tabel di tiap baris, bukan rekor berjalan.
- Nyampur GROUP BY dan window function tanpa paham bedanya. GROUP BY nyusutin baris, window function nggak. Pilih sesuai kebutuhan.
- Lupa PARTITION BY pas butuh per grup. Tanpa PARTITION BY, rekornya kecampur antar cabang atau produk.
- Pakai di MySQL versi lama. Window function baru ada di MySQL 8. Versi 5.7 ke bawah bakal error.
Kalau kamu masih baru di window function, mulai dari pengenalan window function SQL dulu biar konsepnya nyantol.
FAQ
Pertanyaan yang sering muncul soal running max dan min di SQL.
Penutup
Running max dan running min bikin kamu bisa lacak rekor tertinggi dan terendah berjalan cuma dengan satu window function, tanpa self-join yang ribet. Kuncinya ORDER BY di dalam OVER, plus PARTITION BY kalau butuh per grup.
Ingat frame default-nya dari baris pertama sampai baris saat ini. Ubah frame-nya kalau kamu mau moving max yang cuma nengok beberapa baris terakhir.
Mau latihan window function pakai data nyata? Editor SQL interaktif ada di NgulikSQL, dan lanjut baca panduan running total buat teknik sejenis. Referensi resmi sintaksnya ada di dokumentasi window function PostgreSQL.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel terkait
Fungsi NTH_VALUE di SQL untuk Ambil Nilai ke-N (2026)
NTH_VALUE ngambil nilai baris ke-N dalam window, tapi frame default-nya sering bikin hasil NULL. Ini sintaksnya, cara benerin frame, plus contoh transaksi toko.
Analisa Afinitas Produk (Cross-Sell) dengan SQL
Produk apa yang sering dibeli bareng? Analisa afinitas produk pakai SQL self JOIN jawab itu. Ini cara hitung support, confidence, dan lift buat strategi cross-sell.
First Touch vs Last Touch Attribution di SQL
Channel mana yang dapat kredit konversi, yang pertama atau terakhir? Ini cara hitung first touch dan last touch attribution di SQL pakai window function.