Menghitung Modus (Nilai Terbanyak) di SQL
TL;DR
Modus adalah nilai yang paling sering muncul di sebuah kolom. Di SQL, cara paling portabel menghitungnya adalah GROUP BY kolomnya, COUNT jumlah kemunculan, lalu ORDER BY hitungan itu menurun dan ambil baris teratas. PostgreSQL punya jalan pintas lewat MODE() WITHIN GROUP. Buat data yang punya lebih dari satu modus, pakai window function RANK biar semua nilai dengan frekuensi tertinggi ikut kebawa.
Modus di SQL adalah nilai yang paling sering muncul di sebuah kolom. Beda sama rata-rata atau median, modus jawab pertanyaan "nilai mana yang paling sering kejadian?".
Masalahnya, kebanyakan database nggak punya satu fungsi MODE() yang jalan di mana-mana. Tapi kamu tetap bisa ngitungnya cuma pakai GROUP BY, COUNT, dan ORDER BY.
Di bawah ini aku bahas cara portabel yang jalan di semua database, jalan pintas khusus PostgreSQL, cara nanganin modus ganda, sampai modus per cabang di dataset toko_berkah.
Apa itu modus dan kapan dipakai?
Modus adalah nilai yang paling sering muncul dalam satu kumpulan data. Kalau metode bayar di 100 transaksi isinya 60 QRIS, 30 tunai, 10 kartu, modusnya QRIS. Modus dipakai buat data kategori: metode bayar, kota, produk, status. Buat data kategori kayak gini, rata-rata dan median nggak ada artinya, cuma modus yang masuk akal.
Kalau kamu masih nyampur konsep modus, median, dan mean, aku pisahin bedanya di glosarium modus. Buat median, ada pembahasan sendiri di Menghitung Median dan Percentile di SQL.
Gimana cara menghitung modus di SQL?
Cara paling portabel: kelompokin nilainya, hitung berapa kali muncul, urutin dari yang paling banyak, ambil satu teratas. Empat baris, jalan di MySQL, PostgreSQL, SQL Server, SQLite, semuanya.
SELECT metode_bayar, COUNT(*) AS jumlah
FROM transaksi
GROUP BY metode_bayar
ORDER BY jumlah DESC
LIMIT 1;
GROUP BY ngumpulin baris per metode bayar. COUNT ngitung jumlah transaksi tiap metode. ORDER BY nyusun dari yang terbanyak. LIMIT 1 ngambil pemenangnya.
Di SQL Server, ganti LIMIT 1 jadi TOP 1 di depan: SELECT TOP 1 metode_bayar, COUNT(*) .... Sisanya sama.
Cara ini enak soalnya kamu juga langsung lihat angka frekuensinya, bukan cuma nama modusnya. Kalau selisih antara juara 1 dan 2 tipis, kamu langsung tau.
Cara pakai fungsi MODE() di PostgreSQL
Kalau kamu pakai PostgreSQL, ada jalan pintas. Fungsi MODE() ngambil nilai terbanyak dalam satu baris hasil.
SELECT MODE() WITHIN GROUP (ORDER BY metode_bayar) AS modus_bayar
FROM transaksi;
Sintaks WITHIN GROUP (ORDER BY kolom) itu bagian wajibnya. Hasilnya satu nilai: metode bayar yang paling sering muncul. Rapi buat dipakai di dalam query yang lebih besar.
Satu catatan: kalau ada seri, MODE() cuma balikin satu nilai, yaitu yang pertama secara urutan. Jadi kalau kamu curiga ada modus ganda, cara ini bisa nyembunyiin fakta itu. Detail perilakunya ada di dokumentasi agregat PostgreSQL.
Gimana kalau modusnya lebih dari satu?
Data beneran sering punya seri. Dua produk sama-sama laku 214 kali. Kalau kamu pakai LIMIT 1, salah satunya ilang tanpa kamu sadari. Solusinya window function RANK.
WITH freq AS (
SELECT metode_bayar,
COUNT(*) AS jumlah,
RANK() OVER (ORDER BY COUNT(*) DESC) AS peringkat
FROM transaksi
GROUP BY metode_bayar
)
SELECT metode_bayar, jumlah
FROM freq
WHERE peringkat = 1;
RANK ngasih peringkat 1 ke semua nilai dengan frekuensi tertinggi. Kalau ada dua yang seri di puncak, dua-duanya dapat peringkat 1 dan dua-duanya kebawa. Nggak ada yang ketinggalan.
Ini bedanya sama LIMIT 1: LIMIT maksa milih satu, RANK jujur nunjukin kalau modusnya ternyata dua. Buat laporan yang harus akurat, aku selalu pakai versi RANK. Konsep window function ini juga aku pakai di Menghitung Persen dari Total.
Cara menghitung modus per grup
Pertanyaan yang lebih sering muncul di kerjaan: produk terlaris di tiap cabang. Ini modus per grup. Pakai ROW_NUMBER dengan PARTITION.
WITH freq AS (
SELECT cabang,
produk,
COUNT(*) AS jumlah,
ROW_NUMBER() OVER (
PARTITION BY cabang
ORDER BY COUNT(*) DESC
) AS rn
FROM transaksi
GROUP BY cabang, produk
)
SELECT cabang, produk AS produk_terlaris, jumlah
FROM freq
WHERE rn = 1;
PARTITION BY cabang bikin peringkat dihitung ulang buat tiap cabang. ROW_NUMBER ngasih nomor urut, jadi rn = 1 itu produk paling laku di cabang itu. Hasilnya satu baris per cabang.
Kalau kamu mau seri ikut kebawa di sini juga, ganti ROW_NUMBER jadi RANK. Bedanya: ROW_NUMBER maksa satu pemenang per cabang, RANK bisa balikin dua kalau seri.
Contoh kasus: modus di dataset toko_berkah
Dataset toko_berkah punya 8.412 transaksi dari 6 cabang, Januari sampai Juni 2026. Aku ngitung modus di beberapa kolom, dan dua angka bikin kaget pemiliknya.
Modus metode bayar keseluruhan: QRIS, 4.986 transaksi atau 59% dari total. Mereka kira tunai masih dominan. Ternyata udah lama ketinggalan.
Yang lebih menarik pas dipecah per cabang. Modus metode bayar beda-beda:
| Cabang | Modus metode bayar | Porsi |
|---|---|---|
| Depok | QRIS | 71% |
| Bekasi | QRIS | 63% |
| Cibinong | Tunai | 52% |
Cibinong satu-satunya cabang yang modusnya masih tunai. Ternyata lokasinya di pasar tradisional, pelanggannya beda profil. Temuan ini bikin mereka nunda pasang mesin EDC di Cibinong dan malah nambah kasir tunai.
Modus jam transaksi juga kepakai: puncaknya jam 17:00, bukan jam makan siang kayak yang mereka duga. Jadwal shift kasir ikut digeser.
Kesalahan umum waktu menghitung modus
Lupa ada modus ganda. LIMIT 1 dan MODE() cuma balikin satu nilai. Kalau ada seri, kamu nggak akan tau. Pakai RANK kalau akurasi penting.
NULL ikut kehitung. Kalau kolomnya ada NULL, GROUP BY bikin grup NULL sendiri yang kadang jadi "modus" palsu. Tambahin WHERE metode_bayar IS NOT NULL kalau NULL nggak relevan.
Modus dipakai buat data angka kontinu. Modus harga transaksi yang unik-unik hampir nggak ada gunanya, tiap nilai muncul sekali. Buat angka kontinu, median atau rata-rata lebih masuk akal.
ORDER BY salah arah. Lupa nulis DESC bikin kamu dapat nilai yang paling jarang, bukan yang paling sering. Cek selalu arah urutannya.
FAQ
Apakah SQL punya fungsi MODE bawaan?
Tergantung databasenya. PostgreSQL punya MODE() WITHIN GROUP, dan Oracle punya fungsi STATS_MODE. Tapi MySQL, SQL Server, dan SQLite nggak punya fungsi modus bawaan. Buat itu kamu hitung manual pakai GROUP BY, COUNT, dan ORDER BY. Cara manual ini portabel dan jalan di semua database, jadi aku lebih sering pakai itu biar query-nya nggak kunci ke satu jenis database.
Apa bedanya RANK dan ROW_NUMBER buat cari modus?
ROW_NUMBER selalu ngasih nomor urut unik, jadi selalu balikin tepat satu pemenang walaupun ada seri. RANK ngasih peringkat yang sama buat nilai yang seri, jadi kalau dua nilai sama-sama terbanyak, dua-duanya dapat peringkat 1 dan dua-duanya kebawa. Pakai ROW_NUMBER kalau kamu maksa satu jawaban. Pakai RANK kalau kamu mau jujur soal modus ganda.
Gimana cara cari modus kalau kolomnya ada nilai NULL?
Tambahin filter WHERE kolom IS NOT NULL sebelum GROUP BY. Kalau nggak, NULL diperlakukan sebagai satu grup sendiri dan bisa keluar sebagai modus kalau jumlahnya banyak. Tapi kadang NULL yang banyak itu justru info penting, misalnya banyak transaksi tanpa metode bayar tercatat. Jadi putuskan dulu: NULL ini mau kamu anggap nilai atau mau kamu buang.
Bisa nggak menghitung modus dan median sekaligus dalam satu query?
Bisa di PostgreSQL. Gabungin MODE() WITHIN GROUP (ORDER BY kolom) buat modus dan PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY kolom) buat median dalam satu SELECT. Keduanya fungsi ordered-set aggregate, jadi sintaksnya mirip. Di database lain yang nggak punya fungsi ini, kamu perlu subquery terpisah buat masing-masing lalu gabungin hasilnya.
Kenapa modus saya keluar lebih dari satu baris padahal pakai LIMIT 1?
Kemungkinan kamu pakai RANK atau ROW_NUMBER dengan filter peringkat = 1, bukan LIMIT 1. Versi window function memang bisa balikin banyak baris kalau ada seri di puncak. Kalau kamu beneran cuma mau satu baris apa pun yang terjadi, pakai versi GROUP BY dengan ORDER BY dan LIMIT 1. Kalau mau semua modus yang seri, versi RANK itu udah bener.
Penutup
Ringkasnya:
- Cara portabel: GROUP BY, COUNT, ORDER BY DESC, LIMIT 1. Jalan di semua database.
- PostgreSQL punya MODE() WITHIN GROUP buat versi satu baris yang rapi.
- Curiga ada seri? Pakai RANK biar semua modus kebawa, dan ROW_NUMBER dengan PARTITION buat modus per grup.
Buka satu tabel transaksi kamu, pilih kolom kategori kayak metode bayar atau kota, tempel query GROUP BY di atas. Dalam satu run kamu udah tau nilai apa yang paling sering muncul.
Mau latihan GROUP BY dan window function sambil jalanin query beneran? Coba modul di glosarium NgulikSQL, ada dataset yang bisa langsung kamu query.
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.