Truy vấn con trong SQL là một kỹ thuật giúp truy vấn dữ liệu linh hoạt và thông minh hơn. Thay vì chỉ sử dụng một câu lệnh SELECT đơn giản, chúng ta có thể viết một truy vấn bên trong một truy vấn khác để tính toán hoặc lọc dữ liệu.
Trong thực tế, truy vấn con thường được dùng để trả lời các câu hỏi như:
- Sản phẩm nào đã từng được bán?
- Khách hàng nào chưa từng đặt hàng?
- Sản phẩm nào có giá cao hơn mức trung bình?
- Khách hàng nào có nhiều đơn hàng nhất?
Trong bài học này, bạn sẽ tìm hiểu cách sử dụng Subquery trong SQL để lọc và xử lý dữ liệu, cùng với các ví dụ minh họa giúp hiểu rõ cách áp dụng trong thực tế.

1. Truy vấn con là gì?
Truy vấn con (subquery) là một câu lệnh SELECT nằm bên trong một câu lệnh SQL khác. Subquery thường được đặt trong dấu ngoặc: (SELECT …)
Cơ chế hoạt động:
- SQL thực thi subquery trước
- Kết quả của subquery được dùng cho truy vấn chính
Subquery có thể xuất hiện ở nhiều vị trí khác nhau trong câu lệnh SQL như SELECT, WHERE và FROM. Việc hiểu rõ cách hoạt động của truy vấn con trong SQL sẽ giúp bạn viết các truy vấn linh hoạt và hiệu quả hơn.

2. Truy vấn con trong SELECT
Ví dụ: hiển thị khách hàng và tổng số đơn hàng của mỗi khách.
Trong truy vấn này, mỗi dòng là một khách hàng và cột total_orders được tính bằng cách đếm số đơn hàng tương ứng trong bảng orders. Điểm hay của kiểu này là giúp có thêm chỉ số tổng hợp.
Ví dụ: Hiển thị khách hàng và tổng đơn hàng của khách hàng đó
Trong mệnh đề SELECT chứa một câu truy vấn SELECT đếm toàn bộ order theo customerID tương ứng với customerID từ bảng Customers
SELECT
id,
first_name,
last_name,
email,
(
SELECT COUNT(*)
FROM orders o
WHERE o.customer_id = c.id
) AS total_orders
FROM customers c;

3. Truy vấn con trong WHERE
Truy vấn con trong WHERE được dùng để lọc dữ liệu dựa trên kết quả của một query khác. Ứng dụng trong:
- Tìm khách hàng VIP
- Lọc sản phẩm bán chạy
- Tìm đơn hàng bất thường
Tìm các sản phẩm đã từng được bán
SELECT product_name, price
FROM products
WHERE id IN (
SELECT product_id
FROM order_details
);

Lấy các sản phẩm đã từng được bán với số lượng là 2 trong đơn hàng
SELECT product_name, price
FROM products
WHERE id IN (
SELECT product_id
FROM order_details
WHERE quantity = 2
);

Tìm các khách hàng chưa từng đặt hàng
SELECT c.first_name, c.last_name, c.email
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);

4. Truy vấn con trong FROM
Khi logic tính toán hơi dài, bạn có thể “đóng gói” nó thành một bảng tạm trong FROM. Subquery trong FROM tạo ra bảng tạm thời để sử dụng trong query chính. Ứng dụng trong:
- Tạo bảng tổng hợp
- Tính toán phức tạp
- Tạo báo cáo nhiều cấp
Ví dụ: Lọc khách hàng có email là @email.com từ truy vấn con
Truy vấn con trong FROM ở đây được dùng để liệt kê khách hàng CÓ thông tin email: customer_id, full_name, email, phone
SELECT id, full_name, email, phone
FROM (
SELECT
id,
first_name || ' ' || last_name AS full_name,
email,
phone
FROM customers
WHERE email IS NOT NULL
) customer_info
WHERE email LIKE '%@email.com'
ORDER BY full_name;

Ví dụ: Tính giá sau khi giảm 10% từ truy vấn con
Truy vấn con trong FROM ở đây được dùng để liệt kê sản phẩm vẫn còn hàng trong kho (stock_quantity > 0)
SELECT
product_name,
original_price,
ROUND(original_price * 0.9, 2) AS discounted_price,
original_price - ROUND(original_price * 0.9, 2) AS discount_amount
FROM (
SELECT
product_name,
price AS original_price,
brand,
stock_quantity
FROM products
WHERE stock_quantity > 0
) available_products
WHERE original_price > 50
ORDER BY original_price DESC;

5. EXISTS và IN
Hai toán tử thường được dùng với subquery là EXISTS và IN.
EXISTS
EXISTS dùng để kiểm tra xem subquery có trả về dòng dữ liệu hay không. Nếu có ít nhất một dòng → TRUE, nếu subquery trống thì FALSE.
Ví dụ: Tìm khách hàng có ít nhất một đơn hàng
SELECT id, first_name, last_name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);
EXISTS không quan tâm đến giá trị được chọn trong subquery. Vì vậy, SELECT 1, SELECT * hay SELECT o.id đều cho kết quả giống nhau. Mục đích của EXISTS chỉ là kiểm tra có dòng dữ liệu nào tồn tại hay không.

IN
IN dùng để kiểm tra một giá trị có nằm trong danh sách giá trị hay không.Ví dụ: Tìm đơn hàng của những khách hàng gia nhập vào năm 2023
SELECT id, customer_id, order_date
FROM orders
WHERE customer_id IN (
SELECT id
FROM customers
WHERE EXTRACT(YEAR FROM ngay_gia_nhap) = 2023
);

Sử dụng EXISTS, IN, JOIN khi nào?
- EXISTS: Khi chỉ cần kiểm tra sự tồn tại, hiệu suất tốt
- IN: Khi cần so sánh với danh sách giá trị cụ thể
- JOIN: Khi cần lấy thêm dữ liệu từ bảng thứ hai
6. UNION, UNION ALL
UNION dùng để kết hợp kết quả của nhiều câu lệnh SELECT.
Điều kiện:
- số cột giống nhau
- kiểu dữ liệu tương ứng
UNION
UNION kết hợp kết quả nhiều lệnh SELECT và loại bỏ các dòng trùng lặp trong kết quả cuối cùng.
Ví dụ: Danh sách email từ khách hàng và có thể từ nhân viên (giả sử)
SELECT email, 'Customer' AS type
FROM customers
WHERE email IS NOT NULL
UNION
SELECT 'admin@company.com', 'Admin'
UNION
SELECT 'support@company.com', 'Support'
ORDER BY type, email;

Ví dụ 2: Lấy danh sách email và họ tên của khách hàng và nhân viên
SELECT email AS "Email khách", first_name || ' ' || last_name as ho_ten
FROM customers
WHERE email IS NOT NULL
UNION
SELECT 'admin@company.com', 'Admin'
UNION
SELECT email AS "Email NV" , first_name || ' ' || last_name as ho_ten
FROM employees
WHERE is_active=TRUE

UNION ALL
UNION ALL cũng kết hợp kết quả nhiều câu SELECT nhưng giữ lại tất cả các dòng trùng lặp. Nó thường nhanh hơn UNION vì không cần xử lý loại bỏ các bản ghi trùng.
Ví dụ: Danh sách sản phẩm đắt tiền và sản phẩm còn ít hàng
SELECT id, product_name, price, stock_quantity
FROM products
WHERE price > 10000000
UNION ALL
SELECT id, product_name, price, stock_quantity
FROM products
WHERE stock_quantity < 20

Nếu bỏ ALL, các dòng trùng lặp sẽ bị loại bỏ.

7. JOIN và SUBQUERY
Trong nhiều trường hợp, JOIN và Subquery có thể cho cùng kết quả. Ví dụ: tìm các sản phẩm đã từng được bán.
Subquery:
SELECT *
FROM products
WHERE id IN (
SELECT product_id FROM order_details
);
Join
SELECT DISTINCT p.*
FROM products p
JOIN order_details od
ON p.id = od.product_id;
Hai truy vấn trên đều trả về danh sách sản phẩm đã từng xuất hiện trong đơn hàng. Vậy nên dùng cái nào? Không có lựa chọn tuyệt đối, nhưng trong thực tế có một số quy tắc kinh nghiệm sau.
| Trường hợp | Nên dùng |
| Cần lấy thêm dữ liệu từ bảng khác | JOIN |
| Chỉ cần kiểm tra sự tồn tại | EXISTS |
| Lọc theo một danh sách giá trị | IN / Subquery |
| Truy vấn nhiều bước, logic phức tạp | Subquery hoặc CTE |
- Khi cần thêm dữ liệu thì dùng JOIN
SELECT c.first_name, o.order_date
FROM customers c
JOIN orders o ON c.id = o.customer_id;
Ở đây bắt buộc dùng JOIN vì cần lấy dữ liệu từ cả hai bảng.
- Khi chỉ cần kiểm tra tồn tại thì dùng EXISTS
SELECT first_name, last_name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);
EXISTS thường nhanh hơn JOIN khi chỉ kiểm tra sự tồn tại.
- Khi lọc theo tập giá trị thì dùng IN
SELECT product_name
FROM products
WHERE id IN (
SELECT product_id
FROM order_details
);
Phù hợp khi cần lọc theo một danh sách giá trị.
Quy tắc đơn giản để nhớ
- Lấy dữ liệu → JOIN
- Kiểm tra tồn tại → EXISTS
- Lọc theo danh sách → IN
JOIN và Subquery có thể giải quyết nhiều bài toán giống nhau. Tuy nhiên, JOIN thường được dùng khi cần kết hợp dữ liệu từ nhiều bảng, còn Subquery phù hợp khi cần tính toán hoặc lọc dữ liệu dựa trên kết quả của một truy vấn khác. Trong thực tế, hãy chọn cách viết giúp truy vấn dễ đọc, rõ ràng và dễ bảo trì.
8. So sánh vị trí sử dụng Subquery
| Vị trí | Mục đích |
| SELECT | tính toán giá trị |
| WHERE | lọc dữ liệu |
| FROM | tạo bảng tạm |
KẾT LUẬN
Truy vấn con trong SQL là một kỹ thuật quan trọng giúp xây dựng các truy vấn linh hoạt và mạnh mẽ hơn. Subquery cho phép sử dụng kết quả của một truy vấn để phục vụ cho truy vấn khác, từ đó xử lý nhiều bài toán phân tích dữ liệu phức tạp.
Trong bài học này, chúng ta đã tìm hiểu cách sử dụng Subquery trong SELECT, WHERE và FROM, cùng với các toán tử phổ biến như EXISTS và IN, cũng như cách kết hợp dữ liệu bằng UNION và UNION ALL.
Việc hiểu và sử dụng thành thạo truy vấn con trong SQL sẽ giúp bạn xây dựng các truy vấn rõ ràng, linh hoạt và hiệu quả hơn khi làm việc với cơ sở dữ liệu.


