Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

PREWHERE

PREWHERE を使用すると、読み取るデータ量を削減し、フィルタリングを効率化できます。デフォルトでは、クエリで PREWHERE を明示的に指定していなくても、ClickHouse は対象となる条件を WHERE から PREWHERE に移動して、この最適化を適用します。この段階で適用する条件を制御するには、PREWHERE を明示的に指定します。

PREWHERE では、ClickHouse はまず条件の評価に必要なカラムだけを読み取ります。次に、一致する行を少なくとも 1 行含むブロックに対してのみ、クエリで必要な他のカラムを読み取ります。条件で使用するカラム数がクエリの他の部分より少なく、多くのブロックを除外できる場合、読み取るデータ量を削減できます。

PREWHERE を手動で制御する

条件が少数のカラムのみを参照し、多くの行を除外する場合は、PREWHERE を手動で指定します。これにより、残りのカラムから読み取るデータ量を削減できます。

クエリには PREWHEREWHERE の両方を含めることができます。この場合、PREWHERE が先に評価されます。

ClickHouse が条件を WHERE から PREWHERE に自動的に移動しないようにするには、optimize_move_to_prewhere0 に設定します。

FINAL 修飾子を使用するクエリでは、optimize_move_to_prewhereoptimize_move_to_prewhere_if_final の両方が有効な場合にのみ、ClickHouse は条件を WHERE から PREWHERE に移動します。

JOIN を使用する PREWHERE

JOIN を含むクエリでは、PREWHERE 条件が直接参照できるカラムは最大で1つのテーブルに限られます。ClickHouse は、結合前にそのテーブルの行に条件を適用します。

一方、WHERE 条件は論理上、結合結果をフィルタリングします。ただし、結果が変わらない場合、オプティマイザによって結合前に適用されることがあります。そのため、同じ条件を PREWHEREWHERE で使用すると、特に外部結合では結果が異なる場合があります。

次の例では、この違いを示すために2つのテーブルを作成します。

CREATE TABLE table_1
(
    `id` UInt32,
    `value` String
)
ENGINE = MergeTree
ORDER BY id;

CREATE TABLE table_2
(
    `id` UInt32,
    `value` String
)
ENGINE = MergeTree
ORDER BY id;

INSERT INTO table_1 VALUES (1, 'a'), (2, 'b'), (3, 'c');
INSERT INTO table_2 VALUES (1, 'x'), (2, 'y'), (3, 'z');

最初のクエリでは、PREWHERE により LEFT JOIN の前に table_2 がフィルタリングされるため、table_1id = 1 の行は結合先がないまま残ります:

SELECT
    table_1.id,
    table_1.value,
    table_2.value
FROM table_1
LEFT JOIN table_2 ON table_1.id = table_2.id
PREWHERE table_2.id >= 2
ORDER BY table_1.id;
   ┌─id─┬─value─┬─table_2.value─┐
1. │  1 │ a     │               │
2. │  2 │ b     │ y             │
3. │  3 │ c     │ z             │
   └────┴───────┴───────────────┘

WHERE で同じ条件を使用すると、結合結果がフィルタリングされ、id = 1 の行が除外されます。

SELECT
    table_1.id,
    table_1.value,
    table_2.value
FROM table_1
LEFT JOIN table_2 ON table_1.id = table_2.id
WHERE table_2.id >= 2
ORDER BY table_1.id;
   ┌─id─┬─value─┬─table_2.value─┐
1. │  2 │ b     │ y             │
2. │  3 │ c     │ z             │
   └────┴───────┴───────────────┘

制限事項

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

-- Enable tracing to see which predicates are moved to PREWHERE.
set send_logs_level='debug';

MergeTreeWhereOptimizer: condition "B = 0" moved to PREWHERE
-- ClickHouse automatically moves B = 0 to PREWHERE, but this condition does not filter any rows because B is always 0.

-- Move the more selective C = 'x' predicate to 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.)

-- The query with manually specified PREWHERE processes slightly less data: 158.89 MB instead of 168.89 MB.
Navigation