Passer au contenu principal
Prewhere est une optimisation qui permet d’appliquer le filtrage plus efficacement. Elle est activée par défaut, même si la clause PREWHERE n’est pas explicitement spécifiée. Elle fonctionne en déplaçant automatiquement une partie de la condition WHERE vers l’étape prewhere. Le rôle de la clause PREWHERE est uniquement de contrôler cette optimisation si vous pensez savoir mieux l’appliquer que le comportement par défaut. Avec l’optimisation prewhere, seules les colonnes nécessaires à l’exécution de l’expression prewhere sont d’abord lues. Ensuite, les autres colonnes nécessaires à l’exécution du reste de la requête sont lues, mais uniquement dans les blocs où l’expression prewhere vaut true pour au moins certaines lignes. S’il existe de nombreux blocs où l’expression prewhere vaut false pour toutes les lignes, et si prewhere nécessite moins de colonnes que les autres parties de la requête, cela permet souvent de lire beaucoup moins de données depuis le disque pour exécuter la requête.

Contrôle manuel de PREWHERE

Cette clause a la même signification que la clause WHERE. La différence réside dans les données lues depuis la table. Le contrôle manuel de PREWHERE est utile pour les conditions de filtrage utilisées par une minorité des colonnes de la requête, mais qui offrent un fort pouvoir de filtration des données. Cela réduit le volume de données à lire. Une requête peut spécifier simultanément PREWHERE et WHERE. Dans ce cas, PREWHERE est appliqué avant WHERE. Si le paramètre optimize_move_to_prewhere est défini sur 0, les heuristiques qui déplacent automatiquement des parties d’expressions de WHERE vers PREWHERE sont désactivées. Si la requête utilise le modificateur FINAL, l’optimisation PREWHERE n’est pas toujours correcte. Elle n’est activée que si les deux paramètres optimize_move_to_prewhere et optimize_move_to_prewhere_if_final sont activés.
La section PREWHERE est exécutée avant FINAL ; les résultats des requêtes FROM ... FINAL peuvent donc être faussés lors de l’utilisation de PREWHERE avec des champs qui ne figurent pas dans la section ORDER BY d’une table.

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

-- 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
Dernière modification le 25 juin 2026