
Foto oleh Brett Sayles via Pexels
Fungsi agregat adalah fungsi yang menghitung satu nilai ringkasan dari sekumpulan baris, seperti total, rata-rata, atau jumlah data. COUNT, SUM, AVG, MIN, dan MAX adalah lima fungsi agregat paling sering dipakai di SQL Server, dan hampir setiap laporan bisnis pasti melibatkan setidaknya satu di antaranya.
Artikel ini membahas masing-masing fungsi beserta perbedaan penting antara COUNT(*) dan COUNT(kolom) yang sering disalahpahami dan menyebabkan angka laporan yang keliru.
COUNT: Menghitung Jumlah Baris
COUNT(*) vs COUNT(kolom)
COUNT(*) menghitung seluruh baris tanpa peduli nilai NULL, sementara COUNT(nama_kolom) hanya menghitung baris yang nilainya tidak NULL pada kolom tersebut. Perbedaan ini sering jadi sumber bug ketika developer mengira keduanya selalu sama.
SUM, AVG, MIN, MAX
Ringkasan statistik transaksi yang sudah terkirim
Fungsi Agregat Mengabaikan NULL
AVG dan COUNT mengabaikan baris NULL secara otomatis
SUM, AVG, MIN, dan MAX secara otomatis mengabaikan baris dengan nilai NULL pada kolom yang dihitung. Jika sebuah table memiliki 5 baris namun 2 di antaranya NULL pada kolom tertentu, AVG hanya akan membagi total dengan 3, bukan 5 — hal ini penting dipahami agar hasil rata-rata tidak disalahartikan.
Menangani NULL Secara Eksplisit dengan ISNULL atau COALESCE
ISNULL mengubah perilaku perhitungan rata-rata secara signifikan
Ketika kebutuhan bisnis mengharuskan NULL diperlakukan sebagai nol (misalnya produk tanpa diskon dianggap diskon 0%, bukan dikecualikan dari perhitungan), ISNULL atau COALESCE perlu dipakai secara eksplisit sebelum fungsi agregat.
Menggabungkan Beberapa Fungsi Agregat Sekaligus
Lima fungsi agregat dipakai bersamaan dalam satu SELECT
Dalam praktiknya, laporan bisnis jarang hanya membutuhkan satu fungsi agregat saja. Menggabungkan COUNT, SUM, AVG, MIN, dan MAX dalam satu query jauh lebih efisien dibanding menjalankan lima query terpisah, karena mesin database hanya perlu membaca data sumber satu kali untuk menghasilkan seluruh ringkasan tersebut.
Perlu diingat bahwa AVG menghitung rata-rata hanya dari baris yang nilainya tidak NULL, sehingga jika sebagian baris memiliki Quantity NULL, hasil AVG bisa berbeda dari ekspektasi jika dibandingkan dengan menghitung manual SUM dibagi COUNT(*) yang menyertakan seluruh baris termasuk yang NULL.
Perilaku Fungsi Agregat Terhadap Nilai NULL
COUNT(*) vs COUNT(kolom), dan AVG/SUM yang mengabaikan NULL
Seluruh fungsi agregat di SQL Server kecuali COUNT(*) secara otomatis mengabaikan baris dengan nilai NULL pada kolom yang dihitung. Ini berarti AVG(Discount) tidak membagi total nilai dengan jumlah seluruh baris, melainkan hanya dengan jumlah baris yang memiliki nilai Discount bukan NULL; kesalahpahaman terhadap perilaku ini bisa membuat hasil rata-rata terlihat lebih tinggi dari yang sebenarnya diharapkan jika asumsinya adalah NULL dihitung sebagai nol.
Perbedaan antara COUNT(*) dan COUNT(nama_kolom) juga sering tertukar: COUNT(*) menghitung seluruh baris tanpa peduli ada NULL atau tidak pada kolom mana pun, sedangkan COUNT(nama_kolom) hanya menghitung baris yang nilainya pada kolom tersebut bukan NULL. Jika kebutuhannya adalah menghitung berapa banyak pelanggan yang sudah mengisi nomor telepon, COUNT(Phone) adalah pilihan yang tepat, bukan COUNT(*).
Untuk kasus yang membutuhkan NULL diperlakukan sebagai nol dalam perhitungan rata-rata, misalnya memastikan AVG tetap membagi dengan seluruh jumlah baris, fungsi ISNULL atau COALESCE dapat dipakai untuk mengubah NULL menjadi 0 terlebih dahulu di dalam ekspresi, seperti AVG(ISNULL(Discount, 0)), sehingga hasil perhitungan mencerminkan maksud yang sebenarnya diinginkan.
Perbedaan COUNT(*), COUNT(kolom), dan COUNT(DISTINCT kolom)
COUNT(*) vs COUNT(kolom) vs COUNT(DISTINCT kolom)
COUNT(*) menghitung jumlah seluruh baris dalam grup atau table tanpa memandang isi kolom apa pun, termasuk baris yang semua kolomnya bernilai NULL, sehingga hasilnya selalu sama dengan jumlah baris fisik yang memenuhi kondisi WHERE. Sebaliknya, COUNT(nama_kolom) hanya menghitung baris yang nilai kolom tersebut tidak NULL, karena fungsi agregat pada SQL Server secara konsisten mengabaikan NULL kecuali pada COUNT(*).
Perbedaan ini sering menjadi sumber kebingungan ketika seseorang mengharapkan COUNT(NomorTelepon) sama dengan jumlah total baris, padahal pada kenyataannya angka tersebut hanya mencerminkan baris yang kolom NomorTelepon-nya terisi. Memilih antara keduanya harus disesuaikan dengan pertanyaan bisnis yang sebenarnya: jika pertanyaannya 'berapa total pelanggan', pakai COUNT(*); jika pertanyaannya 'berapa pelanggan yang punya nomor telepon', pakai COUNT(NomorTelepon), karena kesalahan memilih keduanya bisa membuat laporan kelengkapan data menjadi menyesatkan.
COUNT(DISTINCT kolom) menambahkan satu lapis logika lagi dengan menghitung jumlah nilai unik pada kolom tersebut, mengabaikan duplikat dan NULL sekaligus, seperti menghitung berapa kota berbeda yang dihuni pelanggan tanpa peduli berapa banyak pelanggan di tiap kota. Operasi DISTINCT di dalam COUNT memerlukan langkah pengurutan atau hashing tambahan secara internal, sehingga pada table sangat besar, COUNT(DISTINCT kolom) cenderung lebih berat secara komputasi dibanding COUNT biasa.
Menggabungkan Nilai Teks per Grup dengan STRING_AGG
STRING_AGG menggabungkan string per grup
STRING_AGG, tersedia sejak SQL Server 2017, melengkapi fungsi agregat numerik seperti SUM dan COUNT dengan kemampuan menggabungkan nilai-nilai teks dari banyak baris dalam satu grup menjadi satu string tunggal yang dipisahkan pemisah tertentu, seperti koma. Fungsi ini menggantikan trik lama yang memakai FOR XML PATH untuk mencapai hasil serupa, dengan sintaks yang jauh lebih sederhana dan mudah dibaca.
Seperti fungsi agregat lainnya, STRING_AGG wajib dipakai bersama GROUP BY ketika hasil yang diinginkan adalah satu baris gabungan per kelompok, dan secara otomatis mengabaikan nilai NULL pada kolom yang digabungkan. STRING_AGG juga mendukung klausa WITHIN GROUP (ORDER BY ...) untuk menentukan urutan kemunculan nilai di dalam string hasil gabungan, sehingga daftar produk misalnya bisa ditampilkan terurut alfabetis alih-alih mengikuti urutan baris asli yang tidak pasti.
Kesalahan Umum yang Sering Terjadi
Mengira COUNT(*) dan COUNT(kolom) selalu menghasilkan angka yang sama
Keduanya hanya identik jika kolom yang dihitung tidak pernah bernilai NULL. Pada kolom yang memperbolehkan NULL, hasil keduanya bisa berbeda signifikan dan menyebabkan laporan yang keliru.
Tidak menyadari AVG mengabaikan NULL, bukan menganggapnya nol
Developer yang mengharapkan AVG memperlakukan NULL sebagai 0 akan mendapat hasil yang lebih tinggi dari ekspektasi, karena baris NULL sebenarnya dikecualikan sepenuhnya dari perhitungan rata-rata.
Kapan Pendekatan Ini Tidak Cocok Dipakai
Menggunakan ISNULL untuk memaksa NULL menjadi 0 pada semua kasus terdengar aman, namun ini bisa mengaburkan makna data asli — misalnya 'diskon belum pernah diatur' berbeda maknanya secara bisnis dibanding 'diskon sengaja diset 0 persen'. Pertimbangkan makna bisnis dari NULL sebelum memutuskan menggantinya dengan nilai default.
Ringkasan
Kelima fungsi agregat ini menjadi dasar dari hampir seluruh laporan bisnis yang dibuat di atas SQL Server. Memahami bagaimana masing-masing memperlakukan nilai NULL akan mencegah kesalahan interpretasi data pada laporan yang dihasilkan.
Seri Belajar SQL Server:
← Sebelumnya: Filtering Hasil Group dengan HAVING
→ Selanjutnya: Menggabungkan Query dengan UNION dan UNION ALL