Analisis Kohort Pendapatan dengan SQL, Bukan Hanya Retensi Pengguna
Blog/Tutorial SQL/Analisis Kohort Pendapatan dengan SQL, Bukan Hanya Retensi Pengguna

Analisis Kohort Pendapatan dengan SQL, Bukan Hanya Retensi Pengguna

BimaBima
·16 Oktober 2025·11 menit baca

Penulis

Bima

Bima

Founder & Data Professional

Bagikan

TL;DR

Analisis kohort pendapatan ngelompokin pelanggan berdasarkan bulan transaksi pertama, lalu ngukur berapa rupiah yang tiap kelompok hasilkan di bulan-bulan berikutnya. Bedanya sama kohort retensi cuma satu: yang dijumlahin nilai transaksi, bukan jumlah orang. Karena itu nilainya bisa lewat 100% waktu pelanggan yang bertahan belanja lebih banyak, sesuatu yang nggak pernah muncul di tabel retensi. Query-nya butuh tiga tahap: label kohort per pelanggan, hitung jarak bulan, lalu bagi tiap sel dengan nilai bulan ke-0 pakai FIRST_VALUE.

Analisis kohort pendapatan ngelompokin pelanggan berdasarkan bulan transaksi pertama, lalu ngukur berapa rupiah yang tiap kelompok itu hasilkan di bulan-bulan berikutnya.

Bedanya sama kohort retensi biasa cuma satu: yang dihitung nilai transaksi, bukan jumlah orang.

Perbedaan kecil itu ngasih jawaban yang beda. Kohort pendapatan bisa nunjukin kelompok pelanggan yang jumlahnya nyusut tapi belanjanya naik, dan itu nggak keliatan sama sekali di tabel retensi.

Apa bedanya kohort pendapatan dan kohort retensi?

Kohort retensi ngitung berapa persen pelanggan yang balik belanja di bulan ke-1, ke-2, dan seterusnya. Kohort pendapatan ngitung berapa persen nilai belanja bulan pertama yang bertahan di bulan-bulan berikutnya. Satu ngukur kepala, satunya ngukur rupiah, dan dua angka itu sering bergerak ke arah yang beda.

AspekKohort retensiKohort pendapatan
Yang dihitungJumlah pelanggan aktifTotal nilai transaksi
Nilai bisa di atas 100%?NggakBisa, kalau belanja per orang naik
Pertanyaan yang dijawabBerapa orang yang balik?Berapa rupiah yang balik?
Paling kepakai buatUkur pengalaman produkUkur nilai jangka panjang pelanggan

Angka di atas 100% itu yang paling menarik. Kalau 40% pelanggan bertahan tapi mereka belanja tiga kali lipat, pendapatan kohortnya naik walau kepalanya nyusut.

Buat dasar konsepnya, cek cohort analysis dulu. Versi retensinya aku bahas di matriks retensi kohort di SQL.

Struktur tabel dan langkah kerjanya

Semua query pakai tabel transaksi dengan kolom id_pelanggan, waktu_transaksi, total, dan status. Dialeknya PostgreSQL.

Empat langkah yang selalu sama:

  1. Tentuin bulan transaksi pertama tiap pelanggan. Itu label kohortnya.
  2. Hitung jarak bulan antara tiap transaksi dan bulan kohortnya.
  3. Jumlahin nilai transaksi per kohort per jarak bulan.
  4. Bagi tiap sel dengan nilai bulan ke-0 kohort itu buat dapat persentasenya.

Query 1: bikin label kohort tiap pelanggan

WITH kohort_pelanggan AS (
  SELECT
    id_pelanggan,
    DATE_TRUNC('month', MIN(waktu_transaksi))::date AS bulan_kohort
  FROM transaksi
  WHERE status = 'selesai'
  GROUP BY id_pelanggan
)
SELECT * FROM kohort_pelanggan LIMIT 10;

Satu pelanggan cuma punya satu bulan kohort selamanya. Itu yang bikin perbandingan antar kohort adil.

Filter status = 'selesai' penting di langkah ini. Kalau transaksi batal ikut kehitung, ada pelanggan yang labelnya kegeser ke bulan yang lebih awal.

Query 2: matriks kohort pendapatan lengkap

WITH kohort_pelanggan AS (
  SELECT
    id_pelanggan,
    DATE_TRUNC('month', MIN(waktu_transaksi))::date AS bulan_kohort
  FROM transaksi
  WHERE status = 'selesai'
  GROUP BY id_pelanggan
),
transaksi_berlabel AS (
  SELECT
    k.bulan_kohort,
    (EXTRACT(YEAR  FROM t.waktu_transaksi) - EXTRACT(YEAR  FROM k.bulan_kohort)) * 12
  + (EXTRACT(MONTH FROM t.waktu_transaksi) - EXTRACT(MONTH FROM k.bulan_kohort))
      AS bulan_ke,
    t.total,
    t.id_pelanggan
  FROM transaksi t
  JOIN kohort_pelanggan k ON k.id_pelanggan = t.id_pelanggan
  WHERE t.status = 'selesai'
),
rekap AS (
  SELECT
    bulan_kohort,
    bulan_ke,
    SUM(total)                    AS pendapatan,
    COUNT(DISTINCT id_pelanggan)  AS pelanggan_aktif
  FROM transaksi_berlabel
  GROUP BY 1, 2
)
SELECT
  bulan_kohort,
  bulan_ke,
  pendapatan,
  pelanggan_aktif,
  ROUND(
    pendapatan * 100.0
    / FIRST_VALUE(pendapatan) OVER (PARTITION BY bulan_kohort ORDER BY bulan_ke)
  , 1) AS persen_pendapatan
FROM rekap
ORDER BY bulan_kohort, bulan_ke;

Bagian kunci ada di FIRST_VALUE. Fungsi itu ngambil nilai bulan ke-0 dari tiap kohort sebagai penyebut, tanpa perlu subquery tambahan.

Cara hitung bulan_ke pakai selisih tahun dikali 12 plus selisih bulan. Ini lebih aman daripada ngurangin tanggal, soalnya panjang bulan beda-beda.

Dokumentasi resmi fungsi jendela ada di halaman window functions PostgreSQL.

Query 3: pendapatan kumulatif per pelanggan

Pertanyaan yang sering nyusul: satu pelanggan baru rata-rata ngasih berapa rupiah selama 6 bulan pertama? Ini versi kumulatifnya.

WITH kohort_pelanggan AS (
  SELECT
    id_pelanggan,
    DATE_TRUNC('month', MIN(waktu_transaksi))::date AS bulan_kohort
  FROM transaksi
  WHERE status = 'selesai'
  GROUP BY id_pelanggan
),
ukuran_kohort AS (
  SELECT bulan_kohort, COUNT(*) AS jml_pelanggan
  FROM kohort_pelanggan
  GROUP BY 1
),
bulanan AS (
  SELECT
    k.bulan_kohort,
    (EXTRACT(YEAR  FROM t.waktu_transaksi) - EXTRACT(YEAR  FROM k.bulan_kohort)) * 12
  + (EXTRACT(MONTH FROM t.waktu_transaksi) - EXTRACT(MONTH FROM k.bulan_kohort))
      AS bulan_ke,
    SUM(t.total) AS pendapatan
  FROM transaksi t
  JOIN kohort_pelanggan k ON k.id_pelanggan = t.id_pelanggan
  WHERE t.status = 'selesai'
  GROUP BY 1, 2
)
SELECT
  b.bulan_kohort,
  b.bulan_ke,
  ROUND(
    SUM(b.pendapatan) OVER (PARTITION BY b.bulan_kohort ORDER BY b.bulan_ke)
    / u.jml_pelanggan
  , 0) AS pendapatan_kumulatif_per_pelanggan
FROM bulanan b
JOIN ukuran_kohort u ON u.bulan_kohort = b.bulan_kohort
ORDER BY 1, 2;

Penyebutnya jumlah pelanggan awal kohort, bukan pelanggan yang masih aktif. Itu yang bikin angkanya kepakai buat nentuin batas biaya akuisisi.

Kalau biaya dapetin satu pelanggan Rp 45.000 dan angka kumulatif bulan ke-6 baru Rp 38.000, kampanyenya belum balik modal.

Contoh kasus: kohort pendapatan toko_berkah

Dataset toko_berkah punya 4.310 pelanggan dengan transaksi Mei sampai Oktober 2025. Ini potongan matriks pendapatan tiga kohort pertama, dalam persentase dari bulan ke-0.

KohortBulan 0Bulan 1Bulan 2Bulan 3
Mei 2025100%41,2%36,8%44,5%
Jun 2025100%38,6%33,1%35,9%
Jul 2025100%52,4%47,9%49,2%

Kohort Juli jauh lebih kuat dari dua kohort sebelumnya, selisihnya 13,8 poin di bulan ke-1.

Yang menarik, retensi pelanggannya nggak beda jauh. Kohort Mei 28,4% dan kohort Juli 30,1%, cuma beda 1,7 poin.

Jadi kenaikan pendapatannya bukan dari jumlah orang yang balik, tapi dari nilai belanja per orang. Nilai transaksi rata-rata pelanggan Juli yang balik di bulan ke-1 adalah Rp 312.400, sedangkan kohort Mei cuma Rp 198.700.

Selisih 57,2% itu yang bikin matriks pendapatannya beda jauh padahal matriks retensinya mirip.

Kalau kamu cuma lihat tabel retensi, kamu bakal simpulin ketiga kohort ini setara. Padahal nggak.

Kesalahan umum waktu bikin kohort pendapatan

Ngitung bulan_ke pakai selisih hari dibagi 30. Hasilnya geser di bulan yang panjangnya 31 hari. Pakai selisih tahun dan bulan.

Bandingin kohort yang umurnya beda. Kohort Oktober baru punya bulan ke-0, jadi wajar keliatan lebih jelek. Potong tabelnya di jarak bulan yang sama buat semua kohort.

Lupa filter status transaksi. Transaksi batal bikin label kohort dan nilai pendapatannya sama-sama meleset.

Nyampur mata uang atau nilai sebelum pajak. Sepakati dulu definisi pendapatan yang dipakai, lalu tulis di deskripsi laporannya.

Nganggap sel kosong sama dengan nol. Kohort tanpa transaksi di bulan tertentu nggak muncul barisnya. Gabungin ke deret bulan lengkap kalau kamu bikin grafik.

Ngukur kohort mingguan buat produk yang siklus belinya bulanan. Datanya jadi terlalu tipis dan naik turun nggak karuan. Sesuaikan ukuran kohort sama siklus beli produkmu.

FAQ

Kohort pendapatan bisa lebih dari 100% ya?

Bisa, dan itu tanda bagus. Angka di atas 100% berarti kelompok pelanggan itu belanja lebih banyak di bulan berikutnya dibanding bulan pertama mereka, walau jumlah orangnya nyusut. Ini kejadian kalau pelanggan yang bertahan naik kelas ke produk yang lebih mahal atau nambah frekuensi belanja. Retensi jumlah orang nggak akan pernah lewat 100%, tapi retensi rupiah bisa.

Kohort bulanan atau mingguan yang lebih pas?

Sesuaikan sama siklus beli produkmu. Buat produk yang dibeli sebulan sekali atau lebih jarang, pakai kohort bulanan biar tiap sel punya cukup data. Kohort mingguan cocok buat produk dengan pembelian sering seperti makanan atau aplikasi harian. Kalau selnya terlalu tipis, angkanya naik turun tanpa pola dan gampang salah dibaca.

Berapa lama data yang dibutuhin buat analisis kohort pendapatan?

Minimal 6 bulan biar kamu bisa lihat pola di bulan ke-3 sampai ke-5 untuk beberapa kohort sekaligus. Dengan 3 bulan data, cuma kohort pertama yang punya jejak panjang dan kesimpulannya rapuh. Kalau bisnismu baru jalan, tetap bikin tabelnya dari sekarang lalu baca ulang tiap bulan. Polanya baru kelihatan setelah beberapa kohort punya panjang yang sama.

Query kohort aku lambat banget, gimana benerinnya?

Simpan hasil langkah pertama ke tabel terpisah yang isinya id pelanggan dan bulan kohortnya, lalu perbarui sekali sehari. Bagian yang paling berat biasanya ngitung ulang MIN waktu transaksi buat semua pelanggan tiap kali query jalan. Tambahin juga index di kolom id pelanggan dan waktu transaksi, dan batasi rentang tanggalnya secukupnya.

Apa gunanya kohort pendapatan buat toko kecil yang pelanggannya sedikit?

Tetap berguna, tapi bacanya harus hati-hati. Kalau satu kohort cuma isi 30 pelanggan, satu order besar bisa narik seluruh barisnya. Cek angka mediannya bareng total pendapatan biar kamu tau apakah kenaikannya merata atau cuma dari satu orang. Buat data kecil, kohort per kuartal sering lebih stabil dari kohort bulanan.

Penutup

Kohort pendapatan ngasih jawaban yang nggak bisa dikasih tabel retensi: berapa rupiah yang beneran bertahan dari tiap kelompok pelanggan.

Query-nya nggak jauh beda dari kohort retensi. Yang berubah cuma yang dijumlahin, dari COUNT(DISTINCT id_pelanggan) jadi SUM(total).

Yang paling menentukan hasilnya bukan query, tapi konsistensi definisi pendapatan dan filter status transaksinya.

Coba jalanin query kedua ke data transaksimu, lalu bandingin hasilnya sama tabel retensi yang udah ada. Lanjut baca SQL repeat purchase buat ngukur pembelian ulang per pelanggan, atau pahami dulu metric yang mau kamu pakai.

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

Cara Membuat Pivot Dinamis di SQL (Panduan 2026)
Tutorial SQL
19 Juli 2026•10 menit baca

Cara Membuat Pivot Dinamis di SQL (Panduan 2026)

Pivot dinamis di SQL ngubah baris jadi kolom tanpa kamu hardcode nama kolomnya. Ini cara bikinnya pakai CASE WHEN dan versi yang kolomnya ngikut data.

BimaBima
Menghitung YTD, QTD, dan MTD di SQL (Panduan 2026)
Tutorial SQL
17 Juli 2026•9 menit baca

Menghitung YTD, QTD, dan MTD di SQL (Panduan 2026)

YTD, QTD, dan MTD ngukur total dari awal tahun, kuartal, atau bulan sampai hari ini. Ini cara ngitungnya di SQL pakai DATE_TRUNC, plus satu query gabungan.

BimaBima
Gap and Island Analysis di SQL
Tutorial SQL
15 Juli 2026•10 menit baca

Gap and Island Analysis di SQL

Gap and island analysis di SQL nyari deret data yang berturut-turut (island) dan celah yang bolong (gap). Cocok buat hitung streak login atau cari tanggal transaksi yang hilang.

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.

© 2026 Ngulik Data. Semua hak dilindungi.

TAUTAN
BantuanHargaDatasetBlogAfiliasi
LEGAL
Syarat & KetentuanKebijakan Privasi
Ngulik Data
DatasetLeaderboardBlogStore