GROUP BY mengumpulkan baris yang memiliki nilai sama ke dalam grup, sehingga fungsi agregat seperti COUNT, SUM, dan AVG mengembalikan satu hasil per grup, bukan satu hasil untuk seluruh tabel. Contoh-contohnya berjalan di PostgreSQL, MySQL, SQLite, dan SQL Server.
Cara menggunakan GROUP BY
Tulis kolom yang ingin dikelompokkan setelah GROUP BY, lalu bungkus setiap kolom lain yang Anda pilih dengan fungsi agregat: COUNT, SUM, AVG, MIN, atau MAX. Anda akan mendapatkan satu baris untuk setiap nilai yang berbeda:
SELECT group_column, AGGREGATE(value_column)
FROM table_name
GROUP BY group_column;
-- Input: scores
-- player | team | points
-- -------+------+-------
-- Ana | Red | 12
-- Ben | Blue | 9
-- Cleo | Red | 7
-- Dev | Blue | 15
-- Eli | Red | 4
SELECT team, SUM(points) AS total_points, COUNT(*) AS players
FROM scores
GROUP BY team;
-- Output:
-- team | total_points | players
-- -----+--------------+--------
-- Red | 23 | 3
-- Blue | 24 | 2
Lima baris menjadi dua, satu per tim, dan setiap agregat hanya melihat baris milik tim tersebut.
Aturan yang perlu diketahui
- Setiap kolom yang dipilih harus ada di GROUP BY atau di dalam agregat. Jika tidak, tidak ada satu nilai pun yang bisa ditampilkan untuk grup tersebut, dan query akan gagal:
-- Input: products
-- name | category
-- --------+---------
-- Blender | Kitchen
-- Toaster | Kitchen
-- Pillow | Bedroom
SELECT name, category, COUNT(*) AS product_count
FROM products
GROUP BY category;
-- ERROR: column "products.name" must appear in the GROUP BY clause
-- or be used in an aggregate function
-- (SQLite runs it anyway and takes name from one row in each group)
Perbaiki dengan menambahkan kolom itu ke GROUP BY, atau dengan mengagregasikannya juga, misalnya dengan MIN(name).
- NULL membentuk satu grup. Setiap baris dengan NULL di kolom pengelompokan masuk ke grup yang sama. Gunakan COALESCE untuk memberinya label:
-- Input: products
-- name | category
-- ------------+-----------
-- Notebook | Stationery
-- Gift Card | NULL
-- Pen Set | Stationery
-- Mystery Box | NULL
-- Stapler | Stationery
SELECT category, COUNT(*) AS product_count
FROM products
GROUP BY category;
-- Output:
-- category | product_count
-- -----------+--------------
-- NULL | 2
-- Stationery | 3
SELECT COALESCE(category, 'Uncategorized') AS category, COUNT(*) AS product_count
FROM products
GROUP BY category;
-- Output:
-- category | product_count
-- --------------+--------------
-- Uncategorized | 2
-- Stationery | 3
-
WHERE memfilter baris sebelum pengelompokan, HAVING memfilter grup sesudahnya. Hanya HAVING yang bisa menguji agregat seperti
COUNT(*) > 2. Apa pun yang tidak membutuhkan agregat sebaiknya ditaruh di WHERE, agar lebih sedikit baris yang dikelompokkan. - Grup dikembalikan tanpa urutan tertentu. SQLite dan SQL Server, misalnya, menampilkan Blue sebelum Red untuk query skor di atas. Tambahkan ORDER BY setiap kali urutan itu penting.
Sintaks alternatif
Anda bisa mengelompokkan berdasarkan alias kolom, atau berdasarkan posisi kolom di daftar SELECT, tanpa perlu mengulang ekspresi:
-- Input: orders
-- order_id | amount
-- ---------+-------
-- 1 | 35.00
-- 2 | 240.00
-- 3 | 18.50
-- 4 | 120.00
-- 5 | 64.00
SELECT CASE WHEN amount >= 100 THEN 'large' ELSE 'small' END AS order_size,
COUNT(*) AS order_count
FROM orders
GROUP BY order_size -- or GROUP BY 1
ORDER BY order_size;
-- SQL Server allows neither: repeat the CASE expression in GROUP BY
-- Output:
-- order_size | order_count
-- -----------+------------
-- large | 2
-- small | 3
ROLLUP menambahkan subtotal dan total keseluruhan. Baris subtotal menampilkan NULL di kolom yang dijumlahkannya:
-- Input: sales
-- region | category | amount
-- -------+----------+-------
-- East | Books | 100.00
-- East | Toys | 50.00
-- West | Books | 80.00
-- East | Books | 40.00
SELECT region, category, SUM(amount) AS total_sales
FROM sales
GROUP BY ROLLUP(region, category);
-- MySQL: GROUP BY region, category WITH ROLLUP
-- SQLite has no ROLLUP
-- Output:
-- region | category | total_sales
-- -------+----------+------------
-- East | Books | 140.00
-- East | Toys | 50.00
-- East | NULL | 190.00
-- West | Books | 80.00
-- West | NULL | 80.00
-- NULL | NULL | 270.00
GROUP BY tanpa agregat mengembalikan setiap nilai berbeda sekali saja, sama seperti SELECT DISTINCT category FROM products.
Kapan sebaiknya menggunakan GROUP BY?
Gunakan GROUP BY setiap kali pertanyaannya mengandung “per” atau “untuk setiap”: pesanan per pelanggan, penjualan per bulan, karyawan per departemen. Sebagian besar penggunaannya masuk ke salah satu dari empat kategori ini.
Menghitung baris di setiap grup
Berapa banyak pesanan untuk setiap status, berapa produk di setiap kategori, berapa orang yang mendaftar setiap hari. COUNT(*) menghitung setiap baris di grup, sedangkan COUNT(column) hanya menghitung baris yang kolomnya tidak NULL.
Total, rata-rata, atau rentang untuk setiap grup
SUM, AVG, MIN, dan MAX mengubah nilai-nilai di setiap grup menjadi satu angka: pendapatan per pelanggan, rata-rata nilai pesanan, produk termurah dan termahal di suatu kategori. Satu query bisa mengembalikan sebanyak yang Anda butuhkan.
Merinci hasil berdasarkan lebih dari satu kolom
Cantumkan beberapa kolom setelah GROUP BY untuk mendapatkan satu baris per kombinasi: penjualan per wilayah per bulan, stok per gudang per jenis produk. Hanya kombinasi yang ada di data yang akan muncul.
Hanya menyimpan grup yang penting
HAVING memfilter grup setelah terbentuk, menggunakan agregatnya: departemen dengan lebih dari sepuluh orang, pelanggan yang berbelanja lebih dari $1,000, atau nilai yang muncul lebih dari sekali, yang merupakan cara untuk menemukan duplikat.
Contoh nyata GROUP BY
Menghitung produk di setiap kategori, termasuk yang kosong
GROUP BY hanya membuat grup untuk nilai yang ada. Untuk menampilkan juga kategori tanpa produk, mulailah dari tabel categories lalu LEFT JOIN tabel products. Hitung kolom dari tabel yang di-join, bukan *: kategori kosong tetap mendapat satu baris yang diisi NULL, dan COUNT(*) akan ikut menghitungnya.
-- Input: categories
-- category_name
-- -------------
-- Audio
-- Cameras
-- Drones
-- Input: products
-- id | name | category
-- ---+------------+---------
-- 1 | Earbuds | Audio
-- 2 | Soundbar | Audio
-- 3 | Action Cam | Cameras
-- Wrong: COUNT(*) counts the NULL row of an empty category
SELECT c.category_name, COUNT(*) AS product_count
FROM categories c
LEFT JOIN products p ON p.category = c.category_name
GROUP BY c.category_name
ORDER BY c.category_name;
-- Output:
-- category_name | product_count
-- --------------+--------------
-- Audio | 2
-- Cameras | 1
-- Drones | 1
-- Right: COUNT(p.id) skips it
SELECT c.category_name, COUNT(p.id) AS product_count
FROM categories c
LEFT JOIN products p ON p.category = c.category_name
GROUP BY c.category_name
ORDER BY c.category_name;
-- Output:
-- category_name | product_count
-- --------------+--------------
-- Audio | 2
-- Cameras | 1
-- Drones | 0
Menemukan tiga kategori teratas berdasarkan pendapatan
Jumlahkan penjualan setiap kategori, urutkan berdasarkan total, lalu ambil tiga teratas. Books berada di posisi kedua meskipun setiap penjualannya lebih kecil daripada satu-satunya penjualan Toys:
-- Input: purchases
-- category | amount
-- ---------+-------
-- Books | 45.00
-- Toys | 120.00
-- Garden | 310.00
-- Books | 80.00
-- Music | 25.00
SELECT category, SUM(amount) AS revenue
FROM purchases
GROUP BY category
ORDER BY revenue DESC
LIMIT 3;
-- SQL Server has no LIMIT: write SELECT TOP 3 category, ... instead
-- Output:
-- category | revenue
-- ---------+--------
-- Garden | 310.00
-- Books | 125.00
-- Toys | 120.00
Laporan penjualan per wilayah per bulan
Kelompokkan berdasarkan wilayah dan bulan yang diambil dari tanggal, sehingga setiap wilayah mendapat satu baris per bulan:
-- Input: sales
-- region | sale_date | amount
-- -------+------------+-------
-- East | 2024-01-08 | 120.00
-- West | 2024-01-15 | 80.00
-- East | 2024-01-22 | 95.00
-- East | 2024-02-03 | 60.00
-- West | 2024-02-11 | 150.00
SELECT region, EXTRACT(MONTH FROM sale_date) AS month, SUM(amount) AS total_sales
FROM sales
GROUP BY region, EXTRACT(MONTH FROM sale_date)
ORDER BY region, month;
-- Output:
-- region | month | total_sales
-- -------+-------+------------
-- East | 1 | 215.00
-- East | 2 | 60.00
-- West | 1 | 80.00
-- West | 2 | 150.00
SQL Server dan SQLite tidak memiliki EXTRACT. Gunakan MONTH(sale_date) di SQL Server dan strftime('%m', sale_date) di SQLite. Kelompokkan juga berdasarkan tahun jika data Anda mencakup lebih dari satu tahun.
Menemukan pelanggan yang kembali tahun ini
WHERE hanya menyimpan pesanan tahun 2024, lalu HAVING menyimpan pelanggan yang memiliki setidaknya dua pesanan tersebut. Pelanggan 202 memiliki total dua pesanan, tetapi hanya satu di 2024, sehingga tidak disertakan:
-- Input: orders
-- order_id | customer_id | order_date
-- ---------+-------------+-----------
-- 1 | 201 | 2023-11-20
-- 2 | 202 | 2023-12-05
-- 3 | 201 | 2024-01-09
-- 4 | 202 | 2024-02-14
-- 5 | 201 | 2024-03-02
SELECT customer_id, COUNT(*) AS orders_2024
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY customer_id
HAVING COUNT(*) >= 2;
-- Output:
-- customer_id | orders_2024
-- ------------+------------
-- 201 | 2
Bacaan lebih lanjut tentang GROUP BY
Di blog ini:
- Panduan SQL COUNT DISTINCT: menghitung nilai unik di setiap grup
-
Panduan menemukan duplikat di SQL: GROUP BY dengan
HAVING COUNT(*) > 1 - Panduan SQL ORDER BY: mengurutkan hasil yang dikelompokkan
- Panduan pernyataan SQL CASE: hitungan dan penjumlahan bersyarat di dalam agregat
- Panduan tabel pivot SQL: mengubah baris yang dikelompokkan menjadi kolom
- Panduan SQL COALESCE: memberi label pada grup NULL
- Panduan diagram SQL JOIN: LEFT JOIN yang mempertahankan grup kosong
Dokumentasi resmi:
- PostgreSQL: GROUP BY dan HAVING
- MySQL: Penanganan GROUP BY
- SQLite: SELECT (query agregat)
- SQL Server: GROUP BY
Beekeeper Studio Adalah GUI Database Gratis & Open Source
Tool query & editor SQL terbaik yang pernah saya pakai. Semua yang saya butuhkan untuk mengelola database ada di sini. - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studio cepat, intuitif, dan mudah digunakan. Beekeeper mendukung banyak sekali database, dan berjalan mulus di Windows, Mac, dan Linux.
Apa Kata Pengguna Tentang Beekeeper Studio
"Beekeeper Studio benar-benar menggantikan alur kerja SQL lama saya. Cepat, intuitif, dan membuat pekerjaan database kembali menyenangkan."
"Saya sudah mencoba banyak GUI database, tetapi Beekeeper punya keseimbangan sempurna antara fitur dan kesederhanaan. Langsung berfungsi."