Idenya
Sebagian besar pertanyaan bisnis bukan "tampilkan baris-barisnya", melainkan "beri aku sebuah angka": berapa banyak pesanan yang kita dapat, berapa total pendapatan kita, berapa rata-rata gajinya? Fungsi agregat menjawabnya. Fungsi ini menerima seluruh kolom berisi nilai-nilai dan memadatkannya menjadi satu nilai.
Agregat adalah status bar di bagian bawah Excel saat kamu memilih sebuah kolom: Count, Sum, Average. SQL memberimu angka-angka yang sama, tapi untuk jutaan baris dan di dalam query yang bisa kamu simpan dan jalankan ulang.
Lima yang akan kamu pakai setiap hari
| Fungsi | Mengembalikan |
|---|---|
COUNT(*) | jumlah baris |
SUM(col) | total dari nilai-nilainya |
AVG(col) | rata-rata (mean) dari nilai-nilainya |
MIN(col) | nilai terkecil |
MAX(col) | nilai terbesar |
SELECT COUNT(*) AS products,
MIN(price) AS cheapest,
MAX(price) AS priciest,
AVG(price) AS avg_price
FROM products;
products | cheapest | priciest | avg_price
----------+----------+----------+---------------------
15 | 38000 | 320000 | 131066.666666666667
(1 row)
Lima belas baris masuk, satu baris keluar. Itulah yang dilakukan agregat.
Rata-ratanya punya banyak desimal. ROUND memperbaikinya, seperti yang kamu lihat di modul ekspresi:
SELECT ROUND(AVG(price)) AS avg_price
FROM products;
avg_price
-----------
131067
(1 row)
MIN dan MAX juga bekerja pada teks (alfabetis) dan tanggal (paling awal/paling akhir):
SELECT MIN(order_date) AS first_order,
MAX(order_date) AS last_order
FROM orders;
first_order | last_order
-------------+------------
2024-01-03 | 2024-06-29
(1 row)
Mengagregasi sebuah perhitungan
Pendapatan dari satu baris pesanan adalah quantity * unit_price. Taruh perhitungannya di dalam SUM, maka ia dihitung per baris, lalu dijumlahkan:
SELECT SUM(quantity) AS units_sold,
SUM(quantity * unit_price) AS revenue
FROM order_items;
units_sold | revenue
------------+----------
117 | 17502000
(1 row)
Agregat dengan WHERE
Biasanya kamu tidak ingin menghitung semuanya. "Berapa pesanan yang kita dapat?" kurang berguna dibanding "berapa pesanan yang benar-benar jadi?", karena pesanan yang dibatalkan tidak menghasilkan uang. Kamu bisa menggabungkan agregat dengan WHERE. WHERE berjalan lebih dulu, membuang baris-baris yang tidak cocok, dan agregat hanya melihat sisanya:
SELECT COUNT(*) AS completed_orders
FROM orders
WHERE status = 'completed';
completed_orders
------------------
26
(1 row)
Tiga jenis COUNT
"Berapa pelanggan yang kita punya?", "berapa yang sudah memberi kita email?", dan "mereka tersebar di berapa kota?" semuanya terdengar seperti pertanyaan menghitung, dan memang begitu, tapi masing-masing butuh COUNT yang sedikit berbeda. Tertukar di antaranya adalah kesalahan klasik, jadi ini ketiganya berdampingan:
SELECT COUNT(*) AS customers,
COUNT(email) AS with_email,
COUNT(DISTINCT city) AS cities
FROM customers;
customers | with_email | cities
-----------+------------+--------
12 | 10 | 8
(1 row)
COUNT(*)menghitung baris, apa pun isinya: 12 pelanggan.COUNT(column)menghitung baris yang kolomnya tidak NULL: dua pelanggan tidak punya email, jadi 10.COUNT(DISTINCT column)menghitung nilai-nilai berbeda yang tidak NULL: 8 kota (Jakarta dihitung sekali, dan kota yang tidak diketahui tidak dihitung).
Semua agregat kecuali COUNT(*) mengabaikan NULL. Itu biasanya yang kamu inginkan, tapi sadari: AVG(stock) adalah rata-rata dari 14 nilai stok yang diketahui, bukan dari ke-15 produk.
Apa yang (belum) bisa kamu lakukan
Coba tampilkan nama produk di sebelah harga tertinggi:
SELECT name, MAX(price)
FROM products;
ERROR: column "products.name" must appear in the GROUP BY clause or be used in an aggregate function
Kedengarannya masuk akal, tapi pikirkan dari sisi database: MAX(price) memadatkan 15 baris menjadi 1, sementara name masih punya 15 nilai. Nama mana yang harus ditaruh di satu baris itu? SQL menolak menebak. Error-nya menyebutkan GROUP BY, yang merupakan modul berikutnya: ia memungkinkanmu mengagregasi per kelompok, seperti harga tertinggi per kategori. (Untuk menemukan produk mana yang termahal, kamu sudah tahu cara yang lebih sederhana: ORDER BY price DESC LIMIT 1.)
Input kosong
Saat tidak ada baris yang cocok, COUNT mengembalikan 0, tapi SUM, AVG, MIN, dan MAX mengembalikan NULL, karena tidak ada yang bisa dijumlahkan:
SELECT COUNT(*) AS n, SUM(salary) AS total
FROM employees
WHERE department = 'Legal';
n | total
---+-------
0 | NULL
(1 row)
Ingat ini saat sebuah dashboard menampilkan sel kosong alih-alih 0: memang tidak ada barisnya.
Mengapa ini penting untuk kode bersih
Mengerjakan hitungan di database berarti satu query, satu angka, sama untuk semua orang. Mengunduh setiap baris ke Excel untuk menjumlahkannya itu lambat dan mengundang kesalahan copy-paste. Beri nama setiap agregat (AS revenue, AS completed_orders), dan pilih COUNT yang tepat dengan sengaja, supaya pembaca tahu persis apa yang sedang dihitung.
Rangkuman
- Agregat memadatkan banyak baris menjadi satu nilai:
COUNT,SUM,AVG,MIN,MAX. - Taruh perhitungan di dalamnya:
SUM(quantity * unit_price). WHEREmemfilter baris sebelum diagregasi.COUNT(*)menghitung baris;COUNT(col)melewati NULL;COUNT(DISTINCT col)menghitung nilai unik.- Agregat mengabaikan NULL; tanpa baris,
SUM/AVGmemberi NULL danCOUNTmemberi 0. - Kamu tidak bisa mencampur kolom biasa dengan agregat tanpa
GROUP BY.