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 EXISTS: lọc khách có đơn mà không nhân dòng khách hàng

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

Endpoint cần liệt kê khách đã có ít nhất một đơn thanh toán. Bạn JOIN bảng đơn vào khách rồi thấy cùng khách xuất hiện hai lần. Vấn đề bắt đầu từ đơn vị kết quả: bạn muốn một dòng mỗi khách, trong khi JOIN tạo một dòng mỗi cặp phù hợp.

Bài này đối chiếu PostgreSQL 17 và tài liệu SQLite ngày 02/11/2026. Fixture được chạy bằng Python 3.9.6 với SQLite 3.54.0 trong database bộ nhớ. Chưa chạy PostgreSQL server và chưa đo hiệu năng; mục tiêu là kiểm chứng số dòng và điều kiện truy vấn.

Tạo dữ liệu có chủ ý

Chạy SQL sau trên database thử trống, không dùng bảng production. Hai khách cùng tên giúp kiểm tra cả trường hợp gộp nhầm người:

CREATE TABLE demo_customers (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL
);
CREATE TABLE demo_orders (
    id INTEGER PRIMARY KEY,
    customer_id INTEGER NOT NULL,
    status TEXT NOT NULL
);
INSERT INTO demo_customers VALUES (1, 'An'), (2, 'Binh'), (3, 'An');
INSERT INTO demo_orders VALUES
    (11, 1, 'paid'),
    (12, 1, 'paid'),
    (13, 2, 'pending'),
    (14, 3, 'paid');

Fixture cố ý không định nghĩa toàn bộ constraint của một hệ thống đơn hàng. Các ID liên kết đều có khách hợp lệ, nhưng đó là dữ liệu thử, không phải bằng chứng rằng ứng dụng đã bật kiểm tra khóa ngoại. Giá trị trạng thái là chuỗi demo, chưa mô hình hóa hoàn tiền hay thanh toán từng phần.

JOIN giữ từng cặp phù hợp

SELECT c.id, c.name
FROM demo_customers AS c
JOIN demo_orders AS o ON o.customer_id = c.id
WHERE o.status = 'paid'
ORDER BY c.id, o.id;

Kết quả đã kiểm tra là (1, An), (1, An), (3, An). Khách 1 có hai đơn paid nên góp hai dòng. Bản thân JOIN hoạt động đúng; truy vấn đang diễn đạt quan hệ nhiều kết quả hơn yêu cầu của endpoint.

Nếu cần mỗi đơn kèm tên khách, bạn nên giữ JOIN và chọn cả o.id. Nhưng nếu chỉ hỏi khách có thỏa điều kiện tồn tại hay không, không cần mang từng đơn vào kết quả ngoài.

Dùng EXISTS cho điều kiện có ít nhất một dòng

SELECT c.id, c.name
FROM demo_customers AS c
WHERE EXISTS (
    SELECT 1
    FROM demo_orders AS o
    WHERE o.customer_id = c.id
      AND o.status = 'paid'
)
ORDER BY c.id;

Kết quả là (1, An), (3, An). Subquery liên hệ với dòng khách ngoài qua o.customer_id = c.id. Khi điều kiện tồn tại đúng, dòng khách được giữ một lần, bất kể khách có một hay nhiều đơn phù hợp. SELECT 1 chỉ thể hiện mục đích kiểm tra tồn tại; nó không có nghĩa subquery trả duy nhất một dòng.

Đừng bỏ điều kiện liên hệ rồi chỉ giữ status = 'paid'. Khi đó, chỉ cần một đơn paid ở bất kỳ khách nào là mọi khách đều vượt qua bộ lọc. Với fixture này, truy vấn sai ấy trả cả khách 2. Đây là case nhỏ nhưng hữu ích trong review và kiểm thử.

DISTINCT có thay thế được không?

Nếu JOIN rồi SELECT DISTINCT c.id, c.name, fixture cũng cho hai khách mong muốn. Tuy nhiên, dùng DISTINCT c.name chỉ còn một giá trị An, làm mất một khách thật. Cần chọn cột theo định danh nghiệp vụ, không dùng việc kết quả trông gọn hơn làm bằng chứng đúng.

EXISTS làm ý định lọc dễ đọc hơn trong trường hợp này. Điều đó không chứng minh nó luôn nhanh hơn JOIN kèm DISTINCT. Kế hoạch thực thi phụ thuộc database, index, thống kê và dữ liệu thực. Có thể xem xét index bắt đầu từ customer_id và điều kiện trạng thái theo workload, rồi dùng công cụ phân tích của database đang triển khai.

Kiểm tra trước khi đưa vào API

Thử khách không có đơn, chỉ pending, một paid, nhiều paid và hai khách trùng tên. Với fixture, thêm một đơn paid nữa cho khách 1 phải giữ nguyên tập ID [1, 3] của truy vấn EXISTS. Kiểm tra cả tổng số dòng nếu tầng API tính pagination, vì đếm cặp JOIN và đếm khách là hai yêu cầu khác nhau.

Nếu endpoint cần số đơn hoặc tổng tiền, EXISTS chỉ giải quyết bộ lọc tồn tại; hãy thiết kế phần tổng hợp riêng. Chốt “mỗi dòng đại diện cho gì” trước khi thêm JOIN là cách đơn giản để tránh sửa kết quả bằng DISTINCT một cách máy móc.

Nguồn tham khảo

  • PostgreSQL 17: EXISTS và subquery liên hệ
  • PostgreSQL 17: table expressions và JOIN
  • SQLite: EXISTS và correlated subqueries

Tags: DatabaseSQLTruy vấn
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ỏ

Lọc thời gian trong SQL: dùng khoảng nửa mở để không trùng biên

by Suong Nguyen
01/11/2026
0

So sánh BETWEEN với điều kiện >= đầu kỳ và < đầu kỳ kế tiếp bằng fixture ở đúng ranh giới tháng.

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

SQLite: có FOREIGN KEY nhưng vẫn lọt dữ liệu mồ côi?

by Suong Nguyen
31/10/2026
0

Bật và đọc lại PRAGMA foreign_keys trên từng connection SQLite, rồi thử cả insert sai lẫn kiểm tra dữ liệu đã tồn tại.

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

UNIQUE và NULL trong PostgreSQL: chốt quy tắc cho khóa tùy chọn

by Suong Nguyen
30/10/2026
0

Phân biệt cột tùy chọn có UNIQUE, UNIQUE NULLS NOT DISTINCT và NOT NULL UNIQUE trước khi viết migration.

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

by Suong Nguyen
29/10/2026
0

Dùng ví dụ đơn hàng để phân biệt điều kiện trên từng dòng với điều kiện trên tổng của một nhóm.

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

Object.hasOwn và in: chọn theo thuộc tính riêng hay cả prototype

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 onLayout: đo vùng card thay vì lấy chiều rộng cửa sổ
  • Object.freeze chỉ đóng băng nông: giải thích bằng assertion
  • Android getString: dùng placeholder có thứ tự để dịch nguyên câu

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