IN替代OR更简洁可读且支持子查询,但仅在同列、有索引、值离散、数量适中(≤1000)、无NULL时才更优;值过多、连续范围、子查询过大或无索引时应选BETWEEN、JOIN或NOT EXISTS等方案。

用 IN 替代多个 OR 是更简洁、可读性更高且数据库优化器更易处理的方式,但前提是值的数量和类型得合理——不是所有场景都适合无脑替换。
什么时候必须用 IN 而不能只靠 OR
当要匹配的值超过 3–5 个时,手写一长串 column = 'a' OR column = 'b' OR column = 'c' ... 不仅难维护,还可能触发某些数据库(如 MySQL 5.7 旧版本)对布尔表达式深度的隐式限制。此外,IN 支持子查询,而 OR 无法直接等价替代:
-
WHERE id IN (SELECT user_id FROM logs WHERE action = 'login')—— 合法且高效 -
WHERE id = (SELECT user_id FROM logs WHERE action = 'login')—— 报错:Subquery returns more than 1 row
IN 的空值(NULL)陷阱
IN 列表中只要有一个 NULL,整个表达式在多数数据库里会返回 UNKNOWN(不是 TRUE 或 FALSE),导致该行被过滤掉,即使其他值完全匹配。例如:
SELECT * FROM users WHERE status IN ('active', NULL, 'pending');
这句实际等价于 status = 'active' OR status = NULL OR status = 'pending',而 status = NULL 永远不成立(要用 IS NULL)。结果就是:没有一行能命中。
- 避免在
IN列表里硬写NULL - 需要同时匹配非空值和空值?拆开写:
WHERE status IN ('active', 'pending') OR status IS NULL - 若值来自参数或子查询,先用
WHERE col IS NOT NULL过滤掉空值
性能差异:IN 不一定比 OR 快,尤其小数据量
现代优化器(PostgreSQL 12+、SQL Server 2019、MySQL 8.0)通常能把短列表的 IN 重写为等价 OR 树,执行计划几乎一致。但以下情况会影响表现:
- 列表长度超过几百项时,
IN可能触发临时哈希表构建(如 PostgreSQL),而手写OR仍走索引范围扫描 - Oracle 对
IN列表有 1000 项硬限制,超限报错:ORA-01795: maximum number of expressions in a list is 1000 - 如果值来自子查询,且子查询结果很大,考虑加索引或改用
JOIN——IN子查询可能被反复执行(取决于优化器是否物化)
字符串与数字混用的隐式转换风险
写 WHERE id IN ('1', '2', '3') 看似方便,但如果 id 是整型字段,数据库可能放弃索引走全表扫描(尤其 MySQL 在某些字符集/排序规则下)。
- 确保
IN中每个字面量类型与列类型严格一致 - 整数列就用
IN (1, 2, 3);UUID 列用IN ('a1b2...', 'c3d4...'),别加引号套数字 - 用参数化查询时,由驱动自动绑定类型,这类问题会少很多,但仍需检查 ORM 生成的 SQL
真正容易被忽略的是:当 IN 和 NOT IN 遇上空值时行为完全不同——NOT IN (1, 2, NULL) 直接让整条查询返回空结果集,因为任何值跟 NULL 比较都得不到 TRUE。这个坑比写错括号还难 debug。










