DHDataHọc
PracticeLesson 22/306 min read+20 XP

Bài tập: đếm theo trạng thái

Bạn sẽ học được

  • Ghép GROUP BY, hàm tổng hợp và ORDER BY thành một câu báo cáo hoàn chỉnh.
  • Đọc đề để xác định đúng cột, đúng tên cột và đúng thứ tự.
  • Hiểu cách bài chấm so sánh kết quả gom nhóm.
  • Chẩn đoán ba lỗi phổ biến nhất khi làm bài đếm theo nhóm.
  • Tự kiểm tra kết quả bằng phép đối chiếu tổng.

Khái niệm cốt lõi

Đề bài Đếm đơn theo trạng thái:

Đếm số đơn hàng theo từng status, đặt tên cột đếm là total; sắp xếp theo status.

Checklist bốn thành phần:

Câu hỏiTrả lờiMệnh đề
Bảng nào?ordersFROM orders
Cột nào?status, và COUNT(*) AS totalSELECT status, COUNT(*) AS total
Nhóm theo gì?statusGROUP BY status
Thứ tự?status tăng dầnORDER BY status

Không có điều kiện lọc, nên không có WHERE và không có HAVING.

Đáp án:

code
SELECT status, COUNT(*) AS total
FROM orders
GROUP BY status
ORDER BY status;

Kết quả:

statustotal
cancelled1
completed13
pending2

Ba dòng, tổng 16 — đúng bằng tổng số đơn. Phép đối chiếu này là bước tự kiểm tra quan trọng nhất: tổng các nhóm phải bằng tổng bảng khi không có WHERE lọc mất dòng. Nếu bạn ra tổng khác 16, lỗi nằm ở phép đếm chứ không phải ở cách trình bày.

Đề nói "đặt tên cột đếm là total" — đây không phải chi tiết trang trí. Bài chấm so sánh theo tên cột, nên COUNT(*) AS so_don hay COUNT(*) trần đều bị coi là sai dù con số đúng. Hãy đọc đề theo kiểu gạch chân từng yêu cầu: cột nào, tên gì, sắp xếp theo gì.

Thứ tự cột trong SELECT cũng do đề quyết định: status trước, total sau. Một số bài chấm so khớp theo vị trí cột; đảo SELECT total, status có thể bị báo sai dù dữ liệu đúng.

Vì sao ORDER BY status cho thứ tự cancelled, completed, pending? Đó là thứ tự alphabet của chuỗi theo collation mặc định, không phải thứ tự nghiệp vụ nào. Nếu đề muốn "hoàn tất trước, chờ sau, huỷ cuối", sẽ phải dùng CASE hoặc một cột ưu tiên — chương này chưa học, và đề hiện tại không yêu cầu.

GROUP BY status gom ba nhóm vì bảng có ba giá trị status khác nhau. Nếu ngày mai có thêm trạng thái refunded, truy vấn tự động trả bốn nhóm — đây là ưu điểm của việc gom theo dữ liệu thay vì liệt kê cứng từng trạng thái bằng ba câu SELECT riêng.

Ví dụ chạy được

Trước khi viết đáp án, kiểm tra phân bố thô:

code
SELECT status FROM orders ORDER BY status;

Mười sáu dòng, nhóm lại thành 1 cancelled, 13 completed, 2 pending. Việc nhìn dữ liệu thô giúp bạn chắc rằng không có giá trị lạ (ví dụ Completed viết hoa, hay khoảng trắng thừa) tạo nhóm thứ tư không mong muốn.

Sau đó chạy bản không sắp xếp:

code
SELECT status, COUNT(*) AS total
FROM orders
GROUP BY status;

Ba dòng với con số đúng nhưng thứ tự không đảm bảo. PostgreSQL thường trả theo thứ tự nhóm nội bộ (hash aggregate có thể cho thứ tự bất kỳ), nên đừng dựa vào đó. Thêm ORDER BY status để kết quả xác định — đây chính là yêu cầu cuối của đề.

Ba lần thử sai thường gặp:

Lần 1 — quên GROUP BY:

code
SELECT status, COUNT(*) AS total FROM orders;

Lỗi column "orders.status" must appear in the GROUP BY clause. Đây là lỗi cú pháp, dễ sửa: thêm GROUP BY status.

Lần 2 — đếm sai đơn vị:

code
SELECT o.status, COUNT(*) AS total
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
GROUP BY o.status
ORDER BY o.status;

Câu này không báo lỗi nhưng cho completed 26, pending 2, cancelled 1 — vì COUNT(*) đếm dòng hàng, không đếm đơn. Đề chỉ hỏi bảng orders; việc tự ý nối thêm bảng là sai phạm vi. Nếu buộc phải nối, dùng COUNT(DISTINCT o.id).

Lần 3 — sai tên cột:

code
SELECT status, COUNT(*) AS so_don
FROM orders
GROUP BY status
ORDER BY status;

Con số đúng, tên cột sai. Bài chấm báo sai cột. Cách sửa: đổi bí danh thành total.

Một lần thử khác đáng lưu ý — liệt kê cứng bằng ba câu UNION:

code
SELECT 'completed' AS status, COUNT(*) AS total FROM orders WHERE status = 'completed'
UNION ALL
SELECT 'pending', COUNT(*) FROM orders WHERE status = 'pending'
UNION ALL
SELECT 'cancelled', COUNT(*) FROM orders WHERE status = 'cancelled'
ORDER BY status;

Kết quả đúng trên dữ liệu hiện tại, nhưng sai về bản chất: khi bộ dữ liệu ẩn có thêm trạng thái mới, câu này bỏ sót. GROUP BY tự thích nghi với mọi giá trị có trong dữ liệu — đó là lý do nó là công cụ đúng cho câu hỏi "theo từng".

Bộ dữ liệu ẩn của bài này có thêm đơn hàng so với bộ hiển thị, nên các con số sẽ khác. Đừng hardcode 13, 2, 1; hãy viết quy tắc để SQL tự đếm. Bài chấm chạy đúng câu lệnh của bạn trên cả hai bộ dữ liệu và so sánh kết quả — một câu lệnh đúng quy tắc sẽ đúng ở cả hai nơi.

Thực hành

  1. Nộp đáp án cho bài Đếm đơn theo trạng thái (count-orders-by-status).
  2. Viết biến thể đếm số đơn theo từng khách, đặt tên cột total, sắp theo total DESC rồi customer_id. Khách có nhiều đơn nhất là Bình Trần với 3.
  3. Viết biến thể đếm theo status nhưng chỉ tính đơn có order_date trong năm 2025. Vì mọi đơn đều thuộc 2025, kết quả phải giống đáp án — dùng phép so sánh này để kiểm tra điều kiện ngày viết đúng.
  4. Thêm cột SUM(quantity * unit_price) vào đáp án bằng cách nối order_items. Nhớ dùng COUNT(DISTINCT o.id) cho cột total, nếu không số đơn sẽ bị thổi phồng.

Lỗi thường gặp

Triệu chứngNguyên nhânCách sửa
must appear in the GROUP BY clauseThiếu GROUP BYThêm GROUP BY status
Số đơn lớn hơn thực tếJOIN rồi COUNT(*)Chỉ dùng bảng orders, hoặc COUNT(DISTINCT o.id)
Chấm báo sai cộtBí danh không phải totalCOUNT(*) AS total
Chấm báo sai thứ tựThiếu ORDER BY statusThêm mệnh đề sắp xếp
Bộ hiển thị đúng, bộ ẩn saiHardcode giá trị hoặc liệt kê cứng trạng tháiViết quy tắc bằng GROUP BY

Lỗi cuối cùng là lỗi khó thấy nhất vì màn hình không báo gì. Cách tự kiểm tra: hỏi "nếu dữ liệu có thêm một trạng thái mới, câu lệnh của mình có tự trả thêm dòng không?". Nếu câu trả lời là không, bạn đã viết sai quy tắc.

Tóm tắt

  • Báo cáo theo nhóm = SELECT nhom, COUNT(*) AS total + GROUP BY nhom + ORDER BY.
  • Đọc đề để lấy đúng tên cột và thứ tự cột — bài chấm kiểm tra cả hai.
  • Tổng các nhóm phải bằng tổng bảng khi không có WHERE.
  • Không nối thêm bảng khi đề chỉ hỏi một bảng; nếu nối, dùng COUNT(DISTINCT id).
  • GROUP BY tự thích nghi với dữ liệu mới; liệt kê cứng thì không.

Bạn đã hoàn thành chương hàm tổng hợp và GROUP BY. Chương tiếp theo chuyển sang JOIN — nơi dữ liệu từ nhiều bảng được kết hợp để trả lời những câu hỏi không bảng đơn nào trả lời được.

Use ← → to move between lessons