TL;DR
Pertumbuhan week over week (WoW) di SQL dihitung dengan ngebandingin nilai minggu ini sama nilai minggu sebelumnya, pakai fungsi window LAG(). Langkahnya: kelompokkan data per minggu pakai DATE_TRUNC('week', tanggal), ambil nilai minggu lalu pakai LAG() OVER (ORDER BY minggu), lalu hitung (sekarang - lalu) / lalu * 100. Hasilnya persen naik-turun antar minggu yang berurutan.
Pertumbuhan week over week di SQL dihitung dengan ngebandingin angka minggu ini sama minggu sebelumnya. Rumusnya (minggu_ini - minggu_lalu) / minggu_lalu * 100, dan alat utamanya fungsi window LAG().
Metrik ini kepakai buat mantau tren jangka pendek. Omzet, jumlah order, user aktif, semua bisa dilihat naik-turunnya per minggu tanpa nunggu akhir bulan.
Aku bakal tunjukin caranya dari nol: agregasi mingguan dulu, baru ambil nilai minggu lalu, terakhir hitung persennya. Semua query di sini pakai sintaks PostgreSQL, tapi logikanya sama di MySQL 8 dan BigQuery.
Week over week (WoW) adalah perbandingan satu metrik antara minggu berjalan dan minggu tepat sebelumnya. Hasilnya berupa persen naik atau turun. Bedanya sama year over year, WoW fokus ke pergerakan mingguan yang lebih peka buat ngedeteksi perubahan cepat, kayak efek promo atau libur.
Buat ngitungnya, kamu butuh dua hal: data yang udah dikelompokkan per minggu, dan cara ngambil nilai minggu sebelumnya di baris yang sama. Yang kedua ini kerjaan LAG(), fungsi window yang ngintip baris sebelumnya. Konsep dasarnya aku bahas di glosarium window function.
Pakai DATE_TRUNC('week', tanggal) buat mundurin tiap tanggal ke awal minggunya. Di PostgreSQL, minggu mulai hari Senin. Setelah itu, GROUP BY hasil truncate-nya dan jumlahin metrik yang kamu mau. Ini ngubah data transaksi harian jadi satu baris per minggu.
SELECT
DATE_TRUNC('week', tanggal_order) AS minggu,
SUM(total) AS omzet
FROM orders
GROUP BY 1
ORDER BY 1;
Hasilnya satu baris per minggu dengan total omzetnya. Baris inilah yang jadi bahan buat langkah berikutnya. Kalau kamu belum familiar sama teknik ngelompokin per rentang waktu, aku bahas lebih lengkap di artikel date bucketing.
LAG(kolom) ngambil nilai dari baris sebelumnya dalam urutan yang kamu tentuin. Dengan OVER (ORDER BY minggu), dia ngintip omzet minggu tepat sebelum baris sekarang. Baris pertama gak punya minggu sebelumnya, jadi hasilnya NULL.
WITH mingguan AS (
SELECT
DATE_TRUNC('week', tanggal_order) AS minggu,
SUM(total) AS omzet
FROM orders
GROUP BY 1
)
SELECT
minggu,
omzet,
LAG(omzet) OVER (ORDER BY minggu) AS omzet_minggu_lalu
FROM mingguan
ORDER BY minggu;
Sekarang tiap baris punya dua angka bersebelahan: omzet minggu ini dan minggu lalu. Tinggal dihitung selisihnya.
Bungkus query tadi jadi CTE, lalu hitung (omzet - omzet_minggu_lalu) / omzet_minggu_lalu * 100. Pakai ROUND() biar rapi dan NULLIF() buat jaga-jaga kalau omzet minggu lalu nol, biar gak error dibagi nol.
WITH mingguan AS (
SELECT
DATE_TRUNC('week', tanggal_order) AS minggu,
SUM(total) AS omzet
FROM orders
GROUP BY 1
),
banding AS (
SELECT
minggu,
omzet,
LAG(omzet) OVER (ORDER BY minggu) AS omzet_lalu
FROM mingguan
)
SELECT
minggu,
omzet,
omzet_lalu,
ROUND((omzet - omzet_lalu) * 100.0 / NULLIF(omzet_lalu, 0), 1) AS wow_persen
FROM banding
ORDER BY minggu;
Kolom wow_persen ngasih angka kayak 18,4 (naik) atau -7,2 (turun). Itu pertumbuhan week over week kamu.
Toko_berkah jualan online, dan aku narik omzet 6 minggu terakhir buat lihat tren. Hasilnya kayak gini:
| Minggu | Omzet | WoW % |
|---|---|---|
| 2 Jun | Rp 42.100.000 | n/a |
| 9 Jun | Rp 48.700.000 | +15,7% |
| 16 Jun | Rp 45.200.000 | -7,2% |
| 23 Jun | Rp 61.800.000 | +36,7% |
| 30 Jun | Rp 52.300.000 | -15,4% |
| 7 Jul | Rp 55.900.000 | +6,9% |
Yang menarik: lonjakan 36,7% di minggu 23 Juni pas banget sama gajian akhir bulan. Aku cek 3 bulan ke belakang, dan pola ini konsisten. Minggu yang ngandung tanggal 25-31 rata-rata 28% lebih tinggi dari minggu awal bulan. Temuan ini yang bikin tim marketing mindahin jadwal promo ke minggu keempat.
Tanpa kolom WoW, lonjakan ini gampang kelewat karena angka absolutnya keliatan wajar. Persennya yang bikin polanya kebaca.
Lupa ORDER BY di dalam LAG. Tanpa ORDER BY minggu, LAG gak tahu mana baris "sebelumnya". Hasilnya bisa ngambil minggu acak. Urutan di window itu wajib, beda sama ORDER BY di akhir query.
Minggu yang kosong bikin lompatan. Kalau ada minggu tanpa transaksi, barisnya hilang dari hasil GROUP BY. LAG bakal ngebandingin dua minggu yang gak berurutan. Buat data yang perlu presisi, bikin dulu deret tanggal lengkap lalu LEFT JOIN.
Dibagi nol. Kalau omzet minggu lalu nol, pembagian langsung error. Selalu bungkus penyebut pakai NULLIF(omzet_lalu, 0).
Awal minggu beda antar database. PostgreSQL mulai Senin, sebagian tool BI mulai Minggu. Kalau angkamu beda sama dashboard, cek dulu definisi awal minggu di kedua sisi.
LAG ngambil nilai dari baris sebelumnya, LEAD ngambil dari baris sesudahnya. Buat week over week, kamu butuh LAG karena mau ngebandingin minggu ini sama minggu lalu yang posisinya di atas. LEAD kepakai kalau kamu mau ngeliat ke depan, misalnya berapa order minggu depan dibanding sekarang. Keduanya fungsi window dengan sintaks yang mirip.
Minggu tanpa transaksi bakal hilang dari hasil GROUP BY, dan itu bikin LAG ngebandingin dua minggu yang gak nyambung. Solusinya, bikin dulu tabel deret semua minggu pakai generate_series, lalu LEFT JOIN ke data kamu. Minggu kosong bakal keisi nol, jadi perhitungan WoW-nya tetap akurat dan gak ada lompatan.
Bisa, tapi ribet. Kamu mesti self join tabel mingguan ke dirinya sendiri dengan syarat minggu selisih 7 hari. Query-nya lebih panjang dan lebih lambat di data besar. LAG jauh lebih ringkas dan gampang dibaca. Kalau database kamu dukung window function, dan hampir semua yang modern dukung, pakai LAG aja.
Karena minggu pertama gak punya minggu sebelumnya buat dibandingin. LAG ngembaliin NULL di baris paling awal, jadi perhitungan persennya juga NULL. Ini normal dan bukan error. Kalau mau, kamu bisa tampilin sebagai strip atau nol pakai COALESCE, tapi secara logika emang gak ada angka pembandingnya.
Bisa, tambahin PARTITION BY di dalam window. Contohnya LAG(omzet) OVER (PARTITION BY kota ORDER BY minggu). Dengan gitu, LAG ngambil minggu lalu dalam kota yang sama, gak nyampur antar kota. Ini berguna kalau kamu mau bandingin tren mingguan Jakarta sama Surabaya secara terpisah dalam satu query.
Ringkasan cara hitung week over week di SQL:
DATE_TRUNC('week', tanggal)LAG() OVER (ORDER BY minggu)(sekarang - lalu) / lalu * 100, jaga pembagian nol pakai NULLIFDokumentasi lengkap fungsi window ada di dokumentasi PostgreSQL. Kalau kamu mau lanjut ke metrik waktu lain, baca cara hitung YTD, QTD, dan MTD di SQL, atau pelajari dasar window function dulu biar makin lancar.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Nambah 30 hari ke tanggal invoice, ngurangin sebulan buat cari periode lalu, ngitung selisih hari antar order. Semua itu aritmetika tanggal, dan SQL punya operator INTERVAL buat ngerjainnya.
Ngurangin tahun sekarang sama tahun lahir kelihatan gampang, tapi hasilnya sering meleset setahun kalau ulang tahunnya belum lewat. Ini cara hitung umur yang akurat di SQL.
Cara pakai DATE_TRUNC di SQL buat memotong timestamp jadi awal bulan, minggu, atau jam. Kunci bikin laporan penjualan per periode yang rapi.