COUNT SQL: Bedanya COUNT(*), COUNT(kolom), dan COUNT(DISTINCT)
TL;DR
COUNT adalah fungsi agregat SQL yang menghitung jumlah baris. COUNT(*) ngitung semua baris termasuk yang isinya NULL. COUNT(nama_kolom) cuma ngitung baris yang kolom itu ada isinya — NULL dilewat. COUNT(DISTINCT nama_kolom) ngitung berapa banyak nilai unik di kolom itu, dan NULL juga dilewat. Dari tabel yang sama, tiga bentuk ini bisa ngasih tiga angka beda.
COUNT adalah fungsi SQL yang ngitung jumlah baris. Kelihatannya gak ada yang bisa salah dari fungsi sesederhana itu.
Tapi COUNT punya tiga bentuk, dan dari tabel yang sama persis, tiga-tiganya bisa ngasih angka beda. Salah pilih satu, dan kamu presentasi pakai angka yang salah — tanpa satu pun error muncul di layar.
Contoh di sini pakai tabel transaksi toko_berkah: 10 baris, 6 pelanggan, dan 3 transaksi yang rating-nya kosong.
Apa itu COUNT di SQL?
COUNT adalah fungsi agregat yang ngitung berapa banyak baris yang lolos kriteria kamu, lalu balikin satu angka.
SELECT COUNT(*) FROM transaksi;
Hasil: 10
Sepuluh baris, sepuluh transaksi. Sejauh ini gak ada drama.
Drama-nya mulai waktu kamu ganti isi kurungnya:
SELECT COUNT(*) AS semua_baris,
COUNT(rating) AS rating_terisi,
COUNT(DISTINCT id_pelanggan) AS pelanggan_unik
FROM transaksi;
Hasil: 10 | 7 | 6
Tabel yang sama. Tiga angka beda. Dan tiga-tiganya benar — buat pertanyaan yang beda.
Apa bedanya COUNT(*), COUNT(kolom), dan COUNT(DISTINCT)?
COUNT(*) ngitung semua baris, gak peduli isinya apa. COUNT(nama_kolom) cuma ngitung baris yang kolom itu ada isinya — baris yang NULL dilewat. COUNT(DISTINCT nama_kolom) ngitung berapa banyak nilai berbeda yang ada di kolom itu, dan NULL juga gak dihitung.
| Bentuk | Yang dihitung | NULL? | Hasil di data kita |
|---|---|---|---|
COUNT(*) | Semua baris | Ikut dihitung | 10 |
COUNT(rating) | Baris yang rating-nya terisi | Dilewat | 7 |
COUNT(DISTINCT id_pelanggan) | Nilai unik | Dilewat | 6 |
Terjemahan ke bahasa bisnis:
- "Ada 10 transaksi bulan ini." →
COUNT(*) - "7 dari 10 transaksi dikasih rating." →
COUNT(rating) - "10 transaksi itu datang dari 6 orang." →
COUNT(DISTINCT id_pelanggan)
Bayangin kamu lapor "toko punya 10 pelanggan bulan ini" padahal aslinya cuma 6. Bos bikin keputusan budget dari angka itu. Perilaku NULL yang jadi biang keroknya aku bahas di glosarium NULL.
Kapan pakai COUNT(*)?
Kalau pertanyaannya "ada berapa baris", pakai COUNT(*). Itu bentuk paling jujur — dia gak peduli isi kolomnya, cuma ngitung barisnya.
SELECT COUNT(*) AS transaksi_januari
FROM transaksi
WHERE tgl_transaksi BETWEEN '2026-01-01' AND '2026-01-31';
Soal COUNT(1): hasilnya sama persis, dan sama cepatnya. Mitos bahwa COUNT(1) lebih ngebut itu peninggalan database jadul. Di PostgreSQL dan MySQL modern, rencana eksekusinya identik. Pakai COUNT(*) aja — lebih jelas maksudnya.
Kapan pakai COUNT(kolom)?
Kalau kamu mau tau berapa baris yang kolom itu terisi. Ini cara paling cepat ngukur kelengkapan data.
SELECT COUNT(*) AS total_transaksi, -- 10
COUNT(rating) AS ada_rating -- 7
FROM transaksi;
Selisih 10 dan 7 itu bukan gangguan — itu informasi. 30% transaksi gak dikasih rating. Kalau kamu lagi bikin dashboard kepuasan pelanggan, angka itu yang nentuin seberapa bisa kamu percaya sama rata-rata rating-nya.
Kapan pakai COUNT(DISTINCT)?
Kalau pertanyaannya soal orang, produk, atau kota — bukan soal baris.
SELECT COUNT(DISTINCT id_pelanggan) AS pelanggan_unik
FROM transaksi;
Hasil: 6
Sepuluh transaksi, enam orang. Artinya ada yang belanja lebih dari sekali.
Metrik turunannya langsung kepakai: 10 dibagi 6 = 1,67 transaksi per pelanggan. Angka itu yang dipakai buat ngukur seberapa sering orang balik lagi.
Bahasan DISTINCT selengkapnya ada di artikel DISTINCT SQL.
Gimana hitung baris per kelompok pakai GROUP BY?
Angka total doang jarang cukup. Pecah per kota:
SELECT kota,
COUNT(*) AS jumlah_transaksi,
COUNT(DISTINCT id_pelanggan) AS pembeli_unik,
COUNT(rating) AS transaksi_dirating
FROM transaksi
GROUP BY kota
ORDER BY jumlah_transaksi DESC;
| kota | transaksi | pembeli unik | dirating |
|---|---|---|---|
| Bandung | 5 | 3 | 4 |
| Surabaya | 3 | 2 | 2 |
| Semarang | 2 | 1 | 1 |
Buat nyaring hasil hitungan, pakai HAVING — bukan WHERE:
SELECT id_pelanggan,
COUNT(*) AS jumlah_transaksi
FROM transaksi
GROUP BY id_pelanggan
HAVING COUNT(*) > 1
ORDER BY jumlah_transaksi DESC;
Ini query "cari repeat buyer". Hasilnya 3 orang: pelanggan 1 (3 transaksi), pelanggan 2 dan 6 (masing-masing 2).
WHERE COUNT(*) > 1 bakal error, soalnya waktu WHERE jalan, COUNT-nya belum dihitung. Urutan lengkapnya ada di panduan fungsi agregat SQL.
Jebakan COUNT(*) di LEFT JOIN
Ini bug yang paling sering lolos ke laporan produksi.
Kamu mau lihat berapa transaksi tiap pelanggan, termasuk yang belum pernah belanja:
-- SALAH
SELECT p.nama_pelanggan,
COUNT(*) AS jumlah_transaksi
FROM pelanggan p
LEFT JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
GROUP BY p.nama_pelanggan;
Lia Anggraini gak pernah belanja. Tapi query ini ngasih dia 1 transaksi.
Kenapa? LEFT JOIN bikin baris Lia tetap muncul, dengan semua kolom transaksi berisi NULL. COUNT(*) ngitung baris itu sebagai baris — dia gak peduli isinya NULL semua.
Perbaikannya: hitung kolom dari tabel kanan.
-- BENAR
SELECT p.nama_pelanggan,
COUNT(t.id_transaksi) AS jumlah_transaksi
FROM pelanggan p
LEFT JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
GROUP BY p.nama_pelanggan;
Sekarang Lia dapat 0. id_transaksi-nya NULL, dan COUNT(kolom) ngelewatin NULL.
Aturan yang gampang diinget: habis LEFT JOIN, jangan pakai COUNT(*). Hitung kolom dari tabel yang kanan.
Contoh kasus: audit pelanggan toko_berkah
Bu Sari punya 8 pelanggan terdaftar. Pertanyaannya: berapa yang beneran belanja, dan berapa yang cuma nama di daftar?
SELECT COUNT(*) AS pelanggan_belum_belanja
FROM pelanggan p
LEFT JOIN transaksi t ON p.id_pelanggan = t.id_pelanggan
WHERE t.id_transaksi IS NULL;
Hasil: 2
Dua nama — Lia Anggraini dan Doni Saputra — terdaftar tapi belum pernah transaksi sekali pun.
Sekarang hitungan bersyarat dalam satu query:
SELECT COUNT(*) AS total_transaksi,
COUNT(CASE WHEN total_belanja > 150000 THEN 1 END) AS transaksi_besar,
COUNT(CASE WHEN rating >= 4 THEN 1 END) AS rating_bagus
FROM transaksi;
Hasil: 10 | 4 | 5
Triknya ada di CASE. Kalau kondisinya gak kepenuhan, CASE ngasih NULL — dan COUNT ngelewatin NULL. Jadi cuma yang cocok yang keitung.
Angkanya: 4 dari 10 transaksi nilainya di atas Rp 150 ribu, tapi 4 transaksi itu nyumbang Rp 934 ribu dari total Rp 1,42 juta — 66% omzet dari 40% transaksi.
Dan rating bagus cuma 5 dari 10. Tapi ingat, 3 transaksi gak ada rating-nya sama sekali. Jadi dari 7 yang ngasih rating, 5 di antaranya bagus — 71%, bukan 50%. Beda penyebutnya, beda cerita ke bos.
Kesalahan umum pakai COUNT
1. COUNT(*) setelah LEFT JOIN
Baris tanpa pasangan tetap keitung 1. Hitung kolom dari tabel kanan: COUNT(t.id_transaksi).
2. Ngira COUNT(kolom) sama dengan COUNT(*)
Di kolom yang gak pernah NULL (kayak primary key), dua-duanya emang sama. Di kolom lain, belum tentu. Dan kamu gak akan tau bedanya sampai ada yang ngecek ulang laporannya.
3. Pakai COUNT di WHERE
WHERE COUNT(*) > 1 selalu error. Kondisi agregat tempatnya di HAVING.
4. COUNT(DISTINCT) di kolom yang salah
COUNT(DISTINCT id_transaksi) hampir selalu sama dengan COUNT(*), soalnya id transaksi emang unik per baris. Yang kamu mau biasanya COUNT(DISTINCT id_pelanggan).
5. Ngebandingin COUNT dari dua query dengan filter beda
"Bulan ini 120 transaksi, bulan lalu 95" cuma valid kalau dua query-nya pakai filter yang sama persis. Satu WHERE yang beda, dan kamu ngebandingin apel sama jeruk.
FAQ soal COUNT SQL
COUNT(*) lebih lambat dari COUNT(1)?
Nggak. Di database modern, dua-duanya diproses identik. Pilih COUNT(*) soalnya lebih jelas kebaca.
Kenapa COUNT hasilnya 0, bukan NULL, di tabel kosong?
COUNT itu satu-satunya fungsi agregat yang balikin 0 kalau gak ada baris — bukan NULL. SUM, AVG, MIN, dan MAX semuanya balikin NULL di tabel kosong. Ini bikin COUNT aman dipakai tanpa COALESCE.
COUNT(DISTINCT) bisa di beberapa kolom?
Di MySQL bisa: COUNT(DISTINCT kota, kanal). Di PostgreSQL pakai tuple: COUNT(DISTINCT (kota, kanal)). SQL Server gak dukung — gabung kolomnya jadi satu teks dulu, atau bungkus jadi subquery.
Gimana cara hitung dengan kondisi tanpa bikin query terpisah?
Pakai COUNT(CASE WHEN kondisi THEN 1 END). Baris yang gak lolos kondisi bakal jadi NULL, dan COUNT ngelewatin NULL. Di PostgreSQL ada sintaks yang lebih bersih: COUNT(*) FILTER (WHERE kondisi).
Ringkasnya
COUNT(*) ngitung baris. COUNT(kolom) ngitung yang terisi. COUNT(DISTINCT kolom) ngitung yang beda-beda.
Sebelum nulis COUNT, tanya dulu ke diri sendiri: yang ditanya ini baris, atau orang? Jawabannya nentuin bentuk mana yang kamu pakai.
Dan satu aturan yang nyelametin banyak laporan: habis LEFT JOIN, jangan pakai COUNT(*).
Coba jalanin SELECT COUNT(*), COUNT(DISTINCT id_pelanggan) FROM transaksi di data kerjaan kamu. Kalau angkanya jauh beda, kamu baru aja nemu insight yang selama ini kelewat. Lanjut kulik semua fungsi agregat lain di halaman fungsi COUNT.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel Terkait
7 SQL Client Gratis Terbaik buat Analis Data (2026)
Nggak perlu bayar buat nulis query. Ini 7 SQL client gratis yang beneran dipakai analis, plus kelebihan dan kekurangannya masing-masing.
SQL Style Guide: Cara Nulis Query yang Enak Dibaca Tim
Sepuluh aturan SQL style guide yang bikin query kamu kebaca sama tim — dan sama kamu sendiri 6 bulan lagi. Lengkap dengan contoh sebelum-sesudah.
Take-Home Test SQL Data Analyst: Contoh Soal + Cara Ngerjainnya
Contoh soal take-home test SQL yang beneran dipakai perusahaan, dikerjain step by step — dari baca soal, nulis query, sampai nyusun insight yang bikin recruiter nengok.