Skip to main content
Tóm tắt

Bài 9 dạy bạn nhóm dữ liệu với GROUP BY và lọc kết quả nhóm bằng HAVING, rất quan trọng khi làm báo cáo tổng hợp (doanh thu theo khách hàng, số đơn hàng theo ngày, v.v.).

🔥 9: GROUP BY - HAVING

📌 Mục tiêu bài học: Hiểu cách nhóm dữ liệu với GROUP BYlọc kết quả nhóm bằng HAVING trong SQL, giúp tổ chức dữ liệu hiệu quả hơn trong báo cáo và phân tích.


1️⃣ Chuẩn bị dữ liệu

Nếu chưa có database, chạy lệnh này để tạo lại:

CREATE DATABASE ShopDB;
USE ShopDB;

CREATE TABLE Orders (
ID INT AUTO_INCREMENT PRIMARY KEY,
CustomerID INT NOT NULL,
TotalAmount FLOAT NOT NULL,
OrderDate DATE NOT NULL
);

INSERT INTO Orders (CustomerID, TotalAmount, OrderDate) VALUES
(1, 1500, '2024-02-20'),
(2, 1200, '2024-02-21'),
(3, 500, '2024-02-22'),
(1, 800, '2024-02-22'),
(2, 2000, '2024-02-23'),
(3, 700, '2024-02-23');

2️⃣ GROUP BY

🔹 GROUP BY là gì?

GROUP BY trong SQL dùng để nhóm các hàng có cùng giá trị trong một hoặc nhiều cột, giúp thực hiện các phép tính tổng hợp trên từng nhóm dữ liệu.

💡 Ứng dụng:

  • Nhóm dữ liệu theo danh mục (ví dụ: doanh thu theo ngày, tổng đơn hàng theo khách hàng).
  • Kết hợp với Aggregate Functions như COUNT(), SUM(), AVG(), MIN(), MAX().

📌 Cú pháp:

SELECT column1, AggregateFunction(column2)
FROM table_name
GROUP BY column1;

🔹 Trong đó:

  • column1 là cột dùng để nhóm.
  • AggregateFunction(column2) là hàm tổng hợp áp dụng cho nhóm đó.

🔹 GROUP BY với nhiều cột

Bạn có thể nhóm theo nhiều cột bằng cách liệt kê các cột trong GROUP BY:

SELECT CustomerID, OrderDate, SUM(TotalAmount) AS DailySpent
FROM Orders
GROUP BY CustomerID, OrderDate;

✅ Kết quả:

CustomerIDOrderDateDailySpent
12024-02-201500
12024-02-22800
22024-02-211200
22024-02-232000

🔹 Lưu ý: Khi dùng GROUP BY với nhiều cột, dữ liệu sẽ nhóm theo từng tổ hợp duy nhất của các cột đó.

🔹 Ví dụ minh họa

📌 Đếm số đơn hàng của mỗi khách hàng

SELECT CustomerID, COUNT(*) AS OrderCount
FROM Orders
GROUP BY CustomerID;

✅ Kết quả:

CustomerIDOrderCount
12
22
32

📌 Tính tổng doanh thu theo từng ngày

SELECT OrderDate, SUM(TotalAmount) AS DailyRevenue
FROM Orders
GROUP BY OrderDate;

✅ Kết quả:

OrderDateDailyRevenue
2024-02-201500
2024-02-211200
2024-02-221300
2024-02-232700

3️⃣ HAVING

🔹 HAVING là gì?

HAVING dùng để lọc dữ liệu sau khi nhóm (GROUP BY), tương tự như WHERE, nhưng WHERE không thể dùng với Aggregate Functions.

💡 Ứng dụng:

  • Lọc nhóm dữ liệu theo điều kiện cụ thể (ví dụ: chỉ lấy nhóm có tổng chi tiêu lớn hơn 2000).
  • Giúp lọc dữ liệu sau khi nhóm (thông thường dùng với COUNT(), SUM(), AVG()), khi WHERE không thể dùng.

📌 Cú pháp:

SELECT column1, AggregateFunction(column2)
FROM table_name
GROUP BY column1
HAVING AggregateFunction(column2) condition;

🔹 So sánh WHEREHAVING

🔍 Tính năngWHERE không dùng đượcHAVING dùng được
Lọc theo giá trị cụ thể✅ Hỗ trợ✅ Hỗ trợ
Lọc dựa trên Aggregate Functions❌ Không hỗ trợ✅ Hỗ trợ
Sử dụng với GROUP BY❌ Không được✅ Hỗ trợ

🔹 Ví dụ minh họa

📌 Chỉ lấy khách hàng có tổng chi tiêu > 2000

SELECT CustomerID, SUM(TotalAmount) AS TotalSpent
FROM Orders
GROUP BY CustomerID
HAVING SUM(TotalAmount) > 2000;

✅ Kết quả:

CustomerIDTotalSpent
23200

📌 Chỉ lấy các ngày có tổng doanh thu >= 2000

SELECT OrderDate, SUM(TotalAmount) AS DailyRevenue
FROM Orders
GROUP BY OrderDate
HAVING SUM(TotalAmount) >= 2000;

✅ Kết quả:

OrderDateDailyRevenue
2024-02-232700

4️⃣ Lưu ý khi sử dụng kết hợp GROUP BY, HAVING và ORDER BY

🔹 Các lỗi thường gặp

⚠️ 1. Các cột trong SELECT phải có trong GROUP BY hoặc là Aggregate Function
Ví dụ sau sẽ báo lỗi:

SELECT CustomerID, OrderDate, SUM(TotalAmount) 
FROM Orders
GROUP BY CustomerID;

Lý do: OrderDate không có trong GROUP BY và cũng không được dùng với Aggregate Function nào.
Sửa đúng:

SELECT CustomerID, SUM(TotalAmount) 
FROM Orders
GROUP BY CustomerID;

⚠️ 2. Sắp xếp dữ liệu sau khi nhóm với ORDER BY
Mặc định, GROUP BY không sắp xếp dữ liệu, do đó cần dùng ORDER BY:

SELECT CustomerID, COUNT(*) AS OrderCount
FROM Orders
GROUP BY CustomerID
ORDER BY OrderCount DESC;

⚠️ 3. Không thể dùng WHERE để lọc Aggregate Function
⛔ Sai:

SELECT CustomerID, SUM(TotalAmount) 
FROM Orders
WHERE SUM(TotalAmount) > 2000
GROUP BY CustomerID;

✅ Đúng (dùng HAVING thay vì WHERE):

SELECT CustomerID, SUM(TotalAmount) 
FROM Orders
GROUP BY CustomerID
HAVING SUM(TotalAmount) > 2000;

⚠️ 4. Tránh nhóm dữ liệu quá lớn
Nếu bảng chứa hàng triệu bản ghi, việc GROUP BY có thể gây hiệu suất chậm.
Giải pháp: Lọc trước với WHERE trước khi GROUP BY.

SELECT CustomerID, SUM(TotalAmount) 
FROM Orders
WHERE OrderDate >= '2024-02-01' -- Giảm số lượng bản ghi trước
GROUP BY CustomerID;

🔹 Ví dụ minh họa

📌 Lấy danh sách khách hàng có từ 2 đơn hàng trở lên, sắp xếp theo tổng chi tiêu giảm dần

SELECT 
CustomerID,
COUNT(*) AS OrderCount,
SUM(TotalAmount) AS TotalSpent
FROM Orders
GROUP BY CustomerID
HAVING COUNT(*) >= 2
ORDER BY TotalSpent DESC;

✅ Kết quả:

CustomerIDOrderCountTotalSpent
223200
122300
321200

5️⃣ Bài tập thử thách 🚀

Hãy thử viết các câu lệnh SQL để giải các bài tập sau:

👉 Bài 1: Tính số lượng đơn hàng theo từng ngày.
👉 Bài 2: Tìm khách hàng có tổng chi tiêu lớn hơn 1000.
👉 Bài 3: Đếm số lượng khách hàng có ít nhất 2 đơn hàng.
👉 Bài 4: Tính doanh thu trung bình mỗi ngày, chỉ lấy ngày có doanh thu lớn hơn 1500.
👉 Bài 5: Lấy danh sách ngày có trên 1 đơn hàng, sắp xếp theo số lượng đơn hàng giảm dần.

6️⃣ Tài liệu tham khảo 📚

1️⃣ Official SQL Documentation:
🔗 MySQL GROUP BY & HAVING
🔗 PostgreSQL GROUP BY
🔗 SQL Server GROUP BY & HAVING

2️⃣ Sách & Khóa học:
📘 SQL for Data Analysis - Cathy Tanimura
📘 Learning SQL - Alan Beaulieu
🎓 Mode Analytics SQL Tutorial


Tóm tắt bài học

✔️ GROUP BY - Nhóm dữ liệu theo một hoặc nhiều cột.
✔️ HAVING - Lọc dữ liệu sau khi nhóm (WHERE không dùng được với Aggregate Functions).
✔️ Kết hợp với ORDER BY để sắp xếp kết quả.

🚀 Tiếp theo: Học về JOIN để kết nối nhiều bảng dữ liệu!

📌 Lộ trình: Học SQL trong 30 ngày