跳到主要内容
跳到主要内容

PREWHERE 子句

PREWHERE 是一种用于更高效执行过滤的优化机制。即使未显式指定 PREWHERE 子句,该优化也会默认启用。其工作方式是自动将部分 WHERE 条件移动到 PREWHERE 阶段。PREWHERE 子句的作用只是用于在你认为自己比默认策略更了解如何进行优化时,手动控制这一优化行为。

启用 PREWHERE 优化后,首先只读取执行 PREWHERE 表达式所需的列。之后,再读取执行查询其余部分所需的其他列,但仅限于那些在至少某些行上 PREWHERE 表达式为 true 的数据块。如果存在大量数据块在所有行上 PREWHERE 表达式均为 false,并且 PREWHERE 所需的列比查询其他部分所需的列更少,那么在执行查询时通常可以显著减少从磁盘读取的数据量。

手动控制 PREWHERE

该子句与 WHERE 子句具有相同的作用。区别在于它决定从表中读取哪些数据。对于在查询中仅被少数列使用、但能够提供强过滤效果的过滤条件,可以手动将其放入 PREWHERE 中进行控制,从而减少需要读取的数据量。

查询中可以同时指定 PREWHEREWHERE。在这种情况下,PREWHERE 先于 WHERE 执行。

如果将 optimize_move_to_prewhere 设置为 0,则会禁用将表达式的一部分从 WHERE 自动移动到 PREWHERE 的启发式算法。

如果查询带有 FINAL 修饰符,则 PREWHERE 优化并不总是正确。只有在同时启用了 optimize_move_to_prewhereoptimize_move_to_prewhere_if_final 两个设置时,才会启用该优化。

注意

PREWHERE 部分在 FINAL 之前执行,因此,当在不属于表 ORDER BY 部分的字段上使用 PREWHERE 时,FROM ... FINAL 查询的结果可能会产生偏差。

限制

PREWHERE 仅支持由 *MergeTree 系列表引擎创建的表。

示例

CREATE TABLE mydata
(
    `A` Int64,
    `B` Int8,
    `C` String
)
ENGINE = MergeTree
ORDER BY A AS
SELECT
    number,
    0,
    if(number between 1000 and 2000, 'x', toString(number))
FROM numbers(10000000);

SELECT count()
FROM mydata
WHERE (B = 0) AND (C = 'x');

1 row in set. Elapsed: 0.074 sec. Processed 10.00 million rows, 168.89 MB (134.98 million rows/s., 2.28 GB/s.)

-- 启用跟踪以查看哪些谓词被移至 PREWHERE
set send_logs_level='debug';

MergeTreeWhereOptimizer: condition "B = 0" moved to PREWHERE  
-- ClickHouse 自动将 `B = 0` 移至 PREWHERE,但这没有意义,因为 B 始终为 0。

-- 将另一个谓词 `C = 'x'` 移至 PREWHERE 

SELECT count()
FROM mydata
PREWHERE C = 'x'
WHERE B = 0;

1 row in set. Elapsed: 0.069 sec. Processed 10.00 million rows, 158.89 MB (144.90 million rows/s., 2.30 GB/s.)

-- 手动指定 `PREWHERE` 的查询处理的数据量略少:158.89 MB 对比 168.89 MB