SQL IN:基于离散列表筛选行
SQL IN 运算符用于判断某个值是否匹配列表中的任意一个元素。使用 SQL WHERE IN 谓词,可以在按固定离散值筛选时替代冗长的 OR 链,例如状态码、分类名称、地区 ID。需要按已知值集合过滤,并且希望条件随集合增长仍保持可读时,SQL WHERE IN 列表是更清晰的写法。
SQL IN 列表匹配示例
输出:
输出将显示在这里...
输出:
+--------+------------+
| name | category |
+--------+------------+
| 橡皮 | 文具 |
| 笔记本 | 文具 |
| 键盘 | 电子产品 |
| 鼠标 | 电子产品 |
+--------+------------+
4 row(s)
这个示例如何工作
CREATE TABLE与INSERT创建并填充了products表,包含 5 行数据,分属 3 个分类。WHERE category IN ('电子产品', '文具')会匹配category等于列表中任一值的行。逻辑上它等价于category = '电子产品' OR category = '文具'。家具这一行会被排除,因为它的category不在列表内。ORDER BY name让输出顺序稳定且可预测。
带字面量列表的 IN 与连写多个 OR 比较在语义上等价,但随着值集合增大,IN 更容易维护: 新增一个值只需在列表中加一项,不必再加一段 OR 条件。
什么是 SQL IN 运算符?
SQL IN 运算符是多个等值比较用 OR 连接后的简写。WHERE x IN (a, b, c) 在逻辑上等同于 WHERE x = a OR x = b OR x = c。在 SQL 的三值逻辑下,如果没有任何列表元素匹配且列表中没有 NULL,IN 返回 false;如果列表里包含 NULL 且不存在精确匹配,结果为 unknown,在 WHERE 子句中会把该行过滤掉。
处理空的 SQL WHERE IN 列表
多数数据库会把 WHERE col IN () 视为语法错误,这在动态生成 SQL WHERE IN 列表时很常见。应显式处理空列表: 要么跳过该筛选条件,要么在空列表应返回空结果时使用恒为假的谓词,例如 WHERE 1 = 0。
SQL NOT IN 与 NULL 陷阱
SQL NOT IN 是反向判断: 只有当值与列表中每个元素都不相等时才返回 true。列表里只要出现一个 NULL,就可能让结果变成 unknown,因为 x <> NULL 永远是 unknown,而 WHERE 会把 unknown 当作 false 处理:
-- 返回 0 行: NULL 会让谓词变成 UNKNOWN
SELECT name
FROM products
WHERE category NOT IN ('电子产品', NULL);
当列表来自子查询时,应在子查询内部过滤 NULL,或改用具备 NULL 安全语义的 NOT EXISTS。
IN 与子查询
IN 除了接收字面量列表,也可以接收子查询。从概念上看,子查询先产出一个值集合,外层查询再检查当前值是否属于这个集合:
SELECT name FROM products
WHERE category IN (
SELECT category FROM products WHERE name LIKE '%本%'
);
对于小规模、非相关子查询,这种写法通常很合适。若子查询返回行很多或存在相关性,EXISTS 谓词或 JOIN 往往性能更好,因为优化器可以应用半连接策略。
IN、EXISTS 与 JOIN 对比
| 写法 | 适用场景 |
|---|---|
IN (list) | 按小规模、已知的字面量值集合筛选 |
IN (subquery) | 与小规模且不可为空的结果集做成员匹配 |
EXISTS (subquery) | 相关子查询检查,或涉及可空外键 |
JOIN | 结果里需要两张表的列 |
BETWEEN | 值是连续区间,而不是离散项 |
规则: 离散值集合优先用 IN。子查询相关或可空时切换到 EXISTS。需要两侧数据时使用 JOIN。
性能说明
在有索引的列上,IN 是可走索引(sargable)的,优化器可对列表中的每个值执行索引查找。部分数据库引擎会先对字面量 IN 列表排序,再做二分搜索评估。字面量列表非常大(数百个值)时,查询解析和执行计划生成可能变慢;这类场景通常应把值加载到临时表后再 JOIN。
常见问题
SQL IN 和多个 OR 条件一样吗?
在逻辑上是一样的。WHERE x IN (1, 2, 3) 等价于 WHERE x = 1 OR x = 2 OR x = 3。多数情况下优化器会为两种写法生成相同执行计划,但 IN 更短,更便于在列表增长时维护。
为什么子查询里有 NULL 时,NOT IN 会返回 0 行?
NOT IN 会展开为一串 AND 比较: x <> a AND x <> b AND x <> NULL。由于 x <> NULL 永远是 unknown,整个表达式都无法为 true。应在子查询中过滤 NULL,或使用 NOT EXISTS 来规避这个问题。
IN 可以配合子查询使用吗?
可以。WHERE col IN (SELECT col FROM other_table) 会把列值与子查询结果集做成员匹配。对于小规模、不可为空的结果集,这通常很高效。若结果集很大或可能含 NULL,EXISTS 或 JOIN 往往给优化器更大的优化空间。