TL;DR
SUM di SQL menjumlahkan semua nilai angka di sebuah kolom, sementara AVG menghitung rata-ratanya. Dua-duanya diam-diam melewati baris yang nilainya NULL, jadi AVG membagi dengan jumlah baris non-NULL, bukan jumlah baris total. Kalau kamu mau NULL dihitung sebagai nol, bungkus kolomnya pakai COALESCE(kolom, 0) dulu.
SUM di SQL menjumlahkan angka di sebuah kolom. AVG ngitung rata-ratanya. Dua fungsi ini yang paling sering kamu pakai begitu selesai belajar SELECT.
SELECT SUM(total) AS total_omzet,
AVG(total) AS rata_rata_transaksi
FROM penjualan;
Sampai sini gampang. Yang bikin banyak analis kejeblos: dua-duanya ngelewatin baris NULL tanpa bunyi.
Nggak ada warning. Nggak ada error. Angka rata-rata kamu cuma... salah.
SUM dan AVG adalah fungsi agregat, fungsi yang ngambil banyak baris dan ngeluarin satu angka. SUM menjumlahkan seluruh nilai di kolom yang kamu sebut. AVG membagi jumlah itu dengan banyaknya baris yang nilainya bukan NULL. Dua-duanya cuma jalan di kolom angka.
Kalau kamu belum kenal fungsi agregat lain, mampir dulu ke panduan fungsi agregat SQL. Di situ COUNT, MIN, dan MAX dibahas sekalian.
Pakai dataset toko_berkah, 10 transaksi Januari 2026. Kolomnya: id_transaksi, tanggal, kota, kategori, total, ongkir.
| id | kota | kategori | total | ongkir |
|---|---|---|---|---|
| 1 | Bandung | Minuman | 30.000 | NULL |
| 2 | Bandung | Makanan | 45.000 | 10.000 |
| 3 | Surabaya | Minuman | 45.000 | 12.000 |
| 4 | Surabaya | Sembako | 120.000 | NULL |
| 5 | Medan | Makanan | 90.000 | 15.000 |
| 6 | Bandung | Sembako | 120.000 | 10.000 |
| 7 | Medan | Minuman | 60.000 | NULL |
| 8 | Surabaya | Makanan | 45.000 | 12.000 |
| 9 | Bandung | Minuman | 30.000 | 10.000 |
| 10 | Medan | Sembako | 120.000 | 15.000 |
Ongkir NULL artinya pelanggan ambil sendiri di toko. Bukan gratis ongkir, emang nggak ada ongkirnya.
SELECT SUM(total) AS total_omzet,
AVG(total) AS rata_rata_transaksi
FROM penjualan;
-- total_omzet | rata_rata_transaksi
-- 705000 | 70500
Omzet Januari 705.000, rata-rata per transaksi 70.500. Dua angka ini aman.
Coba hitung rata-rata ongkir dengan dua cara.
SELECT AVG(ongkir) AS avg_ongkir,
SUM(ongkir) * 1.0 / COUNT(*) AS ongkir_per_transaksi
FROM penjualan;
-- avg_ongkir | ongkir_per_transaksi
-- 12000 | 8400
Dua-duanya bener. Dua-duanya jawab pertanyaan yang beda.
Dari 10 transaksi toko_berkah, cuma 7 yang ada ongkirnya. AVG(ongkir) bagi 7 dan dapat 12.000. SUM(ongkir)/COUNT(*) bagi 10 dan dapat 8.400. Selisihnya 43%, dan nggak ada pesan error apa pun.
Jadi pertanyaannya: kamu mau tau rata-rata ongkir dari orang yang bayar ongkir, atau rata-rata biaya ongkir yang ditanggung per transaksi?
Kalau jawabannya yang kedua, paksa NULL jadi nol.
SELECT AVG(COALESCE(ongkir, 0)) AS avg_ongkir_semua
FROM penjualan;
-- 8400
COALESCE ngeganti setiap NULL jadi angka yang kamu tentuin. Penyebutnya balik jadi 10. Kalau kamu belum familiar sama perilaku NULL, baca dulu glosarium NULL.
Aturan simpelnya: setiap kali kamu nulis AVG di kolom yang boleh kosong, tanya ke diri sendiri. NULL di sini artinya "nol" atau "nggak tau"? Kalau nol, pakai COALESCE. Kalau nggak tau, biarin AVG ngelewatin dia.
Tambahin GROUP BY. SUM dan AVG bakal dihitung ulang per grup.
SELECT kota,
COUNT(*) AS jumlah_transaksi,
SUM(total) AS total_omzet,
ROUND(AVG(total), 0) AS rata_rata
FROM penjualan
GROUP BY kota
ORDER BY total_omzet DESC;
| kota | jumlah_transaksi | total_omzet | rata_rata |
|---|---|---|---|
| Medan | 3 | 270.000 | 90.000 |
| Bandung | 4 | 225.000 | 56.250 |
| Surabaya | 3 | 210.000 | 70.000 |
Ini insight yang nggak kelihatan dari angka total doang: Medan cuma 3 transaksi (paling sedikit bareng Surabaya), tapi omzetnya paling gede. Rata-rata transaksinya 90.000, hampir dua kali lipat Bandung.
Bandung ramai tapi keranjangnya kecil. Medan sepi tapi belanjanya besar. Dua kota ini butuh strategi yang beda, dan kamu nggak akan nyadar kalau cuma lihat SUM.
ROUND aku pakai biar hasilnya rapi. Tanpa itu, AVG suka ngeluarin angka kayak 56249.999999999.
Cara paling gampang pakai WHERE.
SELECT SUM(total) AS omzet_sembako
FROM penjualan
WHERE kategori = 'Sembako';
-- 360000
Tapi kalau kamu mau dua angka sekaligus dalam satu baris hasil, WHERE nggak cukup. Pakai CASE di dalam SUM.
SELECT SUM(CASE WHEN kategori = 'Sembako' THEN total ELSE 0 END) AS omzet_sembako,
SUM(CASE WHEN kategori <> 'Sembako' THEN total ELSE 0 END) AS omzet_lainnya
FROM penjualan;
-- omzet_sembako | omzet_lainnya
-- 360000 | 345000
Teknik ini namanya conditional aggregation. Sekali scan tabel, dapat beberapa metrik. Berguna banget waktu bikin ringkasan dashboard.
Aturan lengkap soal perilaku fungsi agregat ada di dokumentasi PostgreSQL untuk aggregate functions.
SUM(ongkir) / COUNT(*) di kolom INTEGER hasilnya dipotong jadi bulat. Kaliin 1.0 atau pakai CAST dulu.-- Salah: WHERE nggak bisa lihat hasil SUM
-- SELECT kota, SUM(total) FROM penjualan WHERE SUM(total) > 220000 GROUP BY kota;
-- Benar: pakai HAVING
SELECT kota, SUM(total) AS total_omzet
FROM penjualan
GROUP BY kota
HAVING SUM(total) > 220000;
SUM menjumlahkan nilai di dalam kolom, COUNT ngitung berapa banyak barisnya. SUM(total) dari 10 transaksi toko_berkah ngasih 705.000, sementara COUNT(*) dari data yang sama ngasih 10. Sering dipakai bareng: SUM buat omzet, COUNT buat jumlah transaksi.
Gara-gara NULL. AVG cuma bagi dengan baris yang terisi, SUM/COUNT(*) bagi dengan semua baris. Di toko_berkah, AVG(ongkir) ngasih 12.000 sementara SUM(ongkir)/COUNT(*) ngasih 8.400. Selisih 43% tanpa satu pun pesan error.
Nggak. SUM cuma jalan di kolom numerik. Kalau angkanya kesimpen sebagai teks, PostgreSQL bakal nolak, sementara MySQL diam-diam ngonversi dan kasih hasil aneh. Konversi dulu pakai CAST(kolom AS DECIMAL).
Pakai ROUND. Contohnya ROUND(AVG(total), 0) buat bulatin ke rupiah terdekat, atau ROUND(AVG(total), 2) buat dua desimal.
COALESCE(kolom, 0) sebelum di-AVG.Semua query di artikel ini udah dites dan siap kamu copy-paste. Latihan langsung di halaman fungsi SUM, editor SQL-nya jalan di browser, nggak perlu install database.
Lanjut baca: MIN dan MAX SQL buat nyari transaksi terbesar dan terkecil dari tabel yang sama.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Analisa per kuartal, hari kerja, atau musim jadi ribet kalau tiap query ngitung ulang atribut tanggal. Tabel kalender nyimpen semua atribut itu sekali, biar tinggal di-JOIN.
Laporan penjualan harian sering bolong di hari tanpa transaksi. Ini cara bikin deret tanggal lengkap di SQL biar tiap hari muncul, walau nilainya nol.
Struktur organisasi tersimpan sebagai kolom id_atasan yang saling nunjuk. Ini cara narik rantai jabatan, hitung total bawahan, dan span of control cuma pakai SQL.