PREWHERE peut rendre le filtrage plus efficace en réduisant la quantité de données lues. Par défaut, ClickHouse applique cette optimisation, même lorsqu'une requête ne spécifie pas explicitement PREWHERE, en déplaçant les conditions admissibles de WHERE vers PREWHERE. Vous pouvez spécifier explicitement PREWHERE pour contrôler les conditions appliquées à cette étape.
Avec PREWHERE, ClickHouse lit d'abord uniquement les colonnes nécessaires à l'évaluation de la condition. Il lit ensuite les autres colonnes requises par la requête uniquement pour les blocs contenant au moins une ligne correspondante. Cela peut réduire la quantité de données lues lorsque la condition utilise moins de colonnes que le reste de la requête et élimine de nombreux blocs.
Contrôler manuellement PREWHERE
Spécifiez manuellement PREWHERE lorsqu'une condition porte sur un petit nombre de colonnes et élimine de nombreuses lignes. Cela peut réduire la quantité de données lues pour les colonnes restantes.
Une requête peut contenir à la fois PREWHERE et WHERE. Dans ce cas, PREWHERE est évalué en premier.
Définissez optimize_move_to_prewhere sur 0 pour empêcher ClickHouse de déplacer automatiquement les conditions de WHERE vers PREWHERE.
Pour les requêtes utilisant le modificateur FINAL, ClickHouse ne déplace les conditions de WHERE vers PREWHERE que lorsque optimize_move_to_prewhere et optimize_move_to_prewhere_if_final sont tous deux activés.
PREWHERE avec JOIN
Dans une requête contenant un JOIN, une condition PREWHERE ne peut référencer directement les colonnes que d'une seule table au plus. ClickHouse applique la condition aux lignes de cette table avant qu'elles ne soient jointes.
À l'inverse, une condition WHERE filtre logiquement le résultat de la jointure, bien que l'optimiseur puisse l'appliquer avant la jointure lorsque cela ne modifie pas le résultat. Utiliser la même condition dans PREWHERE et WHERE peut donc produire des résultats différents, en particulier avec les jointures externes.
L'exemple suivant crée deux tables pour illustrer cette différence :
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');Dans la première requête, PREWHERE filtre table_2 avant le LEFT JOIN, de sorte que la ligne de table_1 avec id = 1 reste sans correspondance :
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 │
└────┴───────┴───────────────┘L’utilisation de la même condition dans WHERE filtre le résultat de la jointure et supprime la ligne avec 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 │
└────┴───────┴───────────────┘Limitations
PREWHERE n’est pris en charge que par les tables de la famille *MergeTree.
Exemple
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.