首页/数据/NoSQL 速查/PREWHERE 提前过滤
ClickHouse56 条

PREWHERE 提前过滤

PREWHERE 先只读取过滤条件涉及的列,筛掉大部分数据后再读其他列,显著减少 IO;优化器对部分简单条件会自动上移,也可以手动指定。

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

PREWHERE 是怎么省 IO 的

PREWHERE 先只读取过滤条件涉及的列,把不满足条件的行筛掉之后,再读查询需要的其他列。宽表上这个顺序很关键:过滤列往往只占少数几列,先筛掉大部分数据再读宽列,磁盘读取量能下降一个量级。

自动搬运与手动指定

优化器默认会尝试把适合的条件从 WHERE 搬到 PREWHERE(由 optimize_move_to_prewhere 控制),但自动判断有边界,复杂查询可以显式写出 PREWHERE 固定执行顺序。限制是它只能引用表中的列,不能引用 SELECT 里定义的别名或聚合结果,写了会直接报错。

基本信息

写法SELECT ... PREWHERE 条件 WHERE 条件
优化开关optimize_move_to_prewhere 默认开启
硬限制只能引用表列,不能引用 SELECT 别名或聚合结果
验证方式EXPLAIN 查看过滤是否前置
适用版本ClickHouse 各主流版本均支持

示例

示例
SELECT event_time, user_id, props
FROM events
PREWHERE event_type = 'click' AND event_date = today()
WHERE user_id > 0
LIMIT 100;

-- 看执行计划,确认过滤是否前置
EXPLAIN indexes = 1
SELECT count() FROM events PREWHERE event_type = 'click';

-- 对照实验:关闭自动搬运,比较两次读取行数
SET optimize_move_to_prewhere = 0;

典型使用场景

  • 大宽表过滤:先用高选择性条件把数据缩小,再读宽列
  • 慢查询优化:把最省列、过滤效果最好的条件前置
  • 验证效果:用 EXPLAIN 确认过滤发生在读取阶段
  • 对照实验:切换自动搬运开关,比较读取行数与耗时
  • 配合分区裁剪:把分区键相关条件放进 PREWHERE 一起生效

写法与读取行为

写法读取行为效果
高选择性条件放 PREWHERE只读该条件的列并先行筛行IO 下降最明显
低效条件留在 WHERE读取较宽数据后再过滤开销较大
PREWHERE 引用别名直接报错语句需要重写
自动搬运开启优化器自行选择条件多数场景可用,复杂查询建议显式写
决定收益的是条件本身的选择性:先裁掉大比例数据再读宽列,IO 才会明显下降;条件写在哪个关键字里只是手段,不是目的。
把低效条件放进 PREWHERE、把高选择性条件留在 WHERE,反而会增加读取量;显式使用时要按选择性排布,而不是按书写习惯。

易错点

  • 把宽列相关的过滤写在 WHERE、低效条件写在 PREWHERE,反而增加读取量
  • PREWHERE 里引用 SELECT 别名会直接报错,语句需要重写
  • 对列数很少或数据量很小的表加 PREWHERE 没有收益,只是增加书写成本
  • 关闭自动搬运做实验后忘记改回来,会影响后续所有查询的优化行为

数据来源与参考

  • ClickHouse Docs: PREWHERE clause
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌