MariaDB 中文社区

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

下一步

本页目录