首页/数据/SQL 速查/WHERE 条件过滤

WHERE 条件过滤

在分组与排序之前过滤行:WHERE 只保留条件为 TRUE 的行;结合 IS NULL、范围与集合条件可以精确圈定数据。

SQL 速查数据更新于 2026-10-13

WHERE 是什么

WHERE 在分组与排序之前过滤行,只保留条件求值为 TRUE 的行;条件为 FALSE 或 UNKNOWN 的行都会被丢弃。它是减少后续处理数据量最有效的手段,也是索引能否被用上的关键位置。理解三值逻辑是写对 WHERE 的前提:status <> 1 不会返回 status 为 NULL 的行,因为 NULL 参与任何比较都得到 UNKNOWN。把行级条件放在 WHERE、组级条件放在 HAVING,是这两层过滤的基本分工。

基本信息

求值时机FROM 之后、GROUP BY 之前
保留规则只看 TRUE,FALSE 与 UNKNOWN 都丢弃
可用内容列、常量、函数、子查询;不能用列别名与聚合函数
索引友好对列做裸比较(列 运算符 常量)最利于走索引
与 HAVING 分工WHERE 过滤行,HAVING 过滤分组结果
适用平台标准 SQL,MySQL 8.0 与 PostgreSQL 行为一致

典型使用场景

  • 缩小结果集:只查有效状态、指定时间范围内的数据
  • 降低扫描量:把过滤条件推到连接语句的驱动表一侧
  • 配合索引:等值与范围条件命中索引,避免全表扫描
  • 聚合前预过滤:只统计已支付订单的金额
  • 更新与删除前预检:用同一条件先跑 SELECT COUNT(*) 估算影响行数
  • 数据质量排查:找出缺字段、越界值、可疑重复的记录

示例

示例
SELECT id, name, status, created_at
FROM users
WHERE status = 1                        # 等值条件,最利于走索引
  AND city IN ('Beijing','Shanghai')     # 集合条件
  AND created_at >= '2024-01-01'          # 半开区间下端
  AND created_at <  '2025-01-01'          # 半开区间上端,避免漏掉最后一天
  AND vip IS NOT NULL;                    # 判空必须用 IS NULL,不能写 = NULL

条件写法与陷阱

写法等价形式注意
a <> 1不含 NULL 行NULL <> 1 是 UNKNOWN,需 OR a IS NULL 才完整
NOT (a = 1)与 a <> 1 结果相同三值逻辑下取反不等于取补集
a IN (1,2)a = 1 OR a = 2列表很长时改用 JOIN 临时表或值列表
a NOT IN (1,2)含 NULL 时恒不成立子查询列可空时改用 NOT EXISTS
a BETWEEN 1 AND 5a >= 1 AND a <= 5时间列上界建议改用 < 次日
a LIKE 'x%'前缀匹配可走索引前后都加百分号会全表扫描

索引友好改写

索引友好改写
-- 劣:函数包住列,索引失效
SELECT * FROM orders WHERE DATE(created_at) = '2024-05-01';

-- 优:范围条件,可用索引
SELECT * FROM orders
WHERE created_at >= '2024-05-01'
  AND created_at <  '2024-05-02';

-- 劣:字符串列与数字比较,触发隐式转换
SELECT * FROM users WHERE phone = 13800138000;
-- 优:写成字符串
SELECT * FROM users WHERE phone = '13800138000';
WHERE 里不能写聚合函数,COUNT(*) > 5 这类条件必须放到 HAVING,否则直接语法报错。
更新与删除前把 WHERE 子句先跑一遍 SELECT COUNT(*),确认影响范围后再执行,能拦下绝大多数误操作。
OR 与 AND 混用时括号决定语义:a = 1 OR a = 2 AND b = 3 实际等价于 a = 1 OR (a = 2 AND b = 3),条件范围常与直觉不符。
把过滤条件尽量写在驱动表一侧,并对被驱动表的连接列建索引,这是连接查询提速最直接的两点。

索引使用要点

  • 等值条件放最左:联合索引 (a,b,c) 上 a = ? 能定位,只写 b = ? 就用不上这棵索引
  • 范围条件会截断后续列:某列用了 > 或 BETWEEN 之后,后面的列只能靠索引下推做过滤
  • 函数与运算会让列索引失效:把计算移到常量一侧,例如 created_at >= '2024-01-01'
  • 类型要匹配:字符串列传数字会触发隐式转换,索引可能失效
  • 模糊匹配只有前缀形式能走索引:LIKE 'abc%' 可以,LIKE '%abc' 不行
  • OR 两侧都要能走索引才会用索引,否则退化为全表扫描,可改写为 UNION ALL
  • IS NULL 能否走索引要看数据分布:NULL 占比很小时索引仍可能被优化器选中
写条件时先保证业务口径正确,再考虑索引:口径错了再快也没用;优化顺序是先核对语义,再用 EXPLAIN 看 type 与 key 决定要不要加索引。
同一个条件写在 WHERE 还是 HAVING 会直接影响代价:能写 WHERE 就不要写 HAVING,因为 HAVING 在聚合之后才过滤,参与聚合的数据量并没有被减少。
条件写得多不一定更慢:优化器会按选择率决定执行顺序,把区分度高的条件写在前面只是书写习惯,真正影响性能的是有没有可用的索引。

易错点

  • status <> 1 会漏掉 status 为 NULL 的行,需要 OR status IS NULL 才完整
  • WHERE 中对列使用函数(如 DATE(created_at) = '2024-01-01')通常导致该列索引失效
  • 把聚合条件写进 WHERE 会直接报错,聚合过滤必须用 HAVING
  • 字符串列与数字比较触发隐式类型转换,索引失效且可能匹配到意外结果
  • OR 混用 AND 不加括号,运算符优先级导致条件范围与预期不符

数据来源与参考

  • MySQL 8.0 Reference Manual · SELECT · WHERE
  • PostgreSQL Documentation · WHERE Clause
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌