5 分钟搞懂子查询
标量、列、行、表子查询 + 相关 vs 不相关 + EXISTS 的真相
四种子查询
1. 标量(返回一个值)
SELECT name, (SELECT COUNT(*) FROM orders WHERE user_id = u.id) AS order_count
FROM users u;2. 列(返回一列多行)
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);3. 行(返回一行多列)
SELECT * FROM users WHERE (id, status) = (SELECT user_id, 'paid' FROM orders LIMIT 1);4. 表(在 FROM 里)
SELECT t.user_id, t.cnt
FROM (SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id) t
WHERE t.cnt > 10;相关 vs 不相关
不相关:子查询独立可执行:
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders); -- 子查询独立相关:子查询用了外层的列,每行都得跑一次:
SELECT name FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id); -- 用了 u.id相关子查询慢——MariaDB 优化器有时能把它改写成 JOIN,但不要赌。
EXISTS 通常比 IN 快
-- 大表场景:
SELECT * FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE user_id = users.id); -- 快
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders); -- 可能慢原因:EXISTS 找到第一行就 short-circuit,IN 要把整个子查询结果集化。
NOT EXISTS / NOT IN 的 NULL 坑
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders);如果 orders.user_id 里有 NULL,整个 NOT IN 返回空集!这是 SQL 三值逻辑的陷阱。
对策:用 NOT EXISTS 或加 IS NOT NULL:
SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);用 CTE 替代复杂子查询
CTE 可读性远胜嵌套子查询。详见 CTE。