TL;DR
Analisis funnel yang berguna berhenti di hipotesis yang bisa diuji, bukan di grafik corong. Langkahnya: definisikan tiap tahap sebagai peristiwa yang jelas, hitung jumlah pengguna unik per tahap dalam satu query pakai COUNT DISTINCT dengan FILTER, hitung tingkat lolos antar tahap yang berurutan, lalu pecah angkanya per perangkat, kanal, dan hari. Tahap dengan tingkat lolos terendah belum tentu yang paling rugi. Cari tahap yang selisih antar segmennya paling lebar, karena di situ biasanya ada penyebab yang bisa diperbaiki.
Angka konversi turun 18% dalam sebulan, dan nggak ada yang tau bocornya di mana.
Ini cerita nyari kebocoran itu di data toko online perlengkapan rumah tangga, dari query pertama sampai hipotesis yang akhirnya dieksekusi.
Semua query di sini pakai PostgreSQL dan bisa kamu tiru dengan ganti nama tabelnya.
Karena grafik corong cuma nunjukin di mana orang berhenti, bukan kenapa. Tahap dengan tingkat lolos paling rendah belum tentu tahap yang paling rugi, dan belum tentu bisa diperbaiki. Analisis funnel yang berguna berhenti di hipotesis yang bisa diuji minggu itu juga, bukan di gambar corong yang dipajang di dashboard.
Kunci lompatan dari angka ke hipotesis ada di satu gerakan: pecah tiap tahap per segmen, lalu cari selisih paling lebar antar segmen.
Tiap tahap harus berupa peristiwa yang jelas kejadiannya, bukan niat. "Tertarik sama produk" itu bukan tahap. "Klik tombol tambah ke keranjang" itu tahap.
Funnel di kasus ini empat tahap:
| Tahap | Nama peristiwa | Artinya |
|---|---|---|
| 1 | lihat_produk | Buka halaman detail satu produk |
| 2 | tambah_keranjang | Klik tombol tambah ke keranjang |
| 3 | mulai_checkout | Masuk halaman pembayaran |
| 4 | bayar_sukses | Pembayaran terkonfirmasi |
Empat sampai lima tahap itu jumlah yang enak. Kalau funnel kamu punya sembilan tahap, gabungin dulu jadi kelompok besar, baru pecah lagi setelah ketauan kelompok mana yang bermasalah.
Satu keputusan lagi sebelum nulis query: dihitung per sesi atau per pengguna. Toko ini barangnya murah dan dibeli dalam satu kunjungan, jadi per sesi masuk akal.
Trik utamanya ngeringkas tabel peristiwa jadi satu baris per sesi dulu, dengan kolom berisi waktu paling awal tiap peristiwa. Setelah itu, urutan antar tahap gampang dicek dengan bandingin waktunya.
WITH tahap AS (
SELECT
session_id,
MIN(waktu) FILTER (WHERE event = 'lihat_produk') AS t_lihat,
MIN(waktu) FILTER (WHERE event = 'tambah_keranjang') AS t_keranjang,
MIN(waktu) FILTER (WHERE event = 'mulai_checkout') AS t_checkout,
MIN(waktu) FILTER (WHERE event = 'bayar_sukses') AS t_bayar
FROM peristiwa
WHERE waktu >= DATE '2025-09-01'
AND waktu < DATE '2025-10-01'
GROUP BY session_id
)
SELECT
COUNT(*) FILTER (WHERE t_lihat IS NOT NULL) AS lihat_produk,
COUNT(*) FILTER (WHERE t_keranjang > t_lihat) AS tambah_keranjang,
COUNT(*) FILTER (WHERE t_checkout > t_keranjang) AS mulai_checkout,
COUNT(*) FILTER (WHERE t_bayar > t_checkout) AS bayar_sukses
FROM tahap;
Klausa FILTER di situ yang bikin querynya ringkas. Dia nyaring baris mana yang masuk ke tiap agregat, tanpa perlu nulis CASE WHEN berulang. Sintaks resminya ada di dokumentasi ekspresi agregat PostgreSQL.
Perhatiin tanda lebih besar di tiap baris. Itu yang mastiin tahap berikutnya beneran terjadi setelah tahap sebelumnya. Tanpa itu, orang yang buka halaman checkout dari tautan tersimpan bakal kehitung sebagai lolos, dan angka tahap tengah bisa jadi lebih besar dari tahap sebelumnya.
Hasil satu bulan, 41.230 sesi yang lihat produk:
| Tahap | Jumlah sesi | Lolos dari tahap sebelumnya |
|---|---|---|
| lihat_produk | 41.230 | - |
| tambah_keranjang | 9.482 | 23,0% |
| mulai_checkout | 5.317 | 56,1% |
| bayar_sukses | 2.108 | 39,6% |
Tahap dengan tingkat lolos paling rendah jelas yang pertama, cuma 23%. Tapi angka itu wajar buat toko online. Orang lihat-lihat dulu itu perilaku normal.
Yang bikin curiga tahap terakhir. Orang yang udah masuk halaman pembayaran itu orang yang niat beli. Kehilangan 60,4% dari mereka itu mahal.
Pecah tahap yang mencurigakan per segmen, satu segmen dalam satu waktu. Mulai dari tiga yang paling murah dicek: perangkat, kanal masuk, dan hari.
SELECT
perangkat,
COUNT(*) FILTER (WHERE t_checkout > t_keranjang) AS checkout,
COUNT(*) FILTER (WHERE t_bayar > t_checkout) AS bayar,
ROUND(
100.0 * COUNT(*) FILTER (WHERE t_bayar > t_checkout)
/ NULLIF(COUNT(*) FILTER (WHERE t_checkout > t_keranjang), 0)
, 1) AS persen_lolos
FROM tahap
GROUP BY perangkat
ORDER BY checkout DESC;
NULLIF di penyebut itu penting. Tanpa dia, segmen yang jumlah checkout-nya nol bakal bikin querynya gagal karena pembagian dengan nol.
Hasilnya:
| Perangkat | Mulai checkout | Bayar sukses | Lolos |
|---|---|---|---|
| Android | 3.104 | 842 | 27,1% |
| iOS | 1.436 | 771 | 53,7% |
| Desktop | 777 | 495 | 63,7% |
Selisih 26,6 poin persen antara Android dan iOS di tahap yang sama persis. Itu bukan perbedaan selera, itu tanda ada yang rusak.
Pecahan berikutnya per hari nunjukin angka Android jatuh dari sekitar 52% ke 26% mulai tanggal 9 September, lalu bertahan di situ. Penurunan mendadak dengan tanggal jelas hampir selalu berarti ada yang berubah di sistem, bukan perubahan perilaku orang.
Hipotesis yang berguna nyebut tiga hal: siapa, di mana, dan kenapa. Dari data di atas, tiga kandidat muncul.
Urutan pengujiannya dari yang paling murah. Buka halaman pembayaran di HP Android itu lima menit. Ngerancang A/B testing itu dua minggu. Jangan mulai dari yang mahal.
Di kasus ini, jawabannya ada di hipotesis kedua. Satu penyedia dompet digital yang dipakai 44% pengguna Android ngubah alur konfirmasinya, dan tombol kembali ke toko nggak muncul di sebagian perangkat. Orangnya udah bayar, tapi statusnya nggak pernah balik ke sistem toko sebagai sukses.
Perkiraan nilai transaksi yang nyangkut selama 22 hari: Rp 187 juta.
Ngitung peristiwa tanpa ngecek urutannya. Ini yang bikin tahap tengah kelihatan lebih besar dari tahap sebelumnya. Selalu bandingin waktu antar tahap.
Nyampur sesi dan pengguna dalam satu funnel. Tahap awal dihitung per sesi, tahap akhir per pengguna. Angkanya jadi nggak bisa dibandingin. Pilih satu, sebutin di judul tabelnya.
Jendela waktu yang ngegantung. Kalau kamu ambil data sampai kemarin, sesi yang mulai kemarin sore mungkin belum sempat bayar. Potong jendelanya di titik yang aman, atau kasih tenggat konversi yang jelas.
Berhenti di angka total. Angka total hampir nggak pernah nunjukin penyebab. Pecah per segmen, minimal per perangkat dan per kanal, sebelum bikin kesimpulan apa pun.
Bandingin ke tolok ukur industri. Definisi tahap tiap perusahaan beda. Pembanding paling berguna itu dirimu sendiri bulan lalu, dan segmen lain di data yang sama.
Ngabaikan waktu antar tahap. Selain jumlah orang, lihat juga berapa lama jeda dari checkout ke bayar. Jeda yang tiba-tiba memanjang sering jadi tanda pertama ada yang rusak, dan biasanya kelihatan sebelum angka konversinya turun.
Langkah kelima yang paling sering dilewat. Padahal di situ jawabannya biasanya ketemu. Bentuk pemecahan lain yang berguna adalah cohort analysis, terutama kalau kamu curiga yang berubah itu kualitas pengunjung baru, bukan halamannya.
Analisis funnel ngukur berapa banyak orang yang lolos dari satu tahap ke tahap berikutnya dalam sebuah proses, misalnya dari lihat produk sampai bayar. Dipakai waktu kamu tau hasil akhirnya turun tapi belum tau turunnya di bagian mana. Hasil analisis ini nunjukin tahap mana yang paling banyak kehilangan orang, jadi perbaikanmu nggak nebak-nebak.
Biasanya karena kamu ngitung peristiwa tanpa mastiin urutannya. Orang bisa langsung buka halaman checkout dari tautan yang disimpan tanpa lewat halaman produk. Kalau query kamu cuma ngitung siapa yang pernah checkout, mereka ikut kehitung. Solusinya, tambahin syarat bahwa peristiwa tahap berikutnya harus terjadi setelah peristiwa tahap sebelumnya di sesi yang sama.
Tergantung pertanyaannya. Per sesi cocok buat ngukur mulus atau nggaknya satu kali kunjungan, misalnya nyari halaman yang error. Per pengguna cocok buat produk yang orangnya balik beberapa kali sebelum beli, misalnya barang mahal. Kalau kamu pakai per sesi di produk yang siklus belinya panjang, angka konversimu bakal kelihatan jauh lebih buruk dari kenyataan.
Nggak ada angka normal yang berlaku umum, dan ngebandingin diri ke angka industri sering nyesatin karena definisi tahapnya beda-beda. Pembanding yang paling berguna adalah dirimu sendiri di periode sebelumnya, dan segmen lain di dalam datamu. Kalau konversi pengguna Android 2,4% sementara iOS 4,1% dengan definisi yang sama persis, selisih itu jauh lebih bisa ditindaklanjuti.
Tulis hipotesis yang nyebut siapa, di mana, dan kenapa, lalu tentuin cara ngujinya. Contohnya, pengguna Android gagal di halaman pembayaran karena tombolnya ketutup papan ketik. Uji paling cepat biasanya bukan eksperimen, tapi ngecek langsung: buka halamannya di perangkat yang sama, atau lihat pesan error di log. Baru kalau perbaikannya mahal, jalanin A/B testing.
Tiga hal yang paling nentuin hasil analisis funnel.
Definisi tahap yang berupa peristiwa, bukan niat. Ini yang bikin angkamu bisa dipertanggungjawabkan.
Pemecahan per segmen. Angka total nyaris nggak pernah nunjukin penyebab, dan selisih antar segmen yang lebar hampir selalu berarti ada yang rusak.
Hipotesis yang diurutkan dari yang paling murah diuji. Buka halaman di HP itu lima menit, dan sering cukup.
Mau nerusin ke pertanyaan kanal mana yang nyumbang paling besar? Baca attribution multi-touch di SQL. Buat nyusun tampilan hasilnya, ada cheat sheet memilih grafik.
Ambil funnel kamu minggu ini, pecah tahap terakhirnya per perangkat. Lihat selisihnya berapa poin.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Kolom waktu isinya angka gede kayak 1704067200 dan bikin bingung? Itu Unix timestamp. Ini cara ngubahnya jadi tanggal beneran di SQL, plus balik lagi.
Data transaksi tersimpan UTC, tapi laporan harus jam WIB. Kalau salah konversi, angka penjualan tengah malam bisa kecatat di tanggal yang salah. Ini cara handle timezone di SQL dengan benar.
Nambah 30 hari ke tanggal invoice, ngurangin sebulan buat cari periode lalu, ngitung selisih hari antar order. Semua itu aritmetika tanggal, dan SQL punya operator INTERVAL buat ngerjainnya.