Giải pháp: GROUP BY với hàm tổng hợp
GROUP BY gom nhóm các dòng có cùng giá trị, kết hợp hàm tổng hợp (SUM, COUNT, AVG, MAX, MIN) để tính toán mỗi nhóm.
Ví dụ minh họa
Tạo bảng sản phẩm
mysql> CREATE TABLE DemoTable (
Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
Subject VARCHAR(20),
Price INT
);
mysql> INSERT INTO DemoTable(Subject, Price) VALUES('MySQL', 456), ('MySQL', 456), ('MongoDB', 56), ('MongoDB', 60), ('Java', 100), ('Java', 130);
Dữ liệu gốc
+----+---------+-------+ | Id | Subject | Price | +----+---------+-------+ | 1 | MySQL | 456 | | 2 | MySQL | 456 | | 3 | MongoDB | 56 | | 4 | MongoDB | 60 | | 5 | Java | 100 | | 6 | Java | 130 | +----+---------+-------+
Truy vấn GROUP BY
mysql> SELECT SUM(Price) AS Total, Subject
-> FROM DemoTable
-> GROUP BY Subject;
Kết quả
+-------+---------+ | Total | Subject | +-------+---------+ | 912 | MySQL | | 116 | MongoDB | | 230 | Java | +-------+---------+
Các hàm tổng hợp phổ biến
| Hàm | Ý nghĩa | Ví dụ |
|---|---|---|
COUNT(*) | Số dòng | COUNT(*) |
SUM(col) | Tổng | SUM(Price) |
AVG(col) | Trung bình | AVG(Price) |
MIN(col) | Nhỏ nhất | MIN(Price) |
MAX(col) | Lớn nhất | MAX(Price) |
GROUP_CONCAT(col) | Nối chuỗi | GROUP_CONCAT(Subject) |
STDDEV(col) | Độ lệch chuẩn | STDDEV(Price) |
GROUP BY nhiều cột
SELECT Subject, YEAR(created_at) AS year, SUM(Price) FROM DemoTable GROUP BY Subject, YEAR(created_at);
⚠️ Quy tắc ONLY_FULL_GROUP_BY (MySQL 5.7+)
Mặc định bật. Mọi cột trong SELECT phải:
- Có trong GROUP BY, HOẶC
- Trong hàm tổng hợp
-- Sai (nếu ONLY_FULL_GROUP_BY=ON) SELECT Subject, Price FROM DemoTable GROUP BY Subject; -- Error: 'Price' isn't in GROUP BY -- Đúng SELECT Subject, SUM(Price) FROM DemoTable GROUP BY Subject; SELECT Subject, ANY_VALUE(Price) FROM DemoTable GROUP BY Subject; -- Lấy bất kỳ giá trị
HAVING vs WHERE
-- WHERE: Lọc TRƯỚC khi gom nhóm WHERE Price > 100 -- HAVING: Lọc SAU khi gom nhóm HAVING SUM(Price) > 500
Ứng dụng thực tế
-- Doanh thu theo danh mục SELECT category, SUM(amount) AS revenue, COUNT(*) AS orders FROM orders GROUP BY category ORDER BY revenue DESC; -- User hoạt động SELECT user_id, COUNT(*) AS actions, MAX(created_at) AS last_active FROM logs GROUP BY user_id HAVING COUNT(*) > 10;