跳至主要内容

PREWHERE 子句

Prewhere 是一种优化,可以更有效地应用过滤。即使未显式指定 `PREWHERE` 子句,它默认情况下也是启用的。它的工作原理是自动将部分 WHERE 条件移至预过滤阶段。`PREWHERE` 子句的作用只是在您认为自己比默认情况下更了解如何优化时控制此优化。

使用预过滤优化,首先只会读取执行预过滤表达式所需的列。然后,将读取运行查询其余部分所需的其它列,但仅读取预过滤表达式至少对某些行 `true` 的块。如果许多块的预过滤表达式对所有行都为 `false`,并且预过滤需要的列少于查询其它部分,这通常可以减少从磁盘读取大量数据以执行查询。

手动控制预过滤

该子句与 `WHERE` 子句的含义相同。区别在于从表中读取哪些数据。当手动控制 `PREWHERE` 用于查询中少数列使用的过滤条件,但这些条件提供了强有力的数据过滤时。这将减少要读取的数据量。

查询可以同时指定 `PREWHERE` 和 `WHERE`。在这种情况下,`PREWHERE` 在 `WHERE` 之前执行。

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

如果查询具有 FINAL 修饰符,则 `PREWHERE` 优化并不总是正确的。仅当 optimize_move_to_prewhereoptimize_move_to_prewhere_if_final 两个设置都启用时,才会启用它。

注意

`PREWHERE` 部分在 `FINAL` 之前执行,因此当使用 `PREWHERE` 与不在表 `ORDER BY` 部分中的字段时,`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.)

-- let's enable tracing to see which predicate are moved to PREWHERE
set send_logs_level='debug';

MergeTreeWhereOptimizer: condition "B = 0" moved to PREWHERE
-- Clickhouse moves automatically `B = 0` to PREWHERE, but it has no sense because B is always 0.

-- Let's move other predicate `C = 'x'`

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.)

-- This query with manual `PREWHERE` processes slightly less data: 158.89 MB VS 168.89 MB