跳至主要内容
跳至主要内容

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 之前执行,因此在使用 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
    © . This site is unofficial and not affiliated with ClickHouse, Inc.