in基本写法为where column in (val1, val2, …),字符串须加单引号、数值不加但需类型一致;常见错误包括漏括号导致语法错误、字符串未引号被误判为列名、null参与比较返回unknown、空列表in()报错、子查询返回多列或含null。

IN 查询的基本写法和常见错误
直接用 IN 就能查多个值,但很多人写成 WHERE id = (1, 2, 3) 或漏掉括号,结果报错 Operand should contain 1 column(s)。
正确写法必须带括号,且每个值独立列出:
SELECT * FROM users WHERE id IN (1, 2, 3);
-
IN后面必须是括号包裹的值列表,不能是子查询以外的表达式(如IN (SELECT id FROM temp)是合法的,但IN (id_list)这种变量名写法不生效) - 字符串值要用单引号,比如
name IN ('Alice', 'Bob');数字不用引号,混用会触发隐式转换,可能走不了索引 - 空括号
IN ()在 MySQL 中语法错误,PostgreSQL 允许但返回空结果,别这么写
NULL 值在 IN 查询中会被忽略
IN 判断对 NULL 永远返回 UNKNOWN,不是 TRUE 也不是 FALSE,所以哪怕你写了 status IN ('active', NULL),这条条件也永远不匹配任何行。
想查字段为 NULL 的记录,必须单独用 IS NULL:
SELECT * FROM orders WHERE status IN ('pending', 'shipped') OR status IS NULL;
- 不要试图用
IN (..., NULL)包含空值 - 如果业务逻辑里“空”和某个字符串(如
'unknown')等价,建议先清洗数据,统一转成可比较的值 - 使用
COALESCE(status, 'unknown')再进IN是可行的,但注意函数会导致索引失效
IN 列表过长时的性能与限制
MySQL 默认最大允许 IN 列表长度由 max_allowed_packet 和解析器限制共同决定,实际超过几千个值就容易出问题;PostgreSQL 对列表长度更宽容,但性能会明显下降。
- 500 个以内值通常安全;超过 1000 个建议改用临时表 +
JOIN或分批查询 - 参数化查询时(如 Python 的
cursor.execute("SELECT * FROM t WHERE id IN %s", ([1,2,3],))),注意驱动是否支持数组展开——psycopg2 支持,MySQLdb 不支持,得拼成(%s, %s, %s)占位符 - 如果值来自用户输入,务必校验数量,防止 DOS 式攻击(比如传入十万 ID)
替代方案:什么时候不该用 IN
当要查的值来自另一张表、或需要关联计算时,IN 效率往往不如 JOIN,尤其在外层大表、内层小表场景下,MySQL 可能放弃使用索引。
例如查“所有有订单的用户”,写成:
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);
不如:
SELECT DISTINCT u.* FROM users u INNER JOIN orders o ON u.id = o.user_id;
-
NOT IN遇到右表含NULL会整个结果为空,这是陷阱,优先用NOT EXISTS - 如果只是做存在性判断(如“是否存在某 ID”),用
EXISTS通常更快,且语义更清晰 - 某些 ORM(如 Django ORM)生成的
__in查询,在大数据量下默认不优化,需手动控制批次或换用RawSQL
实际用的时候,先看值来源、再看数量、最后看有没有 NULL 或关联需求。三个因素叠在一起,IN 很快就不是最优解了。











