子查询不一定比长OR更快,但更可控;其优势在于逻辑解耦、避免SQL爆炸、便于复用,并支持IN或哈希连接优化,前提是结果集小、有索引且为非相关子查询。

子查询比长 OR 更快吗?不一定,但更可控
直接说结论:用子查询替代一长串 WHERE col = 'a' OR col = 'b' OR col = 'c'... 通常不是为了“一定更快”,而是为了解耦逻辑、避免 SQL 长度爆炸、方便复用条件,同时让数据库有机会走 IN 或哈希连接优化。但前提是子查询结果集不大、有索引支撑、且不带相关性(即非相关子查询)。
常见错误现象:WHERE id IN (SELECT id FROM temp_list) 却没给 temp_list.id 加索引,导致每次外层扫描都触发全表查子查询;或者子查询里用了 ORDER BY + LIMIT 却没加 GROUP BY,结果语义模糊。
- 优先用
IN子查询替代多个OR等值判断,如status IN (SELECT code FROM status_whitelist) - 如果子查询来自固定列表,考虑改用
VALUES表达式(PostgreSQL)或临时表(MySQL 8.0+ 支持 CTE),避免反复解析 - 避免在子查询中引用外层字段(相关子查询),否则无法物化,性能会断崖下跌
- Oracle 用户注意:
IN子查询若返回NULL,整行会被过滤掉(NULL NOT IN (...)恒为UNKNOWN),而OR不会——这是行为差异,不是 bug
MySQL 里 IN 子查询被转成 EXISTS 的真实原因
MySQL 5.7+ 会对某些 IN 子查询自动重写为 EXISTS,尤其当子查询结果集大、外层表小时。这不是 bug,是优化器判断「半连接」(semi-join)更优。但如果你显式写了 EXISTS,它就不再尝试物化子查询结果,可能错过缓存机会。
使用场景:你有一张日志表 log_events,要查所有出现在白名单用户表 trusted_users 中的记录。直接写 user_id IN (SELECT user_id FROM trusted_users),MySQL 可能转成 EXISTS;但如果加了 /*+ NO_SEMIJOIN() */ 提示(8.0.24+),就能强制走物化路径。
- 检查执行计划:关注
Extra列是否出现Using join buffer (hash)或Start temporary,前者说明走了哈希半连接,后者说明物化成功 -
IN子查询若含DISTINCT或聚合,MySQL 通常放弃物化,直接走嵌套循环 - MySQL 5.6 及更早版本对
IN子查询支持弱,大量OR反而更稳——别盲目升级写法
PostgreSQL 中用 VALUES 替代硬编码 OR 的实操细节
PostgreSQL 支持把常量列表直接写成 VALUES 表达式,既避免 SQL 拼接风险,又能让查询计划复用。比如把 WHERE type = 'A' OR type = 'B' OR type = 'C' 换成 WHERE type IN (VALUES ('A'), ('B'), ('C')),本质是构造一个匿名内联表。
注意:这和子查询不同,VALUES 是语法层面的常量表,不触发额外查询,也不受子查询权限限制。但它不能动态传参(除非配合 UNNEST(ARRAY[...]))。
- 写法必须加括号和类型推导,例如
type IN (VALUES ('A'::text), ('B'), ('C')),否则可能报错“column does not exist” - 超过 100 个值时,PostgreSQL 会降级为顺序扫描,此时应建临时表并加索引
- 和
IN (SELECT ...)相比,VALUES的计划稳定性更高,适合配置类枚举匹配
SQL Server 的 INNER JOIN vs IN 子查询:别信“一定更快”的经验谈
SQL Server 优化器对 IN 和 JOIN 的处理高度依赖统计信息质量。如果子查询返回 5 行,IN 往往转成 Seek;如果返回 5000 行,优化器大概率改用 Hash Match,此时显式 JOIN 反而更容易控制连接策略(比如加 OPTION (LOOP JOIN))。
容易踩的坑:用 SELECT * FROM t1 WHERE id IN (SELECT id FROM t2 WHERE flag = 1),但 t2.flag 没索引——这时无论怎么改写,都是扫全表。子查询只是表层写法,底层还是数据分布和索引的事。
- 用
SET STATISTICS XML ON对比两种写法的实际执行计划,重点关注EstimatedRows和Actual Rows是否严重偏差 - 如果子查询含聚合(如
SELECT MAX(ts) FROM events GROUP BY user_id),IN会强制去重,而JOIN不会——语义已不同,不能简单互换 - SQL Server 2019+ 的批处理模式下,
IN子查询对列存储索引支持更好,但行存储仍建议优先测JOIN
真正卡住性能的往往不是 OR 还是子查询的选择,而是子查询本身有没有走索引、返回多少行、是否被当成相关子查询反复执行。先看执行计划里的实际行数和操作类型,再动笔改写。

















