WHERE 是什么
WHERE 是行级过滤子句,在 FROM 完成取数与连接之后、分组之前对原始行做筛选,也是决定索引能不能被用上的关键位置。它最容易出错的地方是空值:NULL 表示未知,任何与 NULL 的比较结果都是未知而不是假,所以判空只能写 IS NULL、IS NOT NULL,NOT IN 的子查询里一旦出现 NULL,整条判断都会失去意义。理解这一点,大部分「条件看起来对却查不到数据」的问题就有答案了。
基本信息
| 用途 | 行级过滤,直接决定扫描范围 |
|---|---|
| 语法 | WHERE 条件 [AND|OR 条件] ... |
| 比较运算符 | = <> != < <= > >=、BETWEEN、IN、LIKE、IS NULL |
| 逻辑优先级 | NOT 高于 AND,AND 高于 OR,混用必须加括号 |
| 索引要点 | 条件要作用在索引列本身,不要套函数或做运算 |
| 执行位置 | FROM 之后、GROUP BY 之前,不能引用 SELECT 的别名 |
典型使用场景
- 列表接口按状态、时间范围、用户过滤,条件写法直接决定是否走索引
- 数据核查:按单号或用户定位记录,用等值条件精确命中
- 增量同步与归档:用时间范围圈出当天或某一批次的数据
- 排查查不到数据:确认是判空写错、隐式类型转换,还是条件本身不成立
- 模糊搜索按前缀匹配,把通配符放在字符串末尾才能用上索引
- 多条件组合筛选,注意 AND 与 OR 混用时的括号与优先级
条件示例
条件示例
SELECT id, status, amount FROM orders WHERE status = 'paid'; # 等值条件,走索引的首选
SELECT id FROM orders WHERE qty BETWEEN 1 AND 10 AND amount > 100; # 范围加比较,范围列尽量靠后
SELECT id FROM orders WHERE user_id IN (1001, 1002, 1003); # 集合条件,短列表等价多次等值
SELECT id FROM orders WHERE remark IS NULL; # 判空只能用 IS NULL,= NULL 永远查不到
SELECT id FROM orders WHERE sku LIKE 'A100%'; # 前缀模糊可用索引
SELECT id FROM orders WHERE status='paid' AND (channel='web' OR channel='app'); # 逻辑混用加括号运算符与索引影响
| 写法 | 含义 | 对索引的影响 |
|---|---|---|
| = | 等值匹配,最理想的条件 | 可直接定位索引记录 |
| BETWEEN a AND b | 闭区间范围 | 可用索引,其后的复合列不再失效但利用率下降 |
| IN (...) | 命中集合内任一值 | 短列表走索引,超长列表退化为扫描 |
| LIKE 'abc%' | 前缀匹配 | 可使用索引 |
| LIKE '%abc%' | 两端通配 | 无法用 B 树索引,需全文检索方案 |
| IS NULL / IS NOT NULL | 空值判断 | 与列的允许空性和统计信息有关 |
| != 或 NOT IN | 取反条件 | 优化器常放弃索引转为扫描 |
| 列做运算或套函数 | 条件不在索引列本身 | 索引失效 |
反例与改写
反例与改写
# 反例:函数包住索引列,索引失效
SELECT id FROM orders WHERE DATE(created_at) = '2026-10-12';
# 改写:改范围条件,索引可用
SELECT id FROM orders
WHERE created_at >= '2026-10-12' AND created_at < '2026-10-13';
# 反例:NOT IN 的子查询可能返回 NULL
SELECT id FROM orders WHERE user_id NOT IN (SELECT user_id FROM blacklist);
# 改写:用 NOT EXISTS 表达同样语义
SELECT o.id FROM orders o
WHERE NOT EXISTS (SELECT 1 FROM blacklist b WHERE b.user_id = o.user_id);LIKE 只有前缀匹配能利用 B 树索引;需要任意位置模糊匹配时应评估全文索引或外部检索方案,不要在业务表上直接用两端通配的写法硬扛。
OR 条件常让优化器放弃索引,改成 IN 或拆成 UNION ALL 往往能让索引重新生效;条件值的类型与列定义不一致触发隐式转换时,索引同样会失效。
删除或更新之前,用完全相同的 WHERE 条件先做一次 SELECT 计数:条件写错的后果会从查不到数据升级成误删误改,写操作几乎无法回退。
条件写法检查清单
- 等值条件优先于范围与模糊:等值可直接定位到索引位置,代价最低
- 复合索引上范围条件之后的列不再参与定位,把等值列放在范围列之前
- 判空条件与其他条件用 OR 连接时容易让索引失效,可拆成两条查询再合并结果
- 最具选择性的条件写在前面对可读性有帮助,但真正决定索引的是优化器与统计信息
- 上线前用 EXPLAIN 复核:确认访问类型与使用的索引和预期一致
- 条件里出现的列都写在同一张表上,避免在过滤条件中做跨表关联
索引列上的 IS NULL 也能使用索引,但空值比例高时优化器可能判断走全表更划算,这类查询要单独用 EXPLAIN 验证,而不是照搬等值条件的经验。