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

PREWHERE 子句

Prewhere 是一种优化,可以更有效地应用过滤。即使未显式指定 `PREWHERE` 子句,默认情况下也会启用它。它的工作原理是将 WHERE 条件的一部分自动移动到 prewhere 阶段。`PREWHERE` 子句的作用仅在于,如果您认为您知道如何比默认情况更好地控制此优化。

通过 prewhere 优化,首先只读取执行 prewhere 表达式所需的列。然后读取运行查询其余部分所需的其他列,但仅读取 prewhere 表达式对某些行至少为 `true` 的块。如果有很多块的 prewhere 表达式对所有行都为 `false`,并且 prewhere 需要的列少于查询的其他部分,这通常可以从磁盘读取少得多的数据以执行查询。

手动控制 Prewhere

此子句与 `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` 之前执行,因此当在 `FROM ... FINAL` 查询中使用 `PREWHERE` 且字段不在表的 `ORDER BY` 部分时,结果可能会倾斜。

限制

`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