SQL IN:基于离散列表筛选行

SQL IN 运算符用于判断某个值是否匹配列表中的任意一个元素。使用 SQL WHERE IN 谓词,可以在按固定离散值筛选时替代冗长的 OR 链,例如状态码、分类名称、地区 ID。需要按已知值集合过滤,并且希望条件随集合增长仍保持可读时,SQL WHERE IN 列表是更清晰的写法。

SQL IN 列表匹配示例

输出:

输出将显示在这里...

输出:

+--------+------------+
| name   | category   |
+--------+------------+
| 橡皮   | 文具       |
| 笔记本 | 文具       |
| 键盘   | 电子产品   |
| 鼠标   | 电子产品   |
+--------+------------+
4 row(s)

这个示例如何工作

  1. CREATE TABLEINSERT 创建并填充了 products 表,包含 5 行数据,分属 3 个分类。
  2. WHERE category IN ('电子产品', '文具') 会匹配 category 等于列表中任一值的行。逻辑上它等价于 category = '电子产品' OR category = '文具'
  3. 家具 这一行会被排除,因为它的 category 不在列表内。
  4. 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 往往给优化器更大的优化空间。