Chuyển tới nội dung chính

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àmChức năngVí dụ
COUNT()Đếm số dòngĐếm số nhân viên
SUM()Tính tổngTổng doanh thu
AVG()Tính trung bìnhLương trung bình
MIN()Giá trị nhỏ nhấtGiá 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ả:

PhongBanSoNhanVien
Kinh doanh4
Kỹ thuật4
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

WHEREHAVING
Thời điểm lọcTrước GROUP BYSau GROUP BY
Dùng vớiCột gốc trong bảngHàm tổng hợp (COUNT, SUM, ...)
Ví dụWHERE Luong > 10000000HAVING 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

  1. Đếm tổng số đơn hàng.
  2. Tính tổng doanh thu (SoLuong × DonGia).
  3. Tìm đơn hàng có giá trị cao nhất.
  4. Thống kê số đơn hàngtổng doanh thu theo từng nhân viên bán hàng.
  5. Tìm nhân viên bán được nhiều đơn hàng nhất.
  6. Thống kê doanh thu theo sản phẩm, sắp xếp giảm dần.
  7. Tìm sản phẩm có tổng số lượng bán > 5.
  8. Thống kê theo tháng: tổng đơn hàng, tổng doanh thu.
  9. Tìm nhân viên có doanh thu trung bình mỗi đơn > 50 triệu.
  10. 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