
WHERE 子句里 AND 和 OR 混用,结果不对
MySQL 的 AND 优先级高于 OR,不加括号时容易误判逻辑。比如写 WHERE status = 'active' OR type = 'vip' AND score > 100,实际等价于 WHERE status = 'active' OR (type = 'vip' AND score > 100),而不是你直觉想表达的「活跃用户或 VIP 且分数大于 100」。
实操建议:
- 所有涉及
AND和OR混合的条件,一律用括号明确分组,哪怕看起来“没必要” - 把语义相近的条件先归组:比如
(status = 'active' OR status = 'pending')再和其它条件组合 - 用
EXPLAIN看执行计划,确认 MySQL 确实按你预期的方式解析了 WHERE 条件
NULL 值导致 WHERE 条件失效
WHERE column = NULL 或 WHERE column != 'abc' 都不会匹配到 NULL 行——因为任何与 NULL 的比较结果都是 UNKNOWN,不是 TRUE,所以被过滤掉。
实操建议:
- 判断 NULL 必须用
IS NULL或IS NOT NULL,不能用=或!= - 需要同时处理非 NULL 值和 NULL 值时,显式写出两种分支:比如
WHERE (score > 80) OR score IS NULL - 如果业务上 NULL 和某个默认值(如 0)等价,考虑在查询前用
COALESCE(score, 0)统一转换,但注意这可能影响索引使用
字符串比较时忽略大小写或尾部空格
MySQL 默认使用 utf8mb4_0900_as_cs 这类排序规则时是区分大小写的,但很多老库用的是 utf8mb4_general_ci,它既不区分大小写,又会忽略末尾空格。所以 WHERE name = 'Alice ' 可能意外命中 'Alice'。
实操建议:
- 查当前字段排序规则:运行
SHOW FULL COLUMNS FROM table_name LIKE 'column_name',看Collation列 - 要严格匹配(含大小写+空格),改用二进制比较:
WHERE BINARY name = 'Alice ' - 线上表不建议临时改排序规则,容易引发隐式转换和索引失效;建表时就定好,比如用
utf8mb4_0900_as_cs
IN 列表太长导致性能骤降或报错
当 WHERE id IN (1,2,3,...,2000) 这种写法超过几千项,不仅慢,还可能触发 max_allowed_packet 限制,直接报错 Packets larger than max_allowed_packet are not allowed。
实操建议:
- 单次
IN列表别超 500 项;超过就拆成多个查询,或改用临时表 +JOIN - 避免拼接超长字符串传参,后端应做分批处理,前端不要一股脑塞几千 ID 进 SQL
- 如果数据来自另一张表,优先写成
WHERE id IN (SELECT id FROM other_table WHERE ...),让优化器决定走 semi-join 还是物化;但注意子查询不能有外部引用,否则变相关子查询,极慢
WHERE 子句看着简单,但每个符号、每个空格、每个 NULL 都可能悄悄改变结果。最常出问题的不是语法错,而是你以为它在做什么,其实它在做另一件事。










