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_prewhere 和 optimize_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