TL;DR
RFM analysis adalah cara segmentasi pelanggan pakai tiga ukuran: Recency (kapan terakhir belanja), Frequency (berapa kali belanja), dan Monetary (total uang yang dibelanjain). Di SQL, kamu bisa hitung ketiganya dari tabel transaksi, kasih skor 1-5 pakai NTILE, lalu gabungin jadi segmen kayak Champion atau Hampir Hilang. Satu query CTE bertingkat udah cukup, nggak butuh tool tambahan.
RFM analysis adalah cara ngelompokin pelanggan pakai tiga angka: kapan terakhir mereka belanja (Recency), berapa kali mereka belanja (Frequency), dan berapa total uang yang mereka keluarin (Monetary).
Yang bikin metode ini enak: datanya udah ada di tabel transaksi kamu. Nggak butuh survei, nggak butuh tool mahal, nggak butuh machine learning.
Satu query SQL udah cukup buat mecah 5.000 pelanggan jadi segmen yang bisa langsung ditindaklanjutin tim marketing. Ini cara bikinnya, step demi step.
RFM analysis adalah metode segmentasi pelanggan berbasis tiga metrik perilaku beli: Recency, Frequency, dan Monetary. Tiap pelanggan dikasih skor 1-5 di masing-masing dimensi, lalu ketiga skor itu digabung jadi label segmen. Pelanggan dengan skor tinggi di ketiganya adalah pelanggan terbaik kamu.
Metodenya lahir di dunia direct mail tahun 90-an, waktu ngirim katalog itu mahal dan orang harus milih siapa yang paling worth dikirimin. Logikanya masih relevan sampai hari ini.
| Dimensi | Yang diukur | Kolom yang dipakai |
|---|---|---|
| Recency | Berapa hari sejak transaksi terakhir | MAX(tanggal_transaksi) |
| Frequency | Berapa kali transaksi | COUNT(DISTINCT transaksi_id) |
| Monetary | Total rupiah yang dibelanjain | SUM(total_harga) |
Cuma tiga kolom. Serius.
transaksi
---------
transaksi_id | integer
pelanggan_id | integer
tanggal_transaksi | date
total_harga | numeric
Kalau tabel transaksi kamu punya empat kolom ini, kamu udah bisa jalanin RFM hari ini juga. Nggak ada tabel pelanggan? Nggak masalah, pelanggan_id aja cukup.
Langkah pertama: agregasi per pelanggan. Ini bagian yang paling gampang.
WITH rfm_base AS (
SELECT
pelanggan_id,
DATE_PART('day', DATE '2026-05-01' - MAX(tanggal_transaksi)) AS recency_hari,
COUNT(DISTINCT transaksi_id) AS frequency,
SUM(total_harga) AS monetary
FROM transaksi
WHERE tanggal_transaksi >= DATE '2025-05-01'
GROUP BY pelanggan_id
)
SELECT * FROM rfm_base LIMIT 5;
Hasilnya kira-kira gini:
pelanggan_id | recency_hari | frequency | monetary
-------------+--------------+-----------+-----------
101 | 4 | 18 | 4.250.000
102 | 212 | 2 | 380.000
103 | 31 | 7 | 1.640.000
Perhatiin tanggal patokannya aku hardcode ke 2026-05-01. Ini penting. Kalau kamu pakai CURRENT_DATE, hasil analisis bulan ini nggak bisa dibandingin sama hasil bulan lalu. Kunci tanggalnya di akhir periode analisis.
Kalau DATE_PART dan CTE masih agak asing, mampir dulu ke glosarium CTE sama fungsi DATE_PART.
Angka mentah tadi belum bisa dibandingin. Frequency 7 itu bagus atau jelek? Tergantung pelanggan lain.
NTILE(5) bagi semua pelanggan ke 5 kelompok sama rata berdasarkan urutan. Yang paling atas dapat skor 5, paling bawah dapat 1.
rfm_skor AS (
SELECT
pelanggan_id,
recency_hari,
frequency,
monetary,
NTILE(5) OVER (ORDER BY recency_hari DESC) AS r_skor,
NTILE(5) OVER (ORDER BY frequency ASC) AS f_skor,
NTILE(5) OVER (ORDER BY monetary ASC) AS m_skor
FROM rfm_base
)
Liat DESC di baris recency. Itu disengaja. Recency bagus itu angkanya kecil, belanja 3 hari lalu jauh lebih berharga dari belanja 300 hari lalu. Jadi urutannya dibalik biar yang harinya sedikit dapat skor 5.
Frequency dan monetary pakai ASC, soalnya di situ makin gede makin bagus.
Detail lengkap soal window function ini ada di halaman fungsi NTILE, dan dokumentasi resminya di postgresql.org.
Sekarang tinggal terjemahin kombinasi skor jadi label yang orang marketing ngerti.
SELECT
pelanggan_id,
recency_hari, frequency, monetary,
r_skor, f_skor, m_skor,
CASE
WHEN r_skor >= 4 AND f_skor >= 4 AND m_skor >= 4 THEN 'Champion'
WHEN r_skor >= 3 AND f_skor >= 3 THEN 'Pelanggan Loyal'
WHEN r_skor >= 4 AND f_skor <= 2 THEN 'Pelanggan Baru'
WHEN r_skor <= 2 AND f_skor >= 3 THEN 'Hampir Hilang'
WHEN r_skor <= 2 AND f_skor <= 2 THEN 'Udah Hilang'
ELSE 'Perlu Perhatian'
END AS segmen
FROM rfm_skor
ORDER BY monetary DESC;
Urutan WHEN di CASE itu penting. SQL berhenti di kondisi pertama yang cocok, jadi taruh yang paling spesifik di atas.
Aku jalanin query ini di dataset toko_berkah, toko kelontong online dengan 1.200 pelanggan dan 8.400 transaksi selama 12 bulan. Ini distribusi segmennya.
| Segmen | Jumlah pelanggan | % dari total | Kontribusi omzet |
|---|---|---|---|
| Champion | 131 | 10,9% | 41,3% |
| Pelanggan Loyal | 246 | 20,5% | 29,7% |
| Pelanggan Baru | 188 | 15,7% | 6,2% |
| Perlu Perhatian | 274 | 22,8% | 13,1% |
| Hampir Hilang | 159 | 13,3% | 7,4% |
| Udah Hilang | 202 | 16,8% | 2,3% |
Angka yang paling menarik: 131 Champion (10,9% dari pelanggan) nyumbang 41,3% omzet. Sementara 202 pelanggan yang udah hilang cuma nyumbang 2,3%.
Ini yang bikin RFM kepake. Kalau budget promo kamu terbatas, kamu jadi tau harus nyelametin siapa dulu. 159 pelanggan Hampir Hilang itu masih nyumbang 7,4% omzet, dan mereka pernah beli sering. Jauh lebih murah nyelametin mereka daripada cari 159 pelanggan baru.
1. Pakai CURRENT_DATE sebagai patokan recency. Bikin hasil analisis nggak bisa dibandingin antar periode. Kunci tanggalnya.
2. Lupa filter transaksi yang batal atau refund. Pelanggan yang beli terus balikin barang bisa masuk segmen Champion. Tambahin WHERE status = 'selesai' di CTE pertama.
3. Pakai COUNT(*) buat frequency. Kalau tabel kamu satu baris per item (bukan per transaksi), COUNT(*) ngitung item, bukan kunjungan. Pakai COUNT(DISTINCT transaksi_id).
4. Bikin 11 segmen padahal cuma sanggup jalanin 3 kampanye. Segmen yang nggak ada follow-up-nya cuma bikin tabel kelihatan rame.
RFM analysis adalah metode segmentasi pelanggan yang cuma pakai tiga angka dari data transaksi: Recency (berapa hari sejak belanja terakhir), Frequency (berapa kali belanja), dan Monetary (total uang yang dibelanjain). Ketiganya dikasih skor 1 sampai 5, terus digabung jadi segmen. Metodenya udah dipakai di direct marketing sejak 90-an dan masih jalan karena datanya udah ada di tabel transaksi kamu.
NTILE(5) bagi pelanggan kamu ke 5 kelompok sama rata berdasarkan urutan. Skornya jadi relatif ke basis pelanggan kamu sendiri, bukan ke angka absolut yang kamu tebak. Kalau kamu pakai threshold manual kayak belanja di atas 1 juta sama dengan skor 5, angka itu bakal basi begitu bisnisnya tumbuh. NTILE ngikutin data.
Karena recency yang bagus itu angkanya kecil. Pelanggan yang belanja 3 hari lalu lebih berharga daripada yang belanja 300 hari lalu. Jadi waktu pakai NTILE, urutannya dibalik pakai ORDER BY recency_hari DESC, biar yang harinya paling sedikit dapat skor 5. Frequency dan monetary kebalikannya, makin gede makin bagus.
Standarnya 12 bulan terakhir. Kalau siklus belinya cepat kayak makanan, 6 bulan udah cukup. Kalau produknya mahal dan jarang dibeli kayak elektronik, 24 bulan lebih masuk akal. Patokan gampangnya: rentang data harus cukup panjang buat nampung 2-3 kali siklus beli normal pelanggan kamu.
Sebanyak jumlah aksi yang beneran bisa kamu jalanin. Kalau tim marketing cuma sanggup jalanin 4 kampanye beda, jangan bikin 11 segmen. Query di artikel ini pakai 6 segmen dan itu udah cukup buat kebanyakan UMKM.
Yang perlu kamu inget:
Query di atas siap kamu copy dan jalanin. Kalau CTE dan window function masih bikin pusing, mulai dari panduan window function SQL dulu, setelah itu query RFM ini bakal kebaca kayak kalimat biasa.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Analisa per kuartal, hari kerja, atau musim jadi ribet kalau tiap query ngitung ulang atribut tanggal. Tabel kalender nyimpen semua atribut itu sekali, biar tinggal di-JOIN.
Laporan penjualan harian sering bolong di hari tanpa transaksi. Ini cara bikin deret tanggal lengkap di SQL biar tiap hari muncul, walau nilainya nol.
Struktur organisasi tersimpan sebagai kolom id_atasan yang saling nunjuk. Ini cara narik rantai jabatan, hitung total bawahan, dan span of control cuma pakai SQL.