aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
SAVED POSTS
aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
aedev.net
No Result
View All Result
Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

COUNT với CASE: vì sao ELSE 0 vẫn đếm cả dòng không khớp?

Suong Nguyen by Suong Nguyen
08/11/2026
in Backend
0
585
SHARES
3.2k
VIEWS
Summarize with ChatGPTShare to Facebook

Bạn cần tổng số đơn đã thanh toán nhưng dashboard luôn trả về tổng số đơn. Một lỗi dễ viết là COUNT(CASE WHEN status = 'paid' THEN 1 ELSE 0 END). Nhìn như đang đếm các số 1, nhưng COUNT không có quy tắc như vậy: số 0 vẫn là một giá trị không NULL và vẫn được tính.

Bài cập nhật ngày 08/11/2026, chạy các truy vấn bằng SQLite 3.54.0 tại máy và đối chiếu thêm tài liệu PostgreSQL 17. Chưa chạy PostgreSQL server, ORM hoặc benchmark. Mục tiêu là làm đúng nghĩa của chỉ số trước khi bàn đến tối ưu truy vấn.

Chuẩn bị một bộ dữ liệu có cả trường hợp không khớp

Ví dụ chỉ dùng bảng đơn hàng độc lập, không có JOIN nên mỗi dòng đại diện đúng một đơn. Trong SQLite session mới, chạy:

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  status TEXT
);
INSERT INTO orders (id, status) VALUES
  (1, 'paid'), (2, 'pending'), (3, 'paid'),
  (4, 'failed'), (5, NULL);

SELECT
  COUNT(*) AS total,
  COUNT(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS wrong_paid,
  COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid,
  SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_sum
FROM orders;

Kết quả là total=5, wrong_paid=5, paid=2, paid_sum=2. Hãy nhìn năm giá trị trung gian của biểu thức sai: 1, 0, 1, 0, 0. COUNT nhận đủ năm giá trị không NULL nên trả 5. Truy vấn vẫn hợp lệ về cú pháp, vì vậy chỉ kiểm tra query có chạy thành công là chưa đủ.

COUNT cần NULL để bỏ một dòng

Theo SQLite aggregate functions, COUNT(X) tính các giá trị X không NULL. Khi CASE không có nhánh ELSE và điều kiện không đúng, kết quả là NULL. PostgreSQL CASE cũng mô tả quy tắc mặc định ấy.

Trong ví dụ, COUNT(CASE ... THEN 1 END) nhận 1, NULL, 1, NULL, NULL, nên chỉ có hai giá trị được đếm. Bạn cũng có thể viết ELSE NULL cho rõ ý. Dùng SUM lại là hợp đồng khác: cộng các số 1 và 0 để ra số lượng.

Tính nhiều chỉ số trên cùng tập đơn

SELECT
  COUNT(*) AS total,
  COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid,
  COUNT(CASE WHEN status = 'pending' THEN 1 END) AS pending,
  COUNT(CASE WHEN status IS NULL THEN 1 END) AS unknown,
  COUNT(*) FILTER (WHERE status = 'failed') AS failed
FROM orders;

Kết quả theo thứ tự là 5, 2, 1, 1, 1. FILTER diễn đạt trực tiếp điều kiện dành riêng cho một aggregate và được SQLite hỗ trợ. Nếu backend dùng engine hoặc phiên bản khác, kiểm tra hỗ trợ cú pháp tại đó trước khi chuyển query; không suy rộng từ lần chạy SQLite.

Các nhánh trong ví dụ loại trừ nhau theo một cột trạng thái. Với chỉ số chồng lấn như “đã thanh toán” và “giá trị cao”, tổng số đếm có thể lớn hơn total. Đó có thể là thiết kế đúng, không nhất thiết là lỗi SQL; cần ghi định nghĩa chỉ số ngay cạnh truy vấn.

Đừng bỏ quên tập đầu vào rỗng

SELECT
  COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_count,
  SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_sum,
  COALESCE(SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END), 0) AS paid_zero
FROM orders
WHERE id < 0;

Kết quả là 0, NULL, 0. PostgreSQL aggregate functions cũng phân biệt COUNT với SUM khi không có dòng. Việc đã viết ELSE 0 không tạo ra một dòng đầu vào mới cho SUM. Nếu API cam kết trả số nguyên không âm, quyết định rõ có chuyển NULL thành 0 ở query hay tại lớp chuyển đổi dữ liệu.

Checklist khi đưa vào dashboard

Kiểm tra riêng bốn tình huống: có hai đơn paid, không có đơn paid nhưng vẫn có đơn khác, tất cả status đều NULL và hoàn toàn không có đơn. Với mỗi tình huống, ghi số mong đợi trước khi chạy query. Nếu chỉ kiểm tra dữ liệu toàn paid, biểu thức sai ở đầu bài vẫn có thể cho kết quả giống biểu thức đúng.

Khi thêm JOIN sau này, hãy kiểm tra lại đơn vị đếm vì một đơn có thể sinh nhiều dòng. Ví dụ hiện tại chỉ chứng minh cách aggregate xử lý CASE, không chứng minh query mở rộng đã đếm đúng thực thể hay sử dụng index hiệu quả.

Đối với COUNT có điều kiện, câu hỏi hữu ích khi review là: biểu thức trả gì ở dòng không khớp? Trả NULL thì COUNT bỏ qua; trả 0 thì COUNT vẫn tính.

Nguồn tham khảo

  • SQLite: Aggregate functions
  • SQLite: CASE expression
  • PostgreSQL 17: Conditional expressions
  • PostgreSQL 17: Aggregate functions

Tags: Báo cáo dữ liệuSQLSQLite
SummarizeShare234
Suong Nguyen

Suong Nguyen

Related Stories

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL RANK và DENSE_RANK: đồng hạng có làm bỏ số thứ hạng tiếp theo?

by Suong Nguyen
07/11/2026
0

Dùng dữ liệu đồng điểm để chọn RANK, DENSE_RANK hoặc ROW_NUMBER, đồng thời tránh vô tình phá nhóm đồng hạng bằng cột ID.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQLite REPLACE: cột bị bỏ qua có thể mất giá trị cũ

by Suong Nguyen
06/11/2026
0

So sánh INSERT OR REPLACE với ON CONFLICT DO UPDATE để thấy một cột không truyền vào có được giữ lại hay không.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL AND và OR: giữ điều kiện tenant bằng dấu ngoặc đúng chỗ

by Suong Nguyen
05/11/2026
0

Tái hiện lỗi lọc trạng thái làm lọt bản ghi tenant khác và kiểm tra nhóm điều kiện bằng dữ liệu đối chứng.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL LIKE: tìm dấu % và _ như ký tự thật bằng ESCAPE

by Suong Nguyen
04/11/2026
0

Kết hợp bind parameter và ESCAPE để ô tìm kiếm chứa % hoặc _ không vô tình trở thành một mẫu wildcard khác ý định người dùng.

0 0 votes
Article Rating
Subscribe
Login
Notify of
guest
guest
0 Comments
Oldest
Newest Most Voted
AE Dev

AE Dev mang đến các bài viết về lập trình, mobile app, backend, frontend, WordPress và công nghệ, phù hợp cho developer muốn học hỏi và cập nhật kiến thức thực tế.

Recent Posts

  • React Native Platform.select: native và default được chọn theo thứ tự nào?
  • Phỏng vấn JavaScript: vì sao regex.test cùng chuỗi lại đổi kết quả?
  • Android TextView.setText(Int): số đếm có thể bị hiểu thành resource ID

Categories

  • AI
  • Backend
  • Đánh giá
  • DevOps
  • Flutter
  • Hướng dẫn
  • Lập trình Mobile
  • Phỏng vấn
  • React Native
  • Uncategorized

Weekly Newsletter

  • Buy JNews
  • Support Forum
  • Pre-sale Question
  • Contact Us

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
No Result
View All Result
  • Trang chủ
  • AI
  • Mobile
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
  • Flutter

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

wpDiscuz
0
0
Would love your thoughts, please comment.x
()
x
| Reply