sql where子句常见陷阱:and优先级高于or,混用时必须加括号;null值须用is null/is not null判断;like前导%导致索引失效;函数运算如date()使索引失效,应改用范围查询。

WHERE 子句里多个条件连写,AND 和 OR 优先级搞错导致结果不对
SQL 中 AND 优先级高于 OR,不加括号时,WHERE a = 1 OR b = 2 AND c = 3 实际等价于 WHERE a = 1 OR (b = 2 AND c = 3),不是你直觉想表达的“(a=1 或 b=2)且 c=3”。线上查出漏数据或误删,十次有八次栽在这儿。
- 只要混用
AND和OR,无条件加括号——哪怕只有一处不确定 - 想表达“状态是 draft 或 pending,且创建时间在本周”,必须写成:
WHERE (status = 'draft' OR status = 'pending') AND created_at >= '2024-06-10' - 用
IN替代一长串OR更安全:比如status IN ('draft', 'pending', 'reviewing'),既清晰又避开了优先级陷阱
NULL 值在 WHERE 条件里永远不匹配 = 或 !=,得用 IS NULL / IS NOT NULL
写 WHERE deleted_at != NULL 或 WHERE name = NULL,结果永远为空——因为 SQL 里任何值与 NULL 做比较都返回 UNKNOWN,而 WHERE 只接受 TRUE 的行。
-
IS NULL和IS NOT NULL是唯一正确判断空值的方式 - 想查“没删除且名字不为空”,不能写
WHERE deleted_at != NULL AND name != NULL,得写:WHERE deleted_at IS NULL AND name IS NOT NULL - 如果字段允许 NULL,又常用于筛选,建表时考虑默认值(如
status VARCHAR(20) DEFAULT 'active'),能少踩一半坑
LIKE 模糊匹配时通配符位置影响索引是否生效
LIKE 后面跟 '%abc'(前导百分号)基本等于放弃索引;而 'abc%' 可以走 B-tree 索引前缀匹配,性能差一个数量级很常见。
- 查“以 admin 开头的用户名”:用
WHERE username LIKE 'admin%',能走索引 - 查“包含 admin 的用户名”:用
WHERE username LIKE '%admin%',除非加全文索引或用fulltext,否则就是全表扫 - 大小写敏感问题别硬扛:MySQL 默认不区分,PostgreSQL 区分;要稳定行为,显式用
ILIKE(PostgreSQL)或LOWER(username) LIKE LOWER('%admin%')
WHERE 中对字段做函数运算(如 DATE(created_at))会让索引失效
写 WHERE DATE(created_at) = '2024-06-10',哪怕 created_at 有索引,数据库也大概率放弃使用——因为函数改变了原始值,无法直接比对索引 B-tree 节点。
- 改用范围查询:把
DATE(created_at) = '2024-06-10'拆成WHERE created_at >= '2024-06-10' AND created_at - 类似地,
WHERE YEAR(created_at) = 2024改成WHERE created_at >= '2024-01-01' AND created_at - 真要高频按天查,可加生成列 + 索引(MySQL 5.7+):
date_only DATE AS (DATE(created_at)) STORED,再给date_only加索引
WHERE 子句看着简单,但每个字符都在和索引、执行计划、NULL 语义较劲。最危险的不是不会写,而是写了还自以为对——尤其当测试数据没 NULL、没边界值、量又小的时候。










