Membedakan Pelanggan Baru vs Lama dengan SQL (2026)
TL;DR
Buat membedakan pelanggan baru dan lama di SQL, cari dulu tanggal order pertama tiap pelanggan pakai window function MIN(order_date) OVER (PARTITION BY customer_id). Kalau tanggal order sama dengan order pertamanya, dia pelanggan baru di periode itu; kalau lebih baru, dia pelanggan lama yang balik lagi. Dari sini kamu bisa hitung rasio baru vs lama per bulan buat ngukur pertumbuhan dan retensi sekaligus.
Buat membedakan pelanggan baru dan lama di SQL, kamu cukup cari tanggal order pertama tiap pelanggan, lalu bandingin sama tanggal tiap transaksinya.
Kedengeran sepele, tapi ini yang misahin laporan "omzet naik" jadi cerita yang beneran kepakai. Omzet naik karena pelanggan baru berdatangan, atau karena yang lama makin sering balik? Dua hal itu butuh strategi beda.
Di bawah ini query lengkapnya, dari logika dasar sampai rekap per bulan yang bisa langsung masuk dashboard.
Apa definisi pelanggan baru dan lama dari data transaksi?
Pelanggan baru adalah pelanggan yang transaksinya jatuh di tanggal yang sama dengan order pertamanya. Pelanggan lama adalah yang order-nya lebih baru dari order pertama, artinya dia udah pernah belanja sebelumnya. Definisi ini murni dari tabel transaksi, gak butuh kolom penanda tambahan.
Kunci logikanya cuma satu: tiap baris transaksi harus tau kapan pelanggannya pertama kali muncul. Begitu tiap baris punya info itu, sisanya gampang.
Gimana cari tanggal order pertama tiap pelanggan?
Pakai window function. Fungsi MIN(order_date) OVER (PARTITION BY customer_id) ngasih tanggal order paling awal tiap pelanggan, tanpa ngurangin jumlah baris.
SELECT
customer_id,
order_date,
MIN(order_date) OVER (PARTITION BY customer_id) AS first_order_date
FROM orders;
Hasilnya: tiap transaksi sekarang punya kolom first_order_date. Pelanggan yang belanja 5 kali bakal punya 5 baris, dan kelimanya nunjukin tanggal order pertama yang sama.
Ini beda dari GROUP BY. Kalau pakai GROUP BY, kamu ngeringkes data dan kehilangan detail per transaksi. Window function nahan semua baris sambil nambahin konteks. Kalau kamu belum kenal konsep ini, aku bahas dasarnya di glossary window function.
Gimana kasih label baru atau lama ke tiap transaksi?
Setelah tiap baris tau first_order_date, tinggal bandingin pakai CASE WHEN.
WITH transaksi AS (
SELECT
customer_id,
order_date,
total,
MIN(order_date) OVER (PARTITION BY customer_id) AS first_order_date
FROM orders
)
SELECT
customer_id,
order_date,
total,
CASE
WHEN order_date = first_order_date THEN 'Baru'
ELSE 'Lama'
END AS tipe_pelanggan
FROM transaksi;
Aku bungkus window function di dalam CTE (blok WITH) biar bisa dipakai ulang di query utama. WITH bikin query panjang jadi kebaca. Detail cara pakainya ada di panduan CTE SQL.
Logika CASE WHEN di sini simpel: kalau tanggal transaksi sama dengan tanggal order pertama, berarti ini kunjungan perdana, kasih label 'Baru'. Selain itu, 'Lama'. Kalau kamu mau pelajari pola CASE WHEN lain, cek CASE WHEN di SQL.
Gimana bikin rekap baru vs lama per bulan?
Ini bentuk yang paling kepakai buat dashboard. Kelompokin per bulan dan per tipe pelanggan, lalu hitung jumlah pelanggan dan omzetnya.
WITH transaksi AS (
SELECT
customer_id,
order_date,
total,
MIN(order_date) OVER (PARTITION BY customer_id) AS first_order_date
FROM orders
)
SELECT
DATE_TRUNC('month', order_date) AS bulan,
CASE
WHEN order_date = first_order_date THEN 'Baru'
ELSE 'Lama'
END AS tipe_pelanggan,
COUNT(DISTINCT customer_id) AS jumlah_pelanggan,
SUM(total) AS omzet
FROM transaksi
GROUP BY 1, 2
ORDER BY 1, 2;
Pakai COUNT(DISTINCT customer_id), bukan COUNT(*). Kalau satu pelanggan belanja dua kali di bulan yang sama, kamu gak mau dia kehitung dua kali. DISTINCT yang jaga itu.
Hasilnya tabel rapi: bulan, tipe, jumlah pelanggan, omzet. Tinggal masuk ke line chart atau stacked bar, dan tren baru vs lama langsung kelihatan.
Contoh kasus: toko_berkah Semester 1 2026
Dataset toko_berkah punya 8.412 transaksi dari 2.147 pelanggan, periode Januari sampai Juni 2026. Waktu query rekap di atas dijalanin, ada pola yang gak kelihatan dari grafik omzet biasa.
| Bulan | Pelanggan Baru | Pelanggan Lama | % Lama |
|---|---|---|---|
| Januari | 612 | 0 | 0% |
| Maret | 388 | 241 | 38% |
| Juni | 194 | 456 | 70% |
Bulan Januari semua pelanggan otomatis "Baru", wajar karena itu awal periode data. Yang menarik Juni: 70% transaksi datang dari pelanggan lama. Toko ini gak lagi tumbuh dari orang baru, dia tumbuh dari yang balik.
Omzet dari pelanggan lama di Juni tembus Rp 61 juta, sementara dari pelanggan baru cuma Rp 18 juta. Rata-rata belanja pelanggan lama Rp 134 ribu, hampir 1,5x pelanggan baru yang Rp 93 ribu. Pelanggan yang balik belanja lebih besar.
Buat toko_berkah, angka ini geser prioritas. Budget promo yang tadinya buat narik orang baru, sebagian dialihin ke program loyalitas. Analisa lanjutannya bisa pakai cohort analysis dengan SQL buat lihat angkatan mana yang paling setia.
Kesalahan umum saat misahin pelanggan
Pakai COUNT(*) bukan COUNT(DISTINCT). Ini bikin pelanggan yang belanja berkali-kali kehitung berulang. Jumlah pelanggan jadi bengkak.
Bandingin pakai tanggal padahal butuh presisi jam. Kalau bisnismu order-nya rapat (kayak ojek online), pakai timestamp penuh, bukan cuma tanggal.
Lupa periode awal selalu 100% baru. Bulan pertama data pasti nunjukin semua pelanggan baru, soalnya gak ada riwayat sebelumnya. Ini bukan bug, tapi jangan diambil kesimpulan.
Nge-join tabel tanpa filter tanggal. Kalau tabel orders campur sama order yang dibatalin atau refund, angkanya melenceng. Filter status dulu sebelum window function jalan.
FAQ
Apa definisi pelanggan baru dan pelanggan lama di SQL?
Pelanggan baru adalah pelanggan yang order-nya jatuh di tanggal yang sama dengan order pertamanya. Pelanggan lama adalah pelanggan yang order-nya lebih baru dari order pertama, jadi dia udah pernah belanja sebelumnya. Definisi ini murni dari data transaksi, gak butuh kolom flag tambahan. Kamu tinggal bandingin order_date tiap baris sama tanggal order pertama pelanggan itu.
Kenapa pakai window function buat bedain pelanggan?
Window function MIN(order_date) OVER (PARTITION BY customer_id) ngasih tanggal order pertama tiap pelanggan tanpa ngurangin jumlah baris. Kamu tetap punya semua transaksi, tapi tiap baris sekarang tau kapan pelanggannya pertama kali muncul. Kalau pakai GROUP BY biasa, kamu kehilangan detail per transaksi. Window function nahan detailnya sambil nambahin konteks.
Gimana kalau satu pelanggan order dua kali di hari pertama?
Dua-duanya bakal dihitung sebagai baru kalau kamu bandingin per tanggal, karena tanggalnya sama dengan first_order_date. Buat hindari dobel, pakai COUNT(DISTINCT customer_id), bukan COUNT(*). Kalau kamu butuh presisi sampai jam, ganti perbandingannya jadi timestamp penuh dan pakai order pertama berdasarkan MIN timestamp, bukan MIN tanggal.
Bisa gak metode ini jalan tanpa window function?
Bisa, pakai subquery yang ngambil MIN(order_date) per customer_id lalu di-join balik ke tabel transaksi. Hasilnya sama, tapi query-nya lebih panjang dan biasanya lebih lambat di data besar. Window function lebih ringkas dan sekali jalan. Kalau database kamu gak dukung window function versi lama, subquery join jadi alternatif yang aman.
Apa beda metode ini sama cohort analysis?
Membedakan baru vs lama cuma misahin dua kelompok per periode. Cohort analysis lebih dalam: dia ngelacak tiap kelompok pelanggan berdasarkan bulan gabung, lalu ngukur berapa yang balik di bulan-bulan berikutnya. Baru vs lama itu langkah pertama. Kalau kamu udah nyaman, cohort analysis kasih gambaran retensi yang jauh lebih detail per angkatan pelanggan.
Penutup
Ringkasnya:
- Cari tanggal order pertama pakai
MIN(order_date) OVER (PARTITION BY customer_id). - Bandingin tiap transaksi sama tanggal itu pakai CASE WHEN buat kasih label Baru atau Lama.
- Rekap per bulan dengan
COUNT(DISTINCT customer_id)biar angkanya bersih.
Coba jalanin query rekap di atas ke tabel order kamu sendiri. Kalau persentase pelanggan lama naik dari bulan ke bulan, itu tanda retensi kamu sehat.
Mau lanjut ngukur pelanggan yang kabur? Baca Churn Rate SQL dan Customer Lifetime Value SQL. Sintaks window function resminya ada di dokumentasi PostgreSQL.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel terkait
Composite Index di SQL: Cara Kerja dan Urutan Kolom
Composite index itu index atas beberapa kolom sekaligus. Urutan kolomnya nentuin query mana yang kebantu, lewat aturan leftmost prefix.
Menghitung Stickiness (DAU/MAU) di SQL
Stickiness ngukur seberapa sering user balik lagi. Ini cara hitung DAU, MAU, dan rasio stickiness pakai SQL, plus contoh aplikasi UMKM.
Menghitung DAU dan MAU dengan SQL
DAU dan MAU dua metrik wajib buat produk digital. Ini cara hitungnya pakai SQL, lengkap query DISTINCT, per bulan, sampai rasio stickiness.