Bài 6: Hàm Tổng Hợp & GROUP BY
Giai đoạn 2 – Truy Vấn Dữ Liệu · Mục tiêu: Sử dụng các hàm tổng hợp để thống kê dữ liệu và nhóm kết quả theo tiêu chí.
1. Các hàm tổng hợp (Aggregate Functions)
Tổng quan
| Hàm | Chức năng | Ví dụ |
|---|---|---|
COUNT() | Đếm số dòng | Đếm số nhân viên |
SUM() | Tính tổng | Tổng doanh thu |
AVG() | Tính trung bình | Lương trung bình |
MIN() | Giá trị nhỏ nhất | Giá rẻ nhất |
MAX() | Giá trị lớn nhất | Điểm cao nhất |
COUNT() – Đếm số dòng
-- Đếm tổng số nhân viên
SELECT COUNT(*) AS TongNhanVien FROM NhanVien;
-- Đếm nhân viên có email (không NULL)
SELECT COUNT(Email) AS CoEmail FROM NhanVien;
-- Đếm số phòng ban (không trùng)
SELECT COUNT(DISTINCT PhongBan) AS SoPhongBan FROM NhanVien;
:::info COUNT(*) vs COUNT(cột)
COUNT(*)→ Đếm tất cả dòng (kể cả dòng có NULL).COUNT(cot)→ Đếm dòng mà cột đó không NULL. :::
SUM() – Tính tổng
-- Tổng quỹ lương
SELECT SUM(Luong) AS TongLuong FROM NhanVien;
-- Tổng giá trị tồn kho
SELECT SUM(Gia * SoLuongTon) AS TongGiaTriTonKho FROM SanPham;
AVG() – Tính trung bình
-- Lương trung bình
SELECT AVG(Luong) AS LuongTB FROM NhanVien;
-- Lương trung bình làm tròn 2 chữ số
SELECT ROUND(AVG(Luong), 2) AS LuongTB FROM NhanVien;
MIN() / MAX() – Giá trị nhỏ nhất / lớn nhất
-- Lương thấp nhất và cao nhất
SELECT
MIN(Luong) AS LuongThapNhat,
MAX(Luong) AS LuongCaoNhat
FROM NhanVien;
-- Ngày vào làm sớm nhất
SELECT MIN(NgayVaoLam) AS NguoiVaoSomNhat FROM NhanVien;
Kết hợp nhiều hàm
SELECT
COUNT(*) AS TongNV,
SUM(Luong) AS TongLuong,
ROUND(AVG(Luong), 0) AS LuongTB,
MIN(Luong) AS LuongMin,
MAX(Luong) AS LuongMax
FROM NhanVien;
2. Nhóm dữ liệu – GROUP BY
Cú pháp
SELECT cot_nhom, HAM_TONG_HOP(cot)
FROM ten_bang
GROUP BY cot_nhom;
Ví dụ
-- Đếm số nhân viên theo phòng ban
SELECT PhongBan, COUNT(*) AS SoNhanVien
FROM NhanVien
GROUP BY PhongBan;
Kết quả:
| PhongBan | SoNhanVien |
|---|---|
| Kinh doanh | 4 |
| Kỹ thuật | 4 |
| Nhân sự | 2 |
Thêm nhiều hàm tổng hợp
-- Thống kê theo phòng ban
SELECT
PhongBan,
COUNT(*) AS SoNV,
SUM(Luong) AS TongLuong,
ROUND(AVG(Luong), 0) AS LuongTB,
MIN(Luong) AS LuongMin,
MAX(Luong) AS LuongMax
FROM NhanVien
GROUP BY PhongBan;
GROUP BY nhiều cột
-- Thống kê theo phòng ban VÀ chức vụ
SELECT
PhongBan,
ChucVu,
COUNT(*) AS SoNV,
AVG(Luong) AS LuongTB
FROM NhanVien
GROUP BY PhongBan, ChucVu;
GROUP BY với ORDER BY
-- Phòng ban có tổng lương cao nhất → thấp nhất
SELECT
PhongBan,
SUM(Luong) AS TongLuong
FROM NhanVien
GROUP BY PhongBan
ORDER BY TongLuong DESC;
3. Lọc nhóm – HAVING
HAVING dùng để lọc kết quả sau khi đã nhóm (GROUP BY), trong khi WHERE lọc trước khi nhóm.
Cú pháp
SELECT cot_nhom, HAM_TONG_HOP(cot)
FROM ten_bang
WHERE dieu_kien_loc_dong -- Lọc dòng TRƯỚC khi nhóm
GROUP BY cot_nhom
HAVING dieu_kien_loc_nhom; -- Lọc nhóm SAU khi nhóm
Ví dụ
-- Phòng ban có hơn 3 nhân viên
SELECT PhongBan, COUNT(*) AS SoNV
FROM NhanVien
GROUP BY PhongBan
HAVING SoNV > 3;
-- Phòng ban có lương trung bình trên 15 triệu
SELECT PhongBan, ROUND(AVG(Luong), 0) AS LuongTB
FROM NhanVien
GROUP BY PhongBan
HAVING LuongTB > 15000000;
-- Danh mục sản phẩm có tổng giá trị tồn kho > 100 triệu
SELECT
DanhMuc,
SUM(Gia * SoLuongTon) AS GiaTriTon
FROM SanPham
GROUP BY DanhMuc
HAVING GiaTriTon > 100000000;
WHERE + HAVING cùng lúc
-- Với nhân viên chức vụ "Nhân viên" (WHERE),
-- tìm phòng ban nào có lương TB > 13 triệu (HAVING)
SELECT
PhongBan,
COUNT(*) AS SoNV,
ROUND(AVG(Luong), 0) AS LuongTB
FROM NhanVien
WHERE ChucVu = 'Nhân viên' -- Lọc dòng trước
GROUP BY PhongBan
HAVING LuongTB > 13000000 -- Lọc nhóm sau
ORDER BY LuongTB DESC;
4. So sánh WHERE vs HAVING
WHERE | HAVING | |
|---|---|---|
| Thời điểm lọc | Trước GROUP BY | Sau GROUP BY |
| Dùng với | Cột gốc trong bảng | Hàm tổng hợp (COUNT, SUM, ...) |
| Ví dụ | WHERE Luong > 10000000 | HAVING AVG(Luong) > 15000000 |
:::warning Không dùng hàm tổng hợp trong WHERE
-- SAI: Không thể dùng COUNT() trong WHERE
SELECT PhongBan FROM NhanVien WHERE COUNT(*) > 3 GROUP BY PhongBan;
-- ĐÚNG: Dùng HAVING
SELECT PhongBan FROM NhanVien GROUP BY PhongBan HAVING COUNT(*) > 3;
:::
5. Thứ tự thực hiện đầy đủ
SELECT cot, HAM() -- 6. Chọn cột & tính toán
FROM bang -- 1. Chọn bảng
WHERE dieu_kien -- 2. Lọc dòng
GROUP BY cot -- 3. Nhóm dữ liệu
HAVING dieu_kien_nhom -- 4. Lọc nhóm
ORDER BY cot -- 5. Sắp xếp
LIMIT n; -- 7. Giới hạn kết quả
Bài tập thực hành
Dữ liệu mẫu
Sử dụng bảng NhanVien đã tạo ở Bài 5. Thêm bảng DonHang:
CREATE TABLE DonHang (
MaDH INT AUTO_INCREMENT PRIMARY KEY,
MaNV INT,
KhachHang VARCHAR(100),
SanPham VARCHAR(200),
SoLuong INT,
DonGia DECIMAL(12,2),
NgayBan DATE,
FOREIGN KEY (MaNV) REFERENCES NhanVien(MaNV)
);
INSERT INTO DonHang (MaNV, KhachHang, SanPham, SoLuong, DonGia, NgayBan) VALUES
(1, 'Công ty ABC', 'iPhone 15', 5, 28990000, '2024-11-01'),
(1, 'Anh Tuấn', 'AirPods Pro', 2, 5990000, '2024-11-05'),
(6, 'Chị Hương', 'MacBook Air', 1, 27490000, '2024-11-10'),
(9, 'Công ty XYZ', 'iPad Air', 10, 16990000, '2024-11-12'),
(1, 'Anh Nam', 'Samsung S24', 3, 22990000, '2024-11-15'),
(6, 'Công ty DEF', 'iPhone 15', 8, 28990000, '2024-11-20'),
(9, 'Chị Mai', 'AirPods Pro', 5, 5990000, '2024-11-22'),
(3, 'Anh Khoa', 'MacBook Pro', 2, 45990000, '2024-11-25'),
(6, 'Công ty GHI', 'Samsung S24', 4, 22990000, '2024-12-01'),
(1, 'Chị Lan', 'iPad Air', 2, 16990000, '2024-12-05');
Bài tập
- Đếm tổng số đơn hàng.
- Tính tổng doanh thu (SoLuong × DonGia).
- Tìm đơn hàng có giá trị cao nhất.
- Thống kê số đơn hàng và tổng doanh thu theo từng nhân viên bán hàng.
- Tìm nhân viên bán được nhiều đơn hàng nhất.
- Thống kê doanh thu theo sản phẩm, sắp xếp giảm dần.
- Tìm sản phẩm có tổng số lượng bán > 5.
- Thống kê theo tháng: tổng đơn hàng, tổng doanh thu.
- Tìm nhân viên có doanh thu trung bình mỗi đơn > 50 triệu.
- Với nhân viên phòng Kinh doanh, thống kê doanh thu và tìm người có doanh thu > 100 triệu.
Quay lại: Roadmap · Bài tiếp: Bài 7 – JOIN: Kết Hợp Nhiều Bảng