Apa yang Sebenarnya Dilakukan Fungsi Agregat
Sebagian besar fungsi SQL yang sudah kamu temui sejauh ini bekerja baris demi baris: UPPER(name) dijalankan sekali per baris, ROUND(price, 2) juga dijalankan sekali per baris. Fungsi agregat berbeda. Fungsi ini melihat satu kumpulan baris sekaligus lalu meringkasnya menjadi satu nilai.
Siapkan tabel kecil untuk dicoba:
Lima baris masuk, satu baris keluar. Itulah gambaran besarnya: fungsi agregat memadatkan baris menjadi ringkasan. Tanpa GROUP BY, ringkasan itu mencakup setiap baris di hasil query.
COUNT: Baris vs Nilai
COUNT punya tiga bentuk, dan perbedaannya penting:
COUNT(*)menghitung baris. NULL ikut dihitung. Selalu mengembalikan angka.COUNT(column)menghitung nilai yang bukan NULL di kolom itu.COUNT(DISTINCT column)menghitung nilai unik yang bukan NULL.
Lima baris, tiga di antaranya punya amount, tiga customer yang berbeda. Kalau kamu pernah melihat COUNT(amount) dan bingung kenapa hasilnya lebih kecil dari COUNT(*), itulah alasannya: NULL tidak dihitung.
SUM, AVG, MIN, MAX
Fungsi agregat aritmetika bekerja seperti yang kamu harapkan, dengan satu aturan yang tidak terlihat: semuanya melewati NULL:
AVG adalah (10 + 20 + 30) / 3 = 20.0, bukan 60 / 4 = 15.0. Pembaginya adalah jumlah nilai yang bukan NULL. Kalau bukan itu yang kamu mau, misalnya kamu lebih suka menganggap data yang hilang sebagai nol, tulis secara eksplisit:
MIN dan MAX juga bekerja untuk teks dan tanggal. Teks dibandingkan secara leksikografis, dan tanggal dibandingkan sebagai string ISO untuk format tanggal standar.
SUM vs TOTAL
SQLite punya fungsi agregat kedua yang mirip penjumlahan, yaitu TOTAL, yang memperbaiki dua hal menyebalkan dari SUM:
SUMdari nol baris mengembalikanNULL.TOTALmengembalikan0.0.SUMdari nilai yang semuanya NULL mengembalikanNULL.TOTALmengembalikan0.0.TOTALselalu mengembalikan bilangan floating-point, jadi tidak pernah mengalami overflow aritmetika bilangan bulat.
Kekurangannya: TOTAL tidak standar, dan hasil yang selalu REAL bisa mengejutkanmu kalau kamu mengharapkan bilangan bulat. Pakai fungsi ini kalau "tidak ada baris berarti nol" adalah jawaban yang tepat untuk aplikasimu, dan tetap pakai SUM kalau kamu ingin perilaku sesuai standar SQL.
DISTINCT di Dalam Fungsi Agregat
DISTINCT bisa dipakai di dalam fungsi agregat apa pun, tidak hanya COUNT. Ia membuang nilai duplikat sebelum agregasi berjalan:
SUM(amount) menjumlahkan amount dari setiap baris. SUM(DISTINCT amount) menjumlahkan setiap amount yang unik satu kali saja. Ini berguna untuk hal seperti "total dari nominal invoice yang unik", tapi jarang itu yang kamu butuhkan. COUNT(DISTINCT customer) adalah yang paling sering dipakai.
FILTER: Mengagregasi Sebagian Baris
Kalau kamu ingin mengagregasi hanya sebagian baris, langkah yang terpikir pertama adalah WHERE. Tapi WHERE menyaring semuanya, jadi dengan cara itu kamu tidak bisa menggabungkan "hitung pesanan yang dibayar" dan "hitung refund" dalam query yang sama. FILTER menyelesaikan masalah ini:
Setiap klausa FILTER (WHERE ...) hanya berlaku untuk satu fungsi agregat itu. Satu kali menelusuri tabel, beberapa irisan diringkas sekaligus. Sebelum FILTER ada, orang menulis SUM(CASE WHEN status = 'paid' THEN amount END): idenya sama, hanya lebih banyak mengetik.
GROUP_CONCAT: Menggabungkan String
GROUP_CONCAT berbeda dari yang lain. Alih-alih mengembalikan angka, fungsi ini menyambung nilai-nilainya menjadi satu string:
Pemisah bawaannya adalah koma. Berikan argumen kedua untuk memakai pemisah lain. Urutannya tidak dijamin kecuali kamu menulis pemanggilannya sebagai GROUP_CONCAT(tag ORDER BY tag). Ini berguna kalau output-nya tampil di UI dan kamu ingin urutannya stabil.
Agregasi Tanpa GROUP BY
Setiap contoh sejauh ini yang memakai fungsi agregat tanpa GROUP BY menghasilkan tepat satu baris. Itulah aturannya: SELECT dengan fungsi agregat dan tanpa GROUP BY adalah ringkasan satu baris dari seluruh tabel (setelah WHERE).
Kamu bisa mencampur fungsi agregat dengan bebas:
Yang tidak bisa kamu lakukan adalah mencampur kolom yang tidak diagregasi dengan fungsi agregat dan berharap hasilnya masuk akal:
-- Allowed by SQLite, but the value of `customer` is arbitrary.
SELECT customer, SUM(amount) FROM orders;
SQLite tidak akan memberi error di sini (database lain akan), tapi ia akan memilih nama customer secara acak untuk ditampilkan di samping total. Kalau kamu ingin jumlah per customer, kamu butuh GROUP BY, yang dibahas di halaman berikutnya.
Berikutnya: GROUP BY dan HAVING
Fungsi agregat atas seluruh tabel menjawab "berapa totalnya." Fungsi agregat per kelompok, misalnya per customer, per bulan, atau per status, menjawab pertanyaan yang lebih menarik. GROUP BY adalah cara membagi baris ke dalam kelompok sebelum diagregasi, dan HAVING adalah cara menyaring hasil agregasinya. Itu yang berikutnya.
Pertanyaan yang sering diajukan
Apa itu fungsi agregat di SQLite?
Fungsi agregat menerima banyak baris dan mengembalikan satu nilai ringkasan. Fungsi bawaannya adalah COUNT, SUM, AVG, MIN, MAX, TOTAL, dan GROUP_CONCAT. Tanpa GROUP BY, fungsi ini meringkas seluruh hasil query menjadi satu baris.
Apa bedanya SUM dan TOTAL di SQLite?
Keduanya menjumlahkan angka, tapi SUM mengembalikan NULL kalau semua input bernilai NULL dan memakai aritmetika bilangan bulat bila memungkinkan (yang bisa overflow). TOTAL selalu mengembalikan bilangan floating-point dan mengembalikan 0.0 kalau tidak ada baris. Pakai TOTAL kalau kamu butuh hasil berupa angka yang pasti, dan SUM kalau perilaku sesuai standar SQL itu penting.
Bagaimana cara menghitung nilai unik di SQLite?
Taruh DISTINCT di dalam pemanggilannya: COUNT(DISTINCT customer_id). Itu menghitung nilai unik yang bukan NULL. COUNT(column) biasa menghitung nilai yang bukan NULL termasuk duplikatnya, dan COUNT(*) menghitung setiap baris tanpa peduli NULL.
Apakah fungsi agregat SQLite mengabaikan NULL?
Ya. Setiap fungsi agregat kecuali COUNT(*) melewati input NULL. AVG membagi dengan jumlah nilai yang bukan NULL, bukan dengan total jumlah baris. COUNT(*) adalah pengecualiannya: ia menghitung baris, bukan nilai, jadi NULL ikut dihitung.