Fungsi Agregat SQL: COUNT, SUM, AVG, MIN, MAX dalam 1 Panduan
TL;DR
Fungsi agregat SQL adalah fungsi yang meringkas banyak baris jadi satu angka: COUNT (hitung baris), SUM (total), AVG (rata-rata), MIN (nilai terkecil), dan MAX (nilai terbesar). Semuanya dipakai di klausa SELECT dan biasanya digabung sama GROUP BY buat ngitung per kelompok. Kecuali COUNT(*), semua fungsi agregat ngelewatin baris yang nilainya NULL.
Fungsi agregat SQL adalah fungsi yang meringkas banyak baris jadi satu angka. Sepuluh ribu transaksi masuk, satu angka omzet keluar.
Ada lima yang standar dan ada di semua database: COUNT, SUM, AVG, MIN, MAX. Kuasain lima ini dan kamu udah bisa bikin 80% laporan yang diminta kantor.
Contoh di sini pakai tabel transaksi toko_berkah: 10 baris, 3 kota, dan kolom rating yang sengaja ada NULL-nya — soalnya gak semua pelanggan ngasih rating.
Apa itu fungsi agregat di SQL?
Fungsi agregat adalah fungsi yang ngambil banyak baris sebagai input dan ngeluarin satu nilai sebagai output. Ditulis di klausa SELECT, dan biasanya dipasangin sama GROUP BY.
| Fungsi | Kerjanya | Contoh pertanyaan bisnis |
|---|---|---|
COUNT | Hitung jumlah baris | Ada berapa transaksi bulan ini? |
SUM | Jumlahkan nilai | Berapa total omzetnya? |
AVG | Rata-rata | Rata-rata belanja per struk berapa? |
MIN | Nilai terkecil | Transaksi paling kecil berapa? |
MAX | Nilai terbesar | Transaksi paling gede berapa? |
Kelimanya bisa dipakai bareng dalam satu query:
SELECT COUNT(*) AS jumlah_transaksi,
SUM(total_belanja) AS total_omzet,
AVG(total_belanja) AS rata_rata_belanja,
MIN(total_belanja) AS belanja_terkecil,
MAX(total_belanja) AS belanja_terbesar
FROM transaksi;
| jumlah_transaksi | total_omzet | rata_rata | terkecil | terbesar |
|---|---|---|---|---|
| 10 | 1.420.500 | 142.050 | 45.000 | 310.000 |
Sepuluh baris jadi satu baris. Tanpa GROUP BY, seluruh tabel dianggap satu kelompok besar.
Daftar resmi fungsi agregat ada di dokumentasi aggregate functions PostgreSQL.
Gimana cara pakai COUNT di SQL?
COUNT punya tiga bentuk, dan tiga-tiganya ngasih angka beda:
SELECT COUNT(*) AS semua_baris,
COUNT(rating) AS rating_terisi,
COUNT(DISTINCT id_pelanggan) AS pelanggan_unik
FROM transaksi;
Hasil: 10 | 7 | 6
COUNT(*)— hitung semua baris, gak peduli isinya. Hasilnya 10.COUNT(rating)— hitung baris yang rating-nya gak NULL. Hasilnya 7, soalnya 3 transaksi gak dikasih rating.COUNT(DISTINCT id_pelanggan)— hitung pelanggan unik. Hasilnya 6, walaupun ada 10 transaksi.
Beda 10 versus 6 itu penting banget. "Ada 10 transaksi" dan "ada 10 pelanggan" itu dua klaim yang jauh beda, dan salah pilih COUNT bikin kamu ngasih angka salah ke bos.
Detail COUNT lengkapnya ada di halaman fungsi COUNT.
Gimana cara pakai SUM dan AVG?
SUM jumlahin isi kolom. AVG rata-ratain.
SELECT SUM(total_belanja) AS total_omzet,
AVG(total_belanja) AS rata_rata
FROM transaksi;
Hasil: 1.420.500 | 142.050
Dua-duanya cuma jalan di kolom angka. Nyoba SUM(kota) bakal langsung error.
AVG suka ngasih desimal panjang. Bungkus pakai ROUND biar rapi:
SELECT ROUND(AVG(total_belanja), 0) AS rata_rata_bulat
FROM transaksi;
Argumen SUM selengkapnya ada di halaman fungsi SUM.
Gimana cara pakai MIN dan MAX?
MIN ngambil nilai terkecil, MAX terbesar. Bedanya sama SUM dan AVG: dua fungsi ini juga jalan di kolom teks dan tanggal.
SELECT MIN(tgl_transaksi) AS transaksi_pertama,
MAX(tgl_transaksi) AS transaksi_terakhir,
MIN(kota) AS kota_pertama_alfabet
FROM transaksi;
Hasil: 2026-01-05 | 2026-01-24 | Bandung
MIN dan MAX di kolom tanggal itu senjata buat ngitung rentang aktivitas pelanggan — kapan dia pertama beli, kapan terakhir muncul.
Satu hal yang sering bikin salah paham: MAX(total_belanja) ngasih kamu angka transaksi terbesar, tapi gak ngasih tau siapa pelakunya. Buat itu kamu butuh subquery atau window function — topik terpisah.
Gimana bikin agregat per kelompok pakai GROUP BY?
Angka total doang jarang cukup. Yang ditanya bos biasanya: "per kota berapa?"
SELECT kota,
COUNT(*) AS jumlah_transaksi,
COUNT(DISTINCT id_pelanggan) AS pelanggan_unik,
SUM(total_belanja) AS total_omzet,
ROUND(AVG(total_belanja), 0) AS rata_rata,
MAX(total_belanja) AS transaksi_terbesar
FROM transaksi
GROUP BY kota
ORDER BY total_omzet DESC;
| kota | transaksi | pelanggan unik | omzet | rata-rata | terbesar |
|---|---|---|---|---|---|
| Bandung | 5 | 3 | 868.500 | 173.700 | 310.000 |
| Surabaya | 3 | 2 | 419.000 | 139.667 | 199.000 |
| Semarang | 2 | 1 | 133.000 | 66.500 | 88.000 |
Aturan wajibnya cuma satu: tiap kolom di SELECT harus ikut di GROUP BY, atau dibungkus fungsi agregat. Nulis SELECT kota, tgl_transaksi, SUM(...) tanpa masukin tgl_transaksi ke GROUP BY bakal error — database gak tau harus nampilin tanggal yang mana dari 5 baris Bandung.
Apa bedanya WHERE dan HAVING?
WHERE nyaring baris, sebelum dikelompokin. HAVING nyaring grup, setelah dihitung.
SELECT kota,
SUM(total_belanja) AS total_omzet
FROM transaksi
WHERE tgl_transaksi >= '2026-01-10' -- saring baris dulu
GROUP BY kota
HAVING SUM(total_belanja) > 300000 -- baru saring grupnya
ORDER BY total_omzet DESC;
Coba tulis WHERE SUM(total_belanja) > 300000 dan kamu bakal langsung kena error. Waktu WHERE jalan, SUM-nya belum ada — belum ada grup yang dijumlahin.
Urutan eksekusinya: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY.
Tips performa: taruh kondisi biasa di WHERE, jangan di HAVING. Makin sedikit baris yang masuk ke GROUP BY, makin ringan kerjaan database.
Gimana fungsi agregat memperlakukan NULL?
Ini yang paling sering bikin angka laporan salah diam-diam.
Semua fungsi agregat ngelewatin NULL — kecuali COUNT(*).
SELECT AVG(rating) AS avg_rating, -- 4.0
SUM(rating) * 1.0 / COUNT(*) AS versi_salah -- 2.8
FROM transaksi;
Total rating = 28. Baris yang ada rating-nya = 7. Total baris = 10.
AVG bagi 28 dengan 7 → 4,0. Kalau kamu ngitung manual pakai SUM/COUNT(*), kamu bagi 28 dengan 10 → 2,8.
Selisih 4,0 versus 2,8 itu gede. Satu bilang pelanggan puas, satu bilang biasa aja. Dan dua-duanya "benar" — tergantung kamu mau jawab pertanyaan yang mana: rata-rata rating dari yang ngasih rating, atau rata-rata rating dari semua transaksi (nganggep yang gak ngasih rating = 0).
Yang bahaya itu kalau kamu gak sadar bedanya. Perilaku NULL lengkapnya ada di glosarium NULL.
Contoh kasus: cari repeat buyer toko_berkah
Bu Sari mau tau pelanggan mana yang balik lagi, dan berapa jarak antara pembelian pertama dan terakhir mereka.
SELECT id_pelanggan,
COUNT(*) AS jumlah_transaksi,
SUM(total_belanja) AS total_belanja,
MIN(tgl_transaksi) AS transaksi_pertama,
MAX(tgl_transaksi) AS transaksi_terakhir
FROM transaksi
GROUP BY id_pelanggan
HAVING COUNT(*) > 1
ORDER BY total_belanja DESC;
| pelanggan | transaksi | total | pertama | terakhir |
|---|---|---|---|---|
| 1 | 3 | 501.000 | 2026-01-05 | 2026-01-18 |
| 2 | 2 | 220.000 | 2026-01-06 | 2026-01-15 |
| 6 | 2 | 133.000 | 2026-01-21 | 2026-01-24 |
Tiga dari enam pelanggan balik lagi. Pelanggan 1 belanja 3 kali dalam 13 hari, total Rp 501 ribu — sendirian nyumbang 35% dari total omzet Rp 1,42 juta.
Angka HAVING COUNT(*) > 1 itu yang misahin "pelanggan" dan "repeat buyer". Tanpa HAVING, kamu dapat daftar semua orang. Dengan HAVING, kamu dapat daftar orang yang layak dikasih diskon loyalitas.
Kesalahan umum pakai fungsi agregat
1. Nulis kolom biasa di SELECT tanpa masukin ke GROUP BY
SELECT kota, tgl_transaksi, SUM(total_belanja) FROM transaksi GROUP BY kota; bakal error di PostgreSQL. MySQL kadang ngizinin, dan itu justru lebih bahaya — dia bakal ngasih tanggal acak dari salah satu baris, dan kamu gak sadar.
2. Pakai WHERE buat nyaring hasil agregat
WHERE COUNT(*) > 1 selalu error. Kondisi yang ngelibatin fungsi agregat wajib di HAVING.
3. Ngira COUNT(*) sama dengan COUNT(kolom)
Bedanya cuma NULL, tapi selisihnya bisa bikin laporan salah. Kalau kamu mau tau "berapa transaksi", pakai COUNT(*). Kalau mau tau "berapa yang ngisi rating", pakai COUNT(rating).
4. COUNT(*) setelah LEFT JOIN
LEFT JOIN bikin pelanggan tanpa transaksi tetap muncul, dengan kolom transaksi berisi NULL. COUNT(*) bakal ngitung baris itu sebagai 1 — padahal transaksinya nol. Pakai COUNT(t.id_transaksi) biar NULL-nya dilewat dan hasilnya 0.
5. Rata-rata dari rata-rata
AVG dari kolom yang isinya udah rata-rata bakal ngasih angka salah, soalnya tiap grup punya jumlah baris beda. Rata-rata dari 3 kota bukan rata-rata nasional — kecuali tiap kota transaksinya sama banyak.
FAQ soal fungsi agregat SQL
Fungsi agregat bisa dipakai tanpa GROUP BY?
Bisa. Tanpa GROUP BY, seluruh tabel dianggap satu kelompok dan hasilnya satu baris. GROUP BY baru dibutuhin kalau kamu mau angka per kota, per bulan, atau per kategori.
Bisa gak nge-nest fungsi agregat?
SUM(AVG(total_belanja)) bakal error di kebanyakan database. Kalau kamu butuh agregat dari hasil agregat, bungkus lapisan pertamanya jadi subquery, terus agregasi lagi di lapisan luar.
Kenapa SUM hasilnya NULL, bukan 0?
Kalau gak ada satu pun baris yang lolos filter, SUM balikin NULL — bukan 0. Ini sering bikin laporan nampilin sel kosong. Bungkus pakai COALESCE: COALESCE(SUM(total_belanja), 0).
Selain lima itu, ada fungsi agregat lain?
Ada, tapi beda-beda per database. PostgreSQL punya STRING_AGG buat gabungin teks dan ARRAY_AGG. MySQL punya GROUP_CONCAT. Buat statistik ada STDDEV dan VARIANCE. Kuasain lima yang standar dulu — itu yang kepakai tiap hari.
Ringkasnya
COUNT ngitung, SUM njumlahin, AVG ngerata-ratain, MIN dan MAX nyari ujungnya. GROUP BY yang bikin semuanya jadi per kelompok.
Dua hal yang paling sering bikin angka salah: NULL dilewat sama semua fungsi kecuali COUNT(*), dan kondisi agregat wajib di HAVING bukan WHERE.
Coba jalanin query agregat per kota di data kerjaan kamu, terus bandingin COUNT(*) sama COUNT(DISTINCT id_pelanggan). Selisihnya sering bikin kaget — dan itu insight pertama kamu.
Buat ngerapiin dasar query-nya, mampir ke panduan SELECT SQL atau DISTINCT SQL.
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.