GROUP BY 將結果集依指定欄位分組,配合聚合函數(COUNT、SUM、AVG、MAX、MIN 等)對每組進行彙總計算。
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE condition
GROUP BY column_name
HAVING aggregate_condition
ORDER BY column_name;
CREATE TABLE employee (
id INT, first_name VARCHAR(15), last_name VARCHAR(15),
start_date DATE, end_date DATE, salary FLOAT(8,2),
city VARCHAR(10), description VARCHAR(15)
);
INSERT INTO employee VALUES
(1,'Jason','Martin','1996-07-25','2006-07-25',1234.56,'Toronto','Programmer'),
(2,'Alison','Mathews','1976-03-21','1986-02-21',6661.78,'Vancouver','Tester'),
(3,'James','Smith','1978-12-12','1990-03-15',6544.78,'Vancouver','Tester'),
(4,'Celia','Rice','1982-10-24','1999-04-21',2344.78,'Vancouver','Manager'),
(5,'Robert','Black','1984-01-15','1998-08-08',2334.78,'Vancouver','Tester'),
(6,'Linda','Green','1987-07-30','1996-01-04',4322.78,'New York','Tester'),
(7,'David','Larry','1990-12-31','1998-02-12',7897.78,'New York','Manager'),
(8,'James','Cat','1996-09-17','2002-04-15',1232.78,NULL,'Tester'); -- city 為 NULL
SELECT city, COUNT(*) AS cnt
FROM employee
GROUP BY city;
結果:
+-----------+-----+
| city | cnt |
+-----------+-----+
| NULL | 1 |
| New York | 2 |
| Toronto | 1 |
| Vancouver | 4 |
+-----------+-----+
重點:NULL 會被視為一個獨立群組。
SELECT city, COUNT(*)
FROM employee
WHERE description IN ('Tester','Programmer')
GROUP BY city;
結果:Manager 被排除,Vancouver 變 4→3,New York 變 2→1。
SELECT city, COUNT(*) AS cnt
FROM employee
WHERE description IS NOT NULL
GROUP BY city
HAVING cnt > 2;
結果:僅 Vancouver (4) 符合。
SELECT city, description, COUNT(*), AVG(salary)
FROM employee
GROUP BY city, description;
SELECT * FROM employee GROUP BY city;
⚠️ 非標準 SQL,MySQL 預設 ONLY_FULL_GROUP_BY 會報錯。回傳的非分組欄位值是「任意一列」,不可依賴。生產環境請開啟 sql_mode=ONLY_FULL_GROUP_BY。
| 特性 | WHERE | HAVING |
|---|---|---|
| 執行時機 | 分組前 | 分組後 |
| 可用聚合函數 | ❌ | ✅ |
| 可用別名 | ❌ | ✅ (MySQL 延伸) |
| 索引利用 | ✅ 可用索引篩選 | ❌ 需掃描分組結果 |
原則:能用 WHERE 就別用 HAVING,越早過濾資料量越小。
GROUP BY city 會將 NULL 歸為一組NULL 群組:WHERE city IS NOT NULLNULL 顯示為自訂標籤:SELECT COALESCE(city, 'Unknown') AS city, ... GROUP BY cityGROUP BY 欄位建立索引(INDEX idx_city (city)),可避免 Using temporary; Using filesortid)導致群組數爆炸INDEX idx_city_desc (city, description) 可直接從索引取得資料,避免回表LIMIT 時,先在子查詢分組再 JOIN 回主表取明細-- 每個城市的員工數,不壓縮列
SELECT *, COUNT(*) OVER (PARTITION BY city) AS city_cnt
FROM employee;
窗格函數保留明細列,適合需同時顯示明細與彙總的報表。
GROUP BY 是 SQL 資料分析的核心工具。掌握 WHERE 前置過濾、HAVING 後置過濾、NULL 群組處理、索引優化,並理解 ONLY_FULL_GROUP_BY 語意,即可寫出正確且高效的分組查詢。