Subquery là một query lồng bên trong query khác. Có hai kiểu, khác nhau ở chi phí:
- Không tương quan (chạy một lần):
... WHERE price > (SELECT AVG(price) FROM products). - Tương quan (chạy lại cho MỖI dòng của query ngoài — dễ thành O(n²), rất chậm với bảng lớn):
... WHERE salary > (SELECT AVG(salary) FROM employees WHERE dept = e.dept).
Vài điểm hay được hỏi:
- EXISTS vs IN: EXISTS (SELECT 1 ...) dừng ngay khi gặp match đầu tiên — hợp khi tập con lớn. IN (...) nạp cả danh sách. Bẫy: NOT IN mà subquery có NULL sẽ trả về rỗng — nên dùng NOT EXISTS.
- Scalar subquery trong SELECT: SELECT name, (SELECT COUNT() FROM orders WHERE user_id = u.id) FROM users u — tương đương LEFT JOIN nhưng thường chậm hơn vì chạy N lần.
- LATERAL (PostgreSQL): lấy "top-N theo nhóm", vd 3 đơn mới nhất của mỗi user — FROM users u, LATERAL (SELECT FROM orders WHERE user_id = u.id ORDER BY created_at DESC LIMIT 3) o.
Khi nào subquery hơn JOIN: kiểm tra tồn tại (EXISTS), khi JOIN gây nhân dòng (fan-out do quan hệ một-nhiều), hoặc cần một giá trị tổng hợp đơn.
A subquery is a query nested inside another. There are two kinds, differing in cost:
- Uncorrelated (runs once):
... WHERE price > (SELECT AVG(price) FROM products). - Correlated (re-runs for EVERY outer row — easily O(n²), very slow on large tables):
... WHERE salary > (SELECT AVG(salary) FROM employees WHERE dept = e.dept).
A few frequently-asked points:
- EXISTS vs IN: EXISTS (SELECT 1 ...) stops at the first match — good for large subsets. IN (...) loads the whole list. Trap: NOT IN with a NULL in the subquery returns empty — prefer NOT EXISTS.
- Scalar subquery in SELECT: SELECT name, (SELECT COUNT() FROM orders WHERE user_id = u.id) FROM users u — equivalent to a LEFT JOIN but usually slower since it runs N times.
- LATERAL (PostgreSQL): great for "top-N per group", e.g. each user's 3 latest orders — FROM users u, LATERAL (SELECT FROM orders WHERE user_id = u.id ORDER BY created_at DESC LIMIT 3) o.
When a subquery beats a JOIN: existence checks (EXISTS), when a JOIN would fan out (duplicate rows from a one-to-many), or when you need a single aggregate value.