Laporan klasik dengan GROUP BY memangkas data menjadi baris-baris kelompok. Namun bentuk laporan yang sama sering dibutuhkan terbalik: satu baris per periode dengan beberapa kolom kategori — misalnya satu baris berisi kolom penjualan_kopi, penjualan_teh, dan penjualan_lain dalam satu kesatuan. Pola seperti ini dikerjakan dengan menyelipkan kondisi ke dalam fungsi agregat, teknik yang dikenal sebagai agregasi bersyarat (conditional aggregation). Pada chapter ini kita membangun pola SUM(CASE WHEN ...), membandingkannya dengan COUNT(IF(...)), merangkai laporan pivot sederhana satu baris multi-kolom, lalu menetapkan kapan pola ini lebih baik daripada GROUP BY dan sebaliknya.
Menyiapkan Tabel Latihan #
Tabel penjualan kecil kita kembali dipakai dengan struktur dan empat belas baris yang persis sama seperti chapter-chapter sebelumnya, sehingga setiap angka agregasi bisa dicocokkan langsung dengan hasil GROUP BY:
DROP DATABASE IF EXISTS belajar_agregasi;
CREATE DATABASE belajar_agregasi;
USE belajar_agregasi;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changedCREATE TABLE penjualan (
id INT AUTO_INCREMENT PRIMARY KEY,
tanggal DATE NOT NULL,
kota VARCHAR(50) NOT NULL,
produk VARCHAR(50) NOT NULL,
jumlah INT NOT NULL,
harga DECIMAL(10,2) NOT NULL,
pelanggan VARCHAR(50) NULL
);
INSERT INTO penjualan (tanggal, kota, produk, jumlah, harga, pelanggan) VALUES
('2026-01-05', 'Jakarta', 'Kopi Gayo', 2, 55000.00, 'Budi'),
('2026-01-05', 'Bandung', 'Teh Melati', 3, 25000.00, 'Siti'),
('2026-01-06', 'Jakarta', 'Teh Melati', 1, 25000.00, NULL),
('2026-01-07', 'Surabaya', 'Kopi Toraja', 5, 62000.00, 'Andi'),
('2026-01-08', 'Bandung', 'Gula Aren', 4, 15000.00, NULL),
('2026-01-09', 'Jakarta', 'Gula Aren', 2, 15000.00, 'Rina'),
('2026-01-10', 'Yogyakarta', 'Kopi Gayo', 3, 55000.00, NULL),
('2026-01-11', 'Surabaya', 'Teh Melati', 2, 25000.00, 'Budi'),
('2026-01-12', 'Yogyakarta', 'Kopi Toraja', 1, 62000.00, NULL),
('2026-01-13', 'Jakarta', 'Kopi Toraja', 4, 62000.00, 'Siti'),
('2026-01-14', 'Bandung', 'Kopi Gayo', 1, 55000.00, NULL),
('2026-01-15', 'Surabaya', 'Gula Aren', 6, 15000.00, 'Rina'),
('2026-01-16', 'Jakarta', 'Kopi Gayo', 1, 55000.00, 'Dewi'),
('2026-01-17', 'Bandung', 'Teh Melati', 2, 25000.00, NULL);Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 14 rows affected (0.00 sec)
Records: 14 Duplicates: 0 Warnings: 0Pola Dasar SUM(CASE WHEN …) #
Ide dasarnya: fungsi agregat menerima ekspresi apa pun, bukan sekadar nama kolom. Jika ekspresi tersebut mengembalikan nilai hanya pada baris yang memenuhi syarat — dan nol pada baris lain — maka penjumlahannya otomatis selektif. Ekspresi CASE WHEN menjdi penyaring di dalam SUM:
SELECT
SUM(CASE WHEN produk LIKE 'Kopi%' THEN jumlah ELSE 0 END) AS total_kopi,
SUM(CASE WHEN produk LIKE 'Teh%' THEN jumlah ELSE 0 END) AS total_teh,
SUM(jumlah) AS total_semua
FROM penjualan;Output yang Dihasilkan:
+------------+-----------+-------------+
| total_kopi | total_teh | total_semua |
+------------+-----------+-------------+
| 17 | 8 | 37 |
+------------+-----------+-------------+
1 row in set (0.00 sec)Mari kita bedah bagaimana angka tersebut terbentuk:
SUM(CASE WHEN produk LIKE 'Kopi%' THEN jumlah ELSE 0 END): untuk setiap baris, ekspresiCASEmengevaluasi prefiks nama produk; baris kopi (Kopi Gayo dan Kopi Toraja) menyumbang nilaijumlah-nya, baris lain menyumbang0. Penjumlahan atas empat belas nilai selektif itu menghasilkan total unit kopi saja.SUM(CASE WHEN produk LIKE 'Teh%' THEN jumlah ELSE 0 END): mekanisme sama dengan syarat prefiksTeh— hanya Teh Melati yang ikut dihitung.SUM(jumlah): agregat tanpa penyaring, menjumlahkan seluruh baris sebagai pembanding.
Kolom total_kopi dijumlahkan dengan total_teh tidak sama dengan total_semua karena masih ada kategori ketiga — Gula Aren. Selisihnya persis merupakan unit gula aren yang tidak dihitung kedua kolom selektif. Pola ELSE 0 penting: tanpa ELSE, CASE mengembalikan NULL untuk baris yang tidak cocok, dan SUM mengabaikan NULL — hasil totalnya sama-sama benar, tetapi ELSE 0 membuat niat “baris lain bernilai nol” eksplisit dan menghindari kebingungan pembaca.
COUNT(IF(…)) — Varian Ringkas #
Fungsi IF(kondisi, nilai_jika_benar, nilai_jika_salah) milik MySQL meringkas CASE WHEN satu cabang menjadi satu panggilan. Untuk menghitung banyaknya transaksi per kategori, kita hanya butuh nilai 1 atau NULL:
SELECT
COUNT(IF(produk LIKE 'Kopi%', 1, NULL)) AS transaksi_kopi,
COUNT(IF(pelanggan IS NULL, 1, NULL)) AS transaksi_tunai
FROM penjualan;Output yang Dihasilkan:
+----------------+-----------------+
| transaksi_kopi | transaksi_tunai |
+----------------+-----------------+
| 7 | 6 |
+----------------+-----------------+
1 row in set (0.00 sec)Perhitungan ini bekerja karena dua sifat yang telah kita buktikan pada chapter COUNT dan chapter IS NULL: COUNT(ekspresi) hanya menghitung nilai non-NULL, dan IF dengan cabang NULL menghasilkan NULL untuk baris yang tidak memenuhi syarat. Transaksi tunai terhitung enam — persis jumlah baris dengan pelanggan IS NULL pada data kita.
Pivot Sederhana: Kolom per Kategori #
Menggabungkan kedua pola di atas dengan GROUP BY menghasilkan laporan pivot: satu baris per kota dengan kolom kategori sebagai pemisah. Bentuk ini cocok untuk tabel ringkasan manajemen yang ingin membandingkan kategori secara berdampingan:
SELECT
kota,
SUM(CASE WHEN produk LIKE 'Kopi%' THEN jumlah ELSE 0 END) AS unit_kopi,
SUM(CASE WHEN produk LIKE 'Teh%' THEN jumlah ELSE 0 END) AS unit_teh,
SUM(CASE WHEN produk LIKE 'Gula%' THEN jumlah ELSE 0 END) AS unit_gula,
COUNT(*) AS jumlah_transaksi
FROM penjualan
GROUP BY kota
ORDER BY kota;Output yang Dihasilkan:
+------------+-----------+----------+-----------+------------------+
| kota | unit_kopi | unit_teh | unit_gula | jumlah_transaksi |
+------------+-----------+----------+-----------+------------------+
| Bandung | 1 | 5 | 4 | 4 |
| Jakarta | 7 | 1 | 2 | 5 |
| Surabaya | 5 | 2 | 6 | 3 |
| Yogyakarta | 4 | 0 | 0 | 2 |
+------------+-----------+----------+-----------+------------------+
4 rows in set (0.00 sec)Setiap baris kota kini menampilkan komposisi penjualan per kategori. GROUP BY kota membentuk kelompok, lalu tiga ekspresi SUM(CASE ...) yang berjalan di dalam kelompok memilah unit ke kolom masing-masing — pembagian kerja yang rapi antara pengelompok dan penyaring.
Kapan Memilih Kolom Kondisional atau GROUP BY #
Kedua pendekatan menjawab pertanyaan yang sama dengan bentuk hasil berbeda, sehingga pilihan ditentukan oleh cara laporan dibaca. Tabel berikut merangkum pertimbangan utamanya:
| Pertimbangan | Agregasi kondisional (pivot) | GROUP BY |
|---|---|---|
| Bentuk hasil | Satu baris, kategori menjadi kolom | Satu baris per kelompok |
| Kategori baru | Perlu menambah kolom manual | Otomatis menjadi baris baru |
| Cocok untuk | Dashboard, perbandingan berdampingan | Daftar, pencarian top-N, detail turunan |
| Panjang query | Bertambah tiap kategori | Tetap pendek |
Aturan praktisnya: bila jumlah kategori stabil dan kecil (misalnya status lunas/belum, tiga jenis produk unggulan), agregasi kondisional menghasilkan laporan yang ringkas dibaca. Bila kategori bisa bertambah tanpa batas — nama produk baru, kota baru — GROUP BY menghindari kita menulis ulang query setiap kali daftar kategori berubah.
Referensi #
- Aggregate Function Descriptions — MySQL 8.4 Reference Manual
- Flow Control Functions — MySQL 8.4 Reference Manual
- SELECT Statement — MySQL 8.4 Reference Manual
Agregasi bersyarat melengkapi peta pola laporan kita: GROUP BY memanjangkan hasil ke bawah, pivot kondisional melebarkannya ke samping. Bab berikutnya menutup section agregasi dengan WITH ROLLUP — teknik menambahkan baris subtotal dan total keseluruhan langsung dari klausa GROUP BY.