首页/数据/SQL 速查/IN 列表条件

IN 列表条件

IN 命中一组离散值,等价于若干 OR 但更简洁;列表很长时优化器代价上升,NOT IN 遇到 NULL 是经典陷阱。

SQL 速查数据更新于 2026-10-13

IN 列表条件是什么

IN 用来命中一组离散值,写法比一长串 OR 更简洁,语义上也等价于若干等值条件的并集。列表里都是常量时,优化器通常能改写成多点等值查找,走 range 访问。列表很长时解析与优化代价上升,几百上千个常量建议改成临时表或值列表再 JOIN。最需要注意的是 NOT IN:只要列表或子查询里出现 NULL,结果就会整体为空,判断「不在集合中」优先用 NOT EXISTS。

基本信息

等价形式x IN (a, b) 即 x = a OR x = b
可写类型常量列表、子查询、表达式结果
NULL 规则列表含 NULL 时:命中仍命中,未命中变 UNKNOWN
索引使用常量列表通常可改写为 range 访问
列表长度值很多时建议改用临时表 INNER JOIN
适用平台标准 SQL,MySQL 8.0 与 PostgreSQL 一致

典型使用场景

  • 按状态筛选:status IN (1, 3, 5) 圈定几种状态
  • 批量查详情:按一批 id 查对应记录
  • 地区或渠道白名单:只保留指定范围的数据
  • 排除枚举值:NOT IN 排除几种已知的无效值
  • 子查询过滤:id IN (SELECT …) 保留另一张表里存在的记录
  • 参数化查询:应用传一组 id,用 IN 一次查完

示例

示例
SELECT * FROM orders WHERE status IN (1, 3, 5);
SELECT * FROM users  WHERE city IN ('Beijing', 'Shanghai');

-- 等价写法,列表长时可读性明显变差
SELECT * FROM orders WHERE status = 1 OR status = 3 OR status = 5;

-- 子查询形式:保留另一张表里存在的记录
SELECT * FROM users
WHERE id IN (SELECT user_id FROM vip_members);

-- 列表很长时改用临时表连接,避免超长 SQL 文本
SELECT o.* FROM orders o JOIN tmp_ids t ON t.id = o.id;

IN 与相关写法

写法语义注意
IN (常量列表)等于列表中任一值命中 NULL 不影响结果
IN (子查询)存在于子查询结果中子查询返回多列时需写成元组形式
NOT IN不在集合中含 NULL 时结果整体为空
EXISTS存在匹配行语义清晰,适合大子查询
BETWEEN连续区间与 IN 适用场景不同
IN 里的值按常量处理,列表为常量时优化器可改写为多点等值查找,仍能利用索引;这与 NOT IN 的 NULL 风险是两件事。
NOT IN 列表或子查询含 NULL 时结果可能整体为空,判断「不在集合中」优先用 NOT EXISTS。
IN 里放几百上千个常量会让 SQL 文本巨大、解析变慢,改用 JOIN 临时表或值列表更稳,也便于复用执行计划。

使用要点

  • 列表值数量控制在几十个以内,更多就换 JOIN 或临时表
  • 子查询作为 IN 的参数时要确认返回单列,否则语法报错
  • NOT IN 出现时先检查内层列是否可空
  • IN 内的值类型要与列类型一致,避免隐式转换拖累索引
  • 参数化查询里 IN 的元素个数变化会影响执行计划复用
  • 用例里覆盖空列表场景:空 IN 通常返回空结果,业务要能接受

易错点

  • NOT IN 列表或子查询含 NULL 时结果可能整体为空,判断「不在集合中」优先用 NOT EXISTS
  • IN 里放几百上千个常量会让 SQL 文本巨大、解析变慢,改用 JOIN 或临时表更稳
  • 子查询返回多列时直接写 IN,语法报错或语义与预期不符
  • IN 内值类型与列类型不一致,触发隐式转换导致索引失效
  • 把 IN 当成连续区间用,遗漏中间值导致结果不全,区间场景应使用 BETWEEN

数据来源与参考

  • MySQL 8.0 Reference Manual · IN Operator
  • PostgreSQL Documentation · IN Predicate
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌