メインコンテンツへスキップ
メインコンテンツへスキップ

PREWHERE 句

PREWHERE は、フィルタリングをより効率的に適用するための最適化です。PREWHERE 句が明示的に指定されていない場合でも、デフォルトで有効になっています。これは、WHERE 条件の一部を自動的に PREWHERE ステージへ移動することで機能します。PREWHERE 句の役割は、このデフォルトの最適化よりも適切に制御できると考える場合に、その挙動を明示的にコントロールすることだけです。

PREWHERE 最適化では、まず PREWHERE 式を評価するために必要な列だけが読み込まれます。その後、クエリの残りの部分を実行するために必要な他の列が読み込まれますが、これは PREWHERE 式が少なくとも一部の行で true となるブロックに対してのみ行われます。すべての行に対して PREWHERE 式が false となるブロックが多数存在し、かつ PREWHERE がクエリの他の部分より少ない列しか必要としない場合には、クエリ実行時にディスクから読み取るデータ量を大幅に削減できることがよくあります。

PREWHERE を手動で制御する

この句は WHERE 句と同じ意味を持ちます。違いは、テーブルからどのデータが読み込まれるかという点です。クエリ内の列のうち一部の列でしか使われないものの、強力なデータフィルタリングを提供する条件について PREWHERE を手動で制御すると、読み取るデータ量を削減できます。

クエリで PREWHEREWHERE を同時に指定することができます。この場合、PREWHEREWHERE に先行して実行されます。

optimize_move_to_prewhere 設定が 0 に設定されている場合、WHERE から PREWHERE へ式の一部を自動的に移動するためのヒューリスティックは無効化されます。

クエリに FINAL 修飾子がある場合、PREWHERE による最適化が常に正しくなるとは限りません。これは、optimize_move_to_prewhereoptimize_move_to_prewhere_if_final の両方の設定がオンになっている場合にのみ有効化されます。

注記

PREWHERE セクションは FINAL より前に実行されるため、テーブルの ORDER BY セクションに含まれないフィールドと併用して PREWHERE を使うと、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.)

-- トレースを有効にして、どの述語がPREWHEREに移動されるかを確認しましょう
set send_logs_level='debug';

MergeTreeWhereOptimizer: condition "B = 0" moved to PREWHERE  
-- ClickHouseは自動的に `B = 0` をPREWHEREに移動しますが、Bは常に0であるため意味がありません。

-- 他の述語 `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.)

-- 手動で `PREWHERE` を指定したこのクエリは、わずかに少ないデータを処理します: 158.89 MB 対 168.89 MB