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ỏ

SQL WHERE và HAVING: lọc đúng dữ liệu trước và sau khi gom nhóm

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

Bạn cần tìm khách có tổng giá trị đơn đã thanh toán từ 20 trở lên. Câu truy vấn chạy được vẫn có thể trả sai nếu điều kiện trên từng đơn bị trộn với điều kiện trên tổng. WHERE amount >= 20 và HAVING SUM(amount) >= 20 đang hỏi hai câu khác nhau.

Bài dùng số nguyên giả lập để tập trung vào ngữ nghĩa SQL, đối chiếu PostgreSQL 17 ngày 29/10/2026. Các truy vấn đọc dữ liệu được chạy trên SQLite CLI 3.54.0; chưa chạy PostgreSQL server hoặc đo kế hoạch thực thi. Giá trị trong ví dụ là đơn vị minh họa, không phải thiết kế tiền tệ hoàn chỉnh.

Chốt câu hỏi trước khi viết truy vấn

Ta có ba bước: chỉ giữ đơn paid, gom theo khách, rồi giữ nhóm có tổng từ 20. Theo Table Expressions của PostgreSQL 17, WHERE lọc dòng trước khi gom nhóm, còn HAVING lọc các nhóm đã hình thành.

Đây là thứ tự để hiểu kết quả, không phải cam kết database sẽ thực thi vật lý đúng từng bước ấy. Bộ tối ưu có thể chọn cách xử lý khác mà vẫn phải giữ ngữ nghĩa.

Tạo bảng trong database thử nghiệm riêng, không chạy phần tạo bảng lên database ứng dụng:

CREATE TABLE orders_demo (
  id INTEGER PRIMARY KEY,
  customer_id TEXT NOT NULL,
  status TEXT NOT NULL,
  amount INTEGER NOT NULL
);

INSERT INTO orders_demo VALUES
  (1, 'A', 'paid', 12),
  (2, 'A', 'paid', 9),
  (3, 'A', 'pending', 100),
  (4, 'B', 'paid', 25),
  (5, 'B', 'paid', 5),
  (6, 'C', 'pending', 40);

A có tổng paid là 21, B là 30. C không có đơn paid. Dòng pending trị giá 100 của A là một ca cố tình cài vào fixture để phát hiện truy vấn quên lọc trạng thái.

Viết điều kiện đúng tầng

SELECT customer_id, SUM(amount) AS paid_total
FROM orders_demo
WHERE status = 'paid'
GROUP BY customer_id
HAVING SUM(amount) >= 20
ORDER BY customer_id;

Kết quả đã kiểm tra là A|21 và B|30. Điều kiện status xác định những dòng được tham gia tính tổng. Điều kiện trong HAVING quyết định nhóm nào xuất hiện trong kết quả cuối.

Lặp lại SUM(amount) trong HAVING thay vì dựa vào alias paid_total giúp ví dụ dùng được với PostgreSQL. Việc một engine chấp nhận alias ở vị trí nào không tự bảo đảm engine khác cũng chấp nhận.

Nếu muốn quan sát bước trung gian, chạy lại truy vấn trên nhưng bỏ dòng HAVING. Bạn vẫn nhận A và B với tổng tương ứng; C không xuất hiện vì đã không còn dòng nào sau WHERE. Nếu cần hiện cả khách không có đơn paid với tổng bằng không, đó là yêu cầu khác, thường cần bảng khách và outer join.

Một thay đổi nhỏ làm mất khách A

SELECT customer_id, SUM(amount) AS paid_total
FROM orders_demo
WHERE status = 'paid' AND amount >= 20
GROUP BY customer_id
ORDER BY customer_id;

Truy vấn này chỉ còn B|25. Cả hai đơn paid của A đều nhỏ hơn 20 nên bị loại trước khi cộng. Đơn trị giá 5 của B cũng bị loại. Nó trả tổng của những đơn paid riêng lẻ đạt ngưỡng, không phải tổng mọi đơn paid của khách đạt ngưỡng.

Một lỗi khác là bỏ WHERE và chỉ dùng HAVING trên tổng. Khi đó pending cũng đi vào phép cộng: A thành 121, C thành 40. Cả hai lỗi đều có thể bị che nếu dữ liệu thử chỉ gồm mỗi khách một đơn paid.

Kiểm tra bằng các ca có chủ đích

Fixture trên có ba đặc điểm đáng giữ trong test của ứng dụng: một khách đạt ngưỡng nhờ nhiều đơn nhỏ, một khách có cả đơn lớn lẫn nhỏ, một khách chỉ có đơn chưa thanh toán. Thêm ca tổng đúng 20 để kiểm tra ranh giới >= thay vì >.

Đọc cả danh sách khách và tổng, đừng chỉ kiểm tra số dòng. Hai truy vấn có thể cùng trả hai khách nhưng tổng đã sai. ORDER BY làm thứ tự hiển thị ổn định cho bài kiểm tra; GROUP BY không tự hứa sắp xếp kết quả.

Các hàm aggregate có quy tắc riêng với NULL và tập rỗng. Tài liệu Aggregate Functions là nguồn cần đọc khi chuyển ví dụ sang cột cho phép NULL. Ở đây amount NOT NULL là lựa chọn có chủ đích để không nhập nhằng giữa bài toán lọc và bài toán dữ liệu thiếu.

Giới hạn và cách áp dụng

SQLite cho phép một số cách chọn cột ngoài GROUP BY mà PostgreSQL không chấp nhận. Ví dụ chỉ chọn khóa nhóm và biểu thức aggregate, tránh dựa vào hành vi chọn một dòng bất kỳ. Tài liệu SELECT của SQLite mô tả các khác biệt này.

Trong API thật, dùng tham số truy vấn cho trạng thái và ngưỡng thay vì nối chuỗi từ input. Nếu kết quả sai, trước tiên kiểm tra dòng nào được phép tham gia tổng; sau đó mới tối ưu index hoặc đọc EXPLAIN. Sửa hiệu năng trước khi chốt ngữ nghĩa thường chỉ làm một truy vấn sai chạy nhanh hơn.

Khi review, đọc WHERE thành “những dòng nào được tính” và HAVING thành “những nhóm nào được trả”. Cách đọc đó giúp phát hiện việc vô tình loại các đơn nhỏ trước khi cộng.

Nguồn tham khảo

  • PostgreSQL 17: Table Expressions
  • PostgreSQL 17: Aggregate Functions
  • SQLite: SELECT

Tags: PostgreSQLSQLTruy vấn dữ liệu
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 UNION và UNION ALL: đừng làm mất sự kiện khi hợp nhất dữ liệu

by Suong Nguyen
28/10/2026
0

Dùng fixture log web và mobile để thấy UNION loại trùng theo toàn bộ cột được chọn, còn UNION ALL giữ số lần xuất hiện.

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

SQL ROW_NUMBER: lấy nguyên bản ghi mới nhất của mỗi khách hàng

by Suong Nguyen
27/10/2026
0

Xếp hạng đơn hàng theo từng khách, xử lý thời điểm trùng nhau và phân biệt lọc trước với lọc sau window function.

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

LEFT JOIN và COUNT: đừng đếm khách chưa có đơn thành một đơn hàng

by Suong Nguyen
26/10/2026
0

Phân biệt COUNT(*) và COUNT(cột) trên LEFT JOIN, đặt điều kiện ON đúng mục tiêu và kiểm tra các hàng không có dữ liệu khớp.

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

SQL NOT IN gặp NULL: vì sao truy vấn loại trừ trả về rỗng?

by Suong Nguyen
25/10/2026
0

Dựng một fixture nhỏ để hiểu NOT IN với NULL, chọn NOT EXISTS theo nghiệp vụ và kiểm tra cả cột bên trái lẫn bên phải.

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 TextInput: bàn phím numeric không thay thế validation
  • Phỏng vấn JavaScript: vì sao tách method thành callback lại mất this?
  • Android NumberFormat: tách locale, mã tiền tệ và giá trị cần hiển thị

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