MoM Growth SQL: Hitung Pertumbuhan Bulanan Otomatis
Blog/Tutorial SQL/MoM Growth SQL: Hitung Pertumbuhan Bulanan Otomatis

MoM Growth SQL: Hitung Pertumbuhan Bulanan Otomatis

BimaBima
·14 Mei 2026·10 menit baca

Penulis

Bima

Bima

Founder & Data Professional

Bagikan

TL;DR

MoM growth (month-over-month) di SQL dihitung pakai fungsi window LAG buat ambil nilai bulan sebelumnya, lalu bandingin sama bulan sekarang. Rumusnya: (bulan_ini - bulan_lalu) / bulan_lalu * 100. Jebakan utamanya bulan yang nggak punya transaksi sama sekali — baris itu hilang dari hasil GROUP BY, dan growth kamu jadi salah.

MoM growth di SQL dihitung pakai fungsi window LAG — dia ambil omzet bulan sebelumnya, terus kamu bandingin sama bulan sekarang. Satu query, langsung dapat kolom pertumbuhan buat semua bulan.

Nggak perlu export ke Excel. Nggak perlu geser-geser kolom.

Aku dulu ngerjain ini manual tiap awal bulan. Export CSV, buka Excel, bikin kolom "bulan lalu", tarik rumus ke bawah. Empat puluh menit. Tiap bulan. Sampai aku sadar SQL bisa ngelakuin ini dalam satu query yang tinggal di-refresh.

Apa itu MoM growth?

MoM growth (month-over-month growth) adalah persentase perubahan sebuah angka dibanding bulan sebelumnya.

Rumusnya:

MoM growth (%) = (bulan_ini - bulan_lalu) / bulan_lalu × 100

Omzet Januari Rp 100 juta, Februari Rp 120 juta. MoM growth Februari = (120 - 100) / 100 × 100 = 20%.

Metrik ini favorit buat lihat momentum jangka pendek. Bisnis lagi ngegas atau lagi ngerem.

Gimana cara hitung MoM growth di SQL?

Tiga langkah:

  1. Grupin data per bulan pakai DATE_TRUNC dan GROUP BY.
  2. Ambil nilai bulan sebelumnya pakai LAG.
  3. Hitung selisihnya dan bagi.

Langkah 1: Agregasi per bulan

SELECT DATE_TRUNC('month', tanggal) AS bulan,
       SUM(qty * harga_satuan) AS omzet
FROM transaksi
WHERE status = 'selesai'
GROUP BY DATE_TRUNC('month', tanggal)
ORDER BY bulan;

DATE_TRUNC('month', tanggal) ngubah tanggal jadi tanggal 1 di bulan itu. Jadi 15 Maret dan 28 Maret sama-sama jadi 1 Maret, dan bisa digrupin bareng.

Jangan lupa WHERE status = 'selesai'. Transaksi batal nggak boleh ikut.

Langkah 2: Ambil bulan lalu pakai LAG

LAG itu window function yang ngambil nilai dari baris sebelumnya. Tanpa self-join, tanpa subquery ribet.

LAG(omzet) OVER (ORDER BY bulan)

Baca gini: "urutin baris berdasarkan bulan, terus di tiap baris, ambil omzet dari baris di atasnya."

Baris pertama nggak punya baris di atasnya, jadi hasilnya NULL. Itu benar dan wajar.

Langkah 3: Query lengkapnya

WITH omzet_bulanan AS (
  SELECT DATE_TRUNC('month', tanggal) AS bulan,
         SUM(qty * harga_satuan) AS omzet
  FROM transaksi
  WHERE status = 'selesai'
  GROUP BY DATE_TRUNC('month', tanggal)
)
SELECT bulan,
       omzet,
       LAG(omzet) OVER (ORDER BY bulan) AS omzet_bulan_lalu,
       ROUND(
         (omzet - LAG(omzet) OVER (ORDER BY bulan))
         / NULLIF(LAG(omzet) OVER (ORDER BY bulan), 0) * 100,
         1
       ) AS mom_growth_persen
FROM omzet_bulanan
ORDER BY bulan;

Perhatiin NULLIF(..., 0). Ini yang nyelametin kamu dari error "division by zero" kalau bulan lalu omzetnya nol. NULLIF ngubah nol jadi NULL, dan pembagian sama NULL hasilnya NULL — bukan crash.

Aku pakai fungsi LAG dan NULLIF di hampir setiap query growth yang aku tulis.

Gimana kalau mau per cabang?

Tambahin PARTITION BY. Ini bilang ke LAG: "reset hitungan tiap ganti cabang".

WITH omzet_bulanan AS (
  SELECT cabang,
         DATE_TRUNC('month', tanggal) AS bulan,
         SUM(qty * harga_satuan) AS omzet
  FROM transaksi
  WHERE status = 'selesai'
  GROUP BY cabang, DATE_TRUNC('month', tanggal)
)
SELECT cabang,
       bulan,
       omzet,
       ROUND(
         (omzet - LAG(omzet) OVER (PARTITION BY cabang ORDER BY bulan))
         / NULLIF(LAG(omzet) OVER (PARTITION BY cabang ORDER BY bulan), 0) * 100,
         1
       ) AS mom_growth_persen
FROM omzet_bulanan
ORDER BY cabang, bulan;

Tanpa PARTITION BY, baris pertama cabang Bekasi bakal bandingin diri sama baris terakhir cabang Depok. Angkanya bakal ngaco total.

Jebakan terbesar: bulan yang nggak ada transaksi

Ini yang bikin angka kamu salah tanpa error apa pun.

Bayangin cabang Bogor tutup renovasi selama Februari 2026. Nol transaksi. Hasil GROUP BY kamu:

bulanomzet
2026-01-0152.000.000
2026-03-0148.000.000

Februari nggak ada barisnya. Jadi LAG di baris Maret ngambil Januari, bukan Februari.

Hasilnya: growth Maret dilaporin -7,7% dibanding Januari. Padahal yang bener, Maret naik dari nol.

Solusinya: bikin deret bulan lengkap dulu, lalu LEFT JOIN ke data.

WITH deret_bulan AS (
  SELECT generate_series(
    DATE '2025-01-01',
    DATE '2026-06-01',
    INTERVAL '1 month'
  )::date AS bulan
),
omzet_bulanan AS (
  SELECT DATE_TRUNC('month', tanggal)::date AS bulan,
         SUM(qty * harga_satuan) AS omzet
  FROM transaksi
  WHERE status = 'selesai'
  GROUP BY DATE_TRUNC('month', tanggal)
)
SELECT d.bulan,
       COALESCE(o.omzet, 0) AS omzet,
       ROUND(
         (COALESCE(o.omzet, 0) - LAG(COALESCE(o.omzet, 0)) OVER (ORDER BY d.bulan))
         / NULLIF(LAG(COALESCE(o.omzet, 0)) OVER (ORDER BY d.bulan), 0) * 100,
         1
       ) AS mom_growth_persen
FROM deret_bulan d
LEFT JOIN omzet_bulanan o ON d.bulan = o.bulan
ORDER BY d.bulan;

generate_series itu fungsi PostgreSQL. Di BigQuery pakai GENERATE_DATE_ARRAY. Di MySQL kamu harus bikin tabel kalender manual.

Contoh kasus: MoM growth toko_berkah

Dataset toko_berkah dari ngulikdata: 12.400 transaksi, 3 cabang, Januari 2025 sampai Juni 2026.

Ini hasil query MoM growth buat 6 bulan terakhir:

BulanOmzetMoM growth
Jan 2026Rp 148.200.000—
Feb 2026Rp 131.900.000-11,0%
Mar 2026Rp 172.400.000+30,7%
Apr 2026Rp 189.100.000+9,7%
Mei 2026Rp 155.300.000-17,9%
Jun 2026Rp 161.800.000+4,2%

Lonjakan Maret +30,7% itu Ramadan. Anjlok Mei -17,9% itu bulan setelah Lebaran, orang lagi ngerem belanja.

Ini poin penting soal MoM: dia kena efek musiman. Kalau kamu lihat -17,9% terus panik, kamu salah baca. Bandingin Mei 2026 sama Mei 2025 (YoY), dan kamu bakal lihat bisnisnya sebenernya tumbuh 12%.

MoM buat momentum. YoY buat kesehatan bisnis. Pakai dua-duanya.

Kesalahan umum

Lupa NULLIF. Satu bulan omzetnya nol, query kamu langsung error "division by zero". Ini nemu kamu di production, bukan di testing.

Lupa PARTITION BY waktu ngitung per grup. Baris pertama tiap cabang bakal ambil data dari cabang lain. Angkanya masuk akal secara visual, tapi salah total.

ORDER BY di dalam OVER nggak sama dengan ORDER BY hasil akhir. Dua-duanya perlu ada. ORDER BY di dalam OVER nentuin urutan LAG. ORDER BY di akhir cuma ngatur tampilan.

Ngelapor MoM growth doang. Angka +30% kelihatan hebat sampai kamu sadar bulan sebelumnya anjlok. Selalu tampilin angka mentahnya di sebelah persentase.

FAQ

Apa itu MoM growth?

MoM growth atau month-over-month growth adalah persentase perubahan sebuah metrik dibanding bulan sebelumnya. Rumusnya nilai bulan ini dikurangi bulan lalu, dibagi bulan lalu, dikali 100. Kalau omzet Januari 100 juta dan Februari 120 juta, MoM growth Februari adalah 20 persen. Ini metrik favorit buat lihat momentum jangka pendek.

Fungsi apa yang dipakai buat MoM growth di SQL?

LAG, salah satu window function. LAG ambil nilai dari baris sebelumnya tanpa perlu self-join. Sintaksnya LAG(omzet, 1) OVER (ORDER BY bulan). Fungsi ini jalan di PostgreSQL, MySQL 8 ke atas, SQL Server, BigQuery, dan SQLite 3.25 ke atas. MySQL 5.7 belum punya, jadi kamu harus pakai self-join.

Gimana kalau ada bulan yang nggak ada transaksi?

Bulan itu bakal hilang dari hasil GROUP BY, dan LAG bakal ambil bulan sebelumnya yang salah. Misalnya Februari kosong, LAG di bulan Maret bakal ambil Januari, bukan Februari. Solusinya bikin deret bulan lengkap dulu pakai generate_series, lalu LEFT JOIN ke data transaksi.

Kenapa hasil growth-ku muncul NULL di baris pertama?

Karena baris pertama nggak punya bulan sebelumnya buat dibandingin. LAG ngembaliin NULL di situ, dan NULL dibagi apa pun tetap NULL. Ini normal dan benar. Kalau kamu mau tampilin sebagai kosong atau tanda strip di dashboard, bungkus pakai COALESCE atau biarkan NULL supaya jujur.

Bedanya MoM growth sama YoY growth apa?

MoM bandingin sama bulan sebelumnya, YoY bandingin sama bulan yang sama tahun lalu. MoM bagus buat lihat momentum cepat, tapi kena efek musiman. Penjualan Januari selalu turun habis Desember, dan itu bukan berarti bisnis lagi jelek. YoY lebih tahan musim karena bandingin periode yang setara.

Penutup

Yang perlu kamu bawa pulang:

LAG + NULLIF = query MoM growth yang aman. Tambahin PARTITION BY kalau mau per cabang. Bikin deret bulan lengkap kalau ada kemungkinan bulan kosong.

Simpan query ini jadi view atau saved question di dashboard kamu. Sekali bikin, tinggal refresh tiap bulan.

Mau lanjut ke window function lain? Baca panduan window function SQL — di situ ada ROW_NUMBER, RANK, dan running total yang sering kepakai bareng LAG.

Detail lengkap perilaku window function ada di dokumentasi resmi PostgreSQL.

Coba Langsung

Mau praktek langsung? Mulai latihan SQL gratis

Latihan interaktif, langsung di browser.

Buka NgulikSQL →
Bagikan:
Bima
Ditulis oleh

Bima

Founder & Data Professional

Founder Ngulik Data. Passionate about making data analysis accessible for everyone.

Artikel Terkait

Churn Rate SQL: Deteksi Pelanggan yang Kabur Sejak Dini
Tutorial SQL
20 Mei 2026•11 menit baca

Churn Rate SQL: Deteksi Pelanggan yang Kabur Sejak Dini

Studi kasus toko_berkah: 1.240 pelanggan hilang dalam setahun, dan sinyalnya udah kelihatan 2 bulan sebelumnya. Ini query yang bikin kamu nangkap mereka lebih awal.

BimaBima
YoY Growth SQL: Bandingkan Performa dengan Tahun Lalu
Tutorial SQL
17 Mei 2026•11 menit baca

YoY Growth SQL: Bandingkan Performa dengan Tahun Lalu

Hitung YoY growth di SQL pakai LAG atau self-join, lengkap dengan cara ngindarin pembagi nol dan jebakan bulan yang bolong. Contohnya pakai data toko_berkah.

BimaBima
Customer Lifetime Value SQL: Hitung LTV dari Data Transaksi
Tutorial SQL
11 Mei 2026•11 menit baca

Customer Lifetime Value SQL: Hitung LTV dari Data Transaksi

LTV bilang berapa nilai satu pelanggan sepanjang dia jadi pelanggan kamu. Ini query SQL buat ngitungnya dari data transaksi, plus cara pakainya buat keputusan budget akuisisi.

BimaBima
Kembali ke Blog
Ngulik Data logoNgulik Data

Platform edukasi data lengkap untuk professionals Indonesia. Belajar SQL, Data Analysis, dan lebih banyak lagi dengan praktek langsung dan feedback real-time.

Copyright © 2026 - All rights reserved

LINKS
SupportPricingDatasetBlogAffiliates
LEGAL
Terms of servicesPrivacy policy
Ngulik Data
DatasetLeaderboardBlogStore