YoY Growth SQL: Bandingkan Performa dengan Tahun Lalu
Blog/Tutorial SQL/YoY Growth SQL: Bandingkan Performa dengan Tahun Lalu

YoY Growth SQL: Bandingkan Performa dengan Tahun Lalu

BimaBima
·17 Mei 2026·11 menit baca

Penulis

Bima

Bima

Founder & Data Professional

Bagikan

TL;DR

YoY growth di SQL dihitung dengan rumus (nilai periode ini - nilai periode sama tahun lalu) / nilai periode sama tahun lalu, biasanya dikali 100 biar jadi persen. Cara paling bersih ngambil nilai tahun lalu adalah window function LAG dengan offset 12 di data bulanan yang udah rapi. Kalau ada bulan yang bolong atau nilainya nol, LAG bakal ngasih hasil ngaco, jadi kamu perlu generate deret bulan lengkap dan pakai NULLIF di pembagi.

YoY growth di SQL dihitung pakai rumus (nilai periode ini − nilai periode sama tahun lalu) dibagi nilai periode sama tahun lalu.

Cara paling bersih ngambil nilai tahun lalu adalah window function LAG dengan offset 12 di data bulanan.

Pertanyaan "omzet bulan ini naik gak dibanding tahun lalu" itu pertanyaan yang paling sering nongol di meeting bulanan. Dan hampir tiap analis pernah salah jawab gara-gara satu bulan bolong di data. Aku bahas query-nya, jebakannya, dan cara benerinnya.

Apa itu YoY growth?

YoY growth (year-over-year growth) adalah persentase perubahan sebuah metrik dibanding periode yang sama di tahun sebelumnya. Maret 2026 dibandingin Maret 2025. Kuartal 2 tahun ini dibandingin kuartal 2 tahun lalu.

Gunanya satu: ngilangin efek musiman. Omzet Desember hampir selalu lebih gede dari November, dan itu bukan berarti bisnisnya lagi tumbuh. Cuma karena Desember emang bulan belanja.

Dengan bandingin Desember ke Desember, faktor musimannya batal sendiri.

Gimana rumus YoY growth?

YoY growth (%) = (nilai_sekarang - nilai_tahun_lalu) / nilai_tahun_lalu * 100

Contoh angka: omzet Maret 2026 Rp 120 juta, omzet Maret 2025 Rp 100 juta.

(120.000.000 - 100.000.000) / 100.000.000 * 100 = 20%

Naik 20 persen. Gampang. Yang bikin ribet itu ngambil angka "tahun lalu"-nya dari database.

Cara hitung YoY growth pakai LAG

Langkah 1: agregat data mentah jadi omzet per bulan dulu. Ini pakai tabel transaksi dari dataset toko_berkah.

SELECT
  DATE_TRUNC('month', tanggal) AS bulan,
  SUM(total_harga) AS omzet
FROM transaksi
GROUP BY 1
ORDER BY 1;

Hasilnya satu baris per bulan, urut dari lama ke baru.

Langkah 2: pakai LAG buat narik nilai 12 baris sebelumnya. Karena datanya bulanan, 12 baris ke belakang = bulan yang sama tahun lalu.

WITH bulanan AS (
  SELECT
    DATE_TRUNC('month', tanggal) AS bulan,
    SUM(total_harga) AS omzet
  FROM transaksi
  GROUP BY 1
)
SELECT
  bulan,
  omzet,
  LAG(omzet, 12) OVER (ORDER BY bulan) AS omzet_tahun_lalu,
  ROUND(
    (omzet - LAG(omzet, 12) OVER (ORDER BY bulan))
    / NULLIF(LAG(omzet, 12) OVER (ORDER BY bulan), 0) * 100
  , 1) AS yoy_persen
FROM bulanan
ORDER BY bulan;

Tiga hal yang lagi terjadi di query itu.

LAG(omzet, 12) ngambil nilai omzet dari 12 baris sebelumnya. Angka 12 itu offset-nya.

OVER (ORDER BY bulan) ngasih tau SQL urutan barisnya gimana. Tanpa ini, "12 baris sebelumnya" gak punya makna.

NULLIF(..., 0) ngubah pembagi nol jadi NULL, biar query-nya gak meledak. Lebih detail soal cara kerja window function bisa kamu cek di LAG.

Hasilnya kira-kira gini di data toko_berkah:

bulanomzetomzet_tahun_laluyoy_persen
2026-01-01142.300.000118.500.00020,1
2026-02-01129.800.000121.200.0007,1
2026-03-01156.400.000134.900.00015,9
2026-04-01171.200.000127.600.00034,2

Kenapa LAG bisa ngasih hasil ngaco?

Ini jebakan yang paling sering makan korban. LAG(omzet, 12) gak ngitung bulan. Dia ngitung baris.

Kalau di data toko_berkah bulan Juni 2025 gak ada transaksi sama sekali, baris Juni 2025 gak muncul di hasil GROUP BY. Deret bulannya jadi bolong.

Efeknya: LAG(omzet, 12) di baris Mei 2026 malah narik nilai April 2025, bukan Mei 2025. Semua angka setelah bulan bolong itu jadi geser dan salah semua.

Ada 2 cara benerin.

Cara 1: self-join lewat tanggal

Ini yang paling aman kalau kamu gak yakin data kamu lengkap. Match-nya lewat tanggal eksplisit, bukan lewat urutan baris.

WITH bulanan AS (
  SELECT
    DATE_TRUNC('month', tanggal) AS bulan,
    SUM(total_harga) AS omzet
  FROM transaksi
  GROUP BY 1
)
SELECT
  s.bulan,
  s.omzet,
  l.omzet AS omzet_tahun_lalu,
  ROUND((s.omzet - l.omzet) / NULLIF(l.omzet, 0) * 100, 1) AS yoy_persen
FROM bulanan s
LEFT JOIN bulanan l
  ON l.bulan = s.bulan - INTERVAL '12 months'
ORDER BY s.bulan;

Kalau bulan pasangannya gak ada, LEFT JOIN ngasih NULL. Jujur dan gak nyasar. Detail soal LEFT JOIN ada di halaman LEFT JOIN.

Cara 2: bikin deret bulan lengkap dulu

Generate semua bulan dari awal sampai akhir, terus LEFT JOIN data transaksi ke situ. Bulan tanpa transaksi dapet omzet 0, dan deretnya jadi rapat lagi. Habis itu LAG aman dipakai.

Di PostgreSQL, generate_series yang ngerjain. Di BigQuery, GENERATE_DATE_ARRAY.

Contoh kasus: YoY per kategori di toko_berkah

Angka YoY total sering nyembunyiin cerita. Bulan April 2026 di data toko_berkah naik 34,2 persen, dan pas dipecah per kategori, ceritanya beda banget.

WITH bulanan AS (
  SELECT
    kategori,
    DATE_TRUNC('month', tanggal) AS bulan,
    SUM(total_harga) AS omzet
  FROM transaksi
  GROUP BY 1, 2
)
SELECT
  kategori,
  bulan,
  omzet,
  ROUND(
    (omzet - LAG(omzet, 12) OVER (PARTITION BY kategori ORDER BY bulan))
    / NULLIF(LAG(omzet, 12) OVER (PARTITION BY kategori ORDER BY bulan), 0) * 100
  , 1) AS yoy_persen
FROM bulanan
ORDER BY kategori, bulan;

Kuncinya ada di PARTITION BY kategori. Tanpa itu, LAG bakal loncat antar kategori dan hasilnya kacau.

Hasil April 2026 per kategori:

kategoriyoy_persen
Minuman+61,4
Sembako+18,9
Snack+7,2
Perawatan tubuh−12,5

Jadi pertumbuhan 34,2 persen itu hampir semuanya ditopang minuman. Kategori perawatan tubuh malah turun 12,5 persen dan gak ada yang nyadar, karena ketutup angka total.

Ini alasan kenapa YoY total doang jarang cukup buat ngambil keputusan.

Kesalahan umum waktu hitung YoY

Lupa PARTITION BY. Kalau data kamu punya dimensi (kategori, kota, produk), LAG tanpa PARTITION BY bakal narik nilai dari grup sebelahnya. Angkanya keliatan wajar, tapi salah total.

Bandingin periode yang belum lengkap. Kalau hari ini tanggal 12 dan kamu bandingin omzet bulan berjalan sama omzet bulan penuh tahun lalu, YoY-nya pasti keliatan anjlok. Bandingin tanggal 1–12 lawan tanggal 1–12.

Bagi nol tanpa NULLIF. Kategori baru yang tahun lalu omzetnya nol bakal bikin query error di PostgreSQL. Bungkus pembagi pakai NULLIF, terus tampilin sebagai "baru" di dashboard.

Pakai persentase buat basis kecil. Naik dari Rp 200 ribu ke Rp 600 ribu itu YoY 200 persen, dan angka itu bakal jadi bintang di dashboard. Padahal nilainya cuma Rp 400 ribu. Selalu tampilin nilai absolut di sebelah persentase.

Sintaks lengkap window function bisa kamu cek di dokumentasi resmi PostgreSQL.

FAQ

Apa rumus YoY growth?

Rumusnya (nilai sekarang − nilai tahun lalu) dibagi nilai tahun lalu, terus dikali 100 buat jadi persen. Misal omzet Maret 2026 Rp 120 juta dan Maret 2025 Rp 100 juta, YoY growth-nya (120−100)/100 × 100 = 20 persen. Yang penting periodenya harus sebanding: Maret dibandingin Maret, bukan Maret dibandingin Februari.

LAG atau self-join, mana yang lebih bagus buat YoY?

LAG lebih pendek dan lebih cepat kalau datanya udah agregat per bulan tanpa bolong. Self-join lebih aman kalau ada bulan yang gak punya transaksi, soalnya kamu match-nya lewat tanggal eksplisit, bukan lewat urutan baris. Kalau kamu gak yakin datanya lengkap, pakai self-join atau generate deret bulan dulu sebelum pakai LAG.

Gimana kalau nilai tahun lalu nol?

Pembagian sama nol bakal bikin error atau NULL, tergantung databasenya. Bungkus pembagi pakai NULLIF, contohnya NULLIF(omzet_lalu, 0). Hasilnya jadi NULL, bukan error. Secara matematis growth dari nol memang gak terdefinisi, jadi NULL itu jawaban yang jujur. Di dashboard, tampilkan sebagai "baru" atau tanda strip.

Kenapa YoY lebih dipilih daripada MoM?

YoY otomatis ngebandingin bulan yang sama, jadi efek musiman ilang. Omzet Desember hampir selalu lebih tinggi dari November, dan itu gak berarti bisnisnya lagi tumbuh. Dengan YoY, kamu bandingin Desember sama Desember. MoM tetap berguna buat lihat tren jangka pendek, cuma harus sadar ada faktor musiman di dalamnya.

Apakah LAG jalan di semua database?

LAG jalan di PostgreSQL, MySQL 8.0 ke atas, SQL Server 2012 ke atas, BigQuery, Snowflake, dan SQLite 3.25 ke atas. Yang gak dapet cuma MySQL 5.7 dan yang lebih tua. Kalau kamu masih di MySQL 5.7, pakai self-join. Sintaks LAG-nya sendiri standar dan hampir gak ada bedanya antar database.

Penutup

Tiga poin yang perlu nempel.

LAG(metrik, 12) itu cara tercepat hitung YoY di data bulanan, asal deret bulannya gak bolong.

Kalau ada bulan yang kosong, pakai self-join lewat tanggal. Lebih panjang, tapi gak bakal bohong.

Pecah YoY per dimensi. Angka total sering nutupin kategori yang lagi jatuh.

Coba query-nya sendiri di dataset toko_berkah, terus utak-atik offset-nya jadi 1 buat dapet MoM. Latihan window function bisa kamu mulai dari LAG dan window function.

Lanjut baca: Churn Rate SQL — metrik lain yang juga sering salah hitung gara-gara periode yang gak sebanding.

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
MoM Growth SQL: Hitung Pertumbuhan Bulanan Otomatis
Tutorial SQL
14 Mei 2026•10 menit baca

MoM Growth SQL: Hitung Pertumbuhan Bulanan Otomatis

Berhenti copy-paste omzet bulan lalu ke Excel buat ngitung growth. Satu query SQL pakai LAG bisa ngasih pertumbuhan bulanan otomatis, lengkap sampai bulan yang datanya kosong.

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