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 通常返回空结果,业务要能接受