> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Les moteurs de table de la famille `MergeTree` sont conçus pour des taux d’ingestion élevés et de très gros volumes de données.

# Moteur de table MergeTree

export const CloudNotSupportedBadge = () => {
  return <div className="cloudNotSupportedBadge">
            <div className="cloudNotSupportedIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.5" d="M6.33366 12.6666L12.3739 12.6667C13.6593 12.6667 14.7073 11.6187 14.7073 10.3334C14.7073 9.04804 13.6593 8.00003 12.3739 8.00003C12.3739 8.00003 12.3337 7.66659 12.0003 7.33325M10.667 5.33322C8.00033 2.33325 4.45395 4.78537 4.14195 6.68203C2.55728 6.7627 1.29395 8.06203 1.29395 9.6667C1.29395 11.3234 2.66699 12.6666 4.00033 12.6666" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.5" d="M2.66699 14L12.0003 4.66663" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>

        </div>
            Non pris en charge par ClickHouse Cloud
        </div>;
};

export const ExperimentalBadge = () => {
  return <div className="experimentalBadge">
            <div className="experimentalIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.25" d="M5.5 2H10.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M9.50015 2V6.19625L13.4283 12.7425C13.4738 12.8183 13.4985 12.9049 13.4996 12.9934C13.5008 13.0818 13.4785 13.169 13.435 13.246C13.3914 13.323 13.3283 13.3871 13.2519 13.4317C13.1755 13.4764 13.0886 13.4999 13.0002 13.5H3.00015C2.91164 13.5 2.8247 13.4766 2.74822 13.432C2.67174 13.3874 2.60847 13.3233 2.56487 13.2463C2.52126 13.1693 2.49889 13.082 2.50004 12.9935C2.50119 12.905 2.52582 12.8184 2.5714 12.7425L6.50015 6.19625V2" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M4.47656 9.56754C5.30344 9.41254 6.47656 9.47942 7.99969 10.25C10.0153 11.2707 11.4216 11.0569 12.2184 10.7282" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            Fonctionnalité expérimentale. <u><a href="/docs/docs/beta-and-experimental-features#experimental-features">En savoir plus.</a></u>
        </div>;
};

Le moteur `MergeTree` et les autres moteurs de la famille `MergeTree` (par ex. `ReplacingMergeTree`, `AggregatingMergeTree`) sont les moteurs de table les plus couramment utilisés et les plus robustes dans ClickHouse.

Les moteurs de table de la famille `MergeTree` sont conçus pour des taux d’ingestion de données élevés et de très gros volumes de données.
Les opérations d’insertion créent des parties de table, qui sont fusionnées avec d’autres parties de table par un processus d’arrière-plan.

Principales fonctionnalités des moteurs de table de la famille `MergeTree`.

* La clé primaire de la table détermine l’ordre de tri au sein de chaque partie de table (index clusterisé). La clé primaire ne référence pas non plus des lignes individuelles, mais des blocs de 8192 lignes appelés granules. Cela rend les clés primaires de très gros jeux de données suffisamment petites pour rester chargées en mémoire vive, tout en offrant un accès rapide aux données sur disque.

* Les tables peuvent être partitionnées à l’aide d’une expression de partition arbitraire. L’élagage des partitions garantit que certaines partitions ne sont pas lues lorsque la requête le permet.

* Les données peuvent être répliquées sur plusieurs nœuds du cluster pour assurer la haute disponibilité, le failover et des mises à niveau sans interruption de service. Voir [Réplication des données](/docs/fr/reference/engines/table-engines/mergetree-family/replication).

* Les moteurs de table `MergeTree` prennent en charge différents types de statistiques et méthodes d’échantillonnage pour faciliter l’optimisation des requêtes.

<Note>
  Malgré un nom similaire, le moteur [Merge](/docs/fr/reference/engines/table-engines/special/merge) est différent des moteurs `*MergeTree`.
</Note>

<div id="table_engine-mergetree-creating-a-table">
  ## Créer des tables
</div>

```sql theme={null}
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr1] [COMMENT ...] [CODEC(codec1)] [STATISTICS(stat1)] [TTL expr1] [PRIMARY KEY] [SETTINGS (name = value, ...)],
    name2 [type2] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr2] [COMMENT ...] [CODEC(codec2)] [STATISTICS(stat2)] [TTL expr2] [PRIMARY KEY] [SETTINGS (name = value, ...)],
    ...
    INDEX index_name1 expr1 TYPE type1(...) [GRANULARITY value1],
    INDEX index_name2 expr2 TYPE type2(...) [GRANULARITY value2],
    ...
    PROJECTION projection_name_1 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]),
    PROJECTION projection_name_2 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY])
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr
    [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' [, ...] ]
    [WHERE conditions]
    [GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]
[SETTINGS name = value, ...]
```

Pour une description détaillée des paramètres, consultez l’instruction [CREATE TABLE](/docs/fr/reference/statements/create/table)

<div id="mergetree-query-clauses">
  ### Clauses de requête
</div>

<div id="engine">
  #### ENGINE
</div>

`ENGINE` — Nom et paramètres du moteur. `ENGINE = MergeTree()`. Le moteur `MergeTree` n’a pas de paramètres.

<div id="order_by">
  #### ORDER BY
</div>

`ORDER BY` — La clé de tri.

Un tuple de noms de colonnes ou d'expressions arbitraires. Exemple : `ORDER BY (CounterID + 1, EventDate)`.

Si aucune clé primaire n'est définie (c.-à-d. si `PRIMARY KEY` n'a pas été spécifiée), ClickHouse utilise la clé de tri comme clé primaire.

Si aucun tri n'est nécessaire, vous pouvez utiliser la syntaxe `ORDER BY tuple()`.
Autrement, si le paramètre `create_table_empty_primary_key_by_default` est activé, `ORDER BY ()` est ajouté implicitement aux instructions `CREATE TABLE`. Voir [Sélection d'une clé primaire](#selecting-a-primary-key).

<div id="partition-by">
  #### PARTITION BY
</div>

`PARTITION BY` — La [clé de partitionnement](/docs/fr/reference/engines/table-engines/mergetree-family/custom-partitioning-key). Facultatif. Dans la plupart des cas, vous n'avez pas besoin de clé de partitionnement, et si vous devez partitionner, il n'est généralement pas nécessaire d'utiliser une clé de partitionnement plus fine qu'un partitionnement mensuel. Le partitionnement n'accélère pas les requêtes (contrairement à l'expression ORDER BY). Vous ne devez jamais utiliser un partitionnement trop fin. Ne partitionnez pas vos données par identifiant ou nom de client (utilisez plutôt l'identifiant ou le nom du client comme première colonne de l'expression ORDER BY).

Pour un partitionnement par mois, utilisez l'expression `toYYYYMM(date_column)`, où `date_column` est une colonne de type [Date](/docs/fr/reference/data-types/date). Les noms de partition ont ici le format `"YYYYMM"`.

<div id="primary-key">
  #### CLÉ PRIMAIRE
</div>

`PRIMARY KEY` — La clé primaire, si elle [diffère de la clé de tri](#choosing-a-primary-key-that-differs-from-the-sorting-key). Facultatif.

La définition d'une clé de tri (à l'aide de la clause `ORDER BY`) définit implicitement une clé primaire.
Il n'est généralement pas nécessaire de spécifier la clé primaire en plus de la clé de tri.

<div id="sample-by">
  #### SAMPLE BY
</div>

`SAMPLE BY` — Une expression d’échantillonnage. Optionnel.

Si elle est spécifiée, elle doit faire partie de la clé primaire.
L’expression d’échantillonnage doit renvoyer un entier non signé.

Exemple : `SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID))`.

<div id="ttl">
  #### TTL
</div>

`TTL` — Une liste de règles qui spécifient la durée de conservation des lignes et la logique de déplacement automatique des parts [entre les disques et les volumes](#table_engine-mergetree-multiple-volumes). Facultatif.

L'expression doit produire un `Date` ou un `DateTime`, par exemple `TTL date + INTERVAL 1 DAY`.

Le type de règle `DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'|GROUP BY` spécifie l'action à effectuer sur la part si l'expression est vérifiée (atteint l'heure actuelle) : suppression des lignes expirées, déplacement d'une part (si l'expression est vérifiée pour toutes les lignes d'une part) vers le disque spécifié (`TO DISK 'xxx'`) ou vers le volume (`TO VOLUME 'xxx'`), ou agrégation des valeurs des lignes expirées. Le type de règle par défaut est la suppression (`DELETE`). Il est possible de spécifier plusieurs règles, mais il ne doit pas y avoir plus d'une règle `DELETE`.

Pour plus de détails, voir [TTL pour les colonnes et les tables](#table_engine-mergetree-ttl)

<div id="settings">
  #### PARAMÈTRES
</div>

Voir [Paramètres de MergeTree](/docs/fr/reference/settings/merge-tree-settings).

**Exemple de paramètre Sections**

```sql theme={null}
ENGINE MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity=8192
```

Dans l'exemple, nous définissons un partitionnement par mois.

Nous définissons également une expression d'échantillonnage sous la forme d'un hash de l'ID utilisateur. Cela permet de répartir pseudo-aléatoirement les données de la table pour chaque `CounterID` et `EventDate`. Si vous définissez une clause [SAMPLE](/docs/fr/reference/statements/select/sample) lors de la sélection des données, ClickHouse renverra un échantillon de données pseudorandom uniforme pour un sous-ensemble d'utilisateurs.

Le paramètre `index_granularity` peut être omis, car 8192 est la valeur par défaut.

<details markdown="1">
  <summary>Méthode obsolète de création d'une table</summary>

  <Note>
    N'utilisez pas cette méthode dans de nouveaux projets. Si possible, migrez les anciens projets vers la méthode décrite ci-dessus.
  </Note>

  ```sql theme={null}
  CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
  (
      name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
      name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
      ...
  ) ENGINE [=] MergeTree(date-column [, sampling_expression], (primary, key), index_granularity)
  ```

  **Paramètres de MergeTree()**

  * `date-column` — Le nom d'une colonne de type [Date](/docs/fr/reference/data-types/date). ClickHouse crée automatiquement des partitions mensuelles à partir de cette colonne. Les noms des partitions sont au format `"YYYYMM"`.
  * `sampling_expression` — Une expression d'échantillonnage.
  * `(primary, key)` — Clé primaire. Type : [Tuple()](/docs/fr/reference/data-types/tuple)
  * `index_granularity` — La granularité d'un index. Le nombre de lignes de données entre les "marks" d'un index. La valeur 8192 convient à la plupart des tâches.

  **Exemple**

  ```sql theme={null}
  MergeTree(EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID)), 8192)
  ```

  Le moteur `MergeTree` est configuré de la même manière que dans l'exemple ci-dessus pour la méthode principale de configuration du moteur.
</details>

<div id="mergetree-data-storage">
  ## Stockage des données
</div>

Une table se compose de parties de données triées par clé primaire.

Lorsque des données sont insérées dans une table, des parties de données distinctes sont créées, et chacune d'elles est triée lexicographiquement par clé primaire. Par exemple, si la clé primaire est `(CounterID, Date)`, les données de la partie sont triées par `CounterID` et, pour chaque `CounterID`, elles sont ordonnées par `Date`.

Les données appartenant à des partitions différentes sont séparées dans des parties distinctes. En arrière-plan, ClickHouse fusionne les parties de données pour optimiser le stockage. Les parties appartenant à des partitions différentes ne sont pas fusionnées. Le mécanisme de fusion ne garantit pas que toutes les lignes ayant la même clé primaire se trouveront dans une seule et même partie de données.

Les parties de données peuvent être stockées au format `Wide` ou `Compact`. Au format `Wide`, chaque colonne est stockée dans un fichier distinct du système de fichiers, tandis qu'au format `Compact`, toutes les colonnes sont stockées dans un seul fichier. Le format `Compact` peut être utilisé pour améliorer les performances des insertions petites et fréquentes.

Le format de stockage des données est contrôlé par les paramètres `min_bytes_for_wide_part` et `min_rows_for_wide_part` du moteur de table. Si le nombre d'octets ou de lignes dans une partie de données est inférieur à la valeur du paramètre correspondant, la partie est stockée au format `Compact`. Sinon, elle est stockée au format `Wide`. Si aucun de ces paramètres n'est défini, les parties de données sont stockées au format `Wide`.

Chaque partie de données est logiquement divisée en granules. Un granule est le plus petit ensemble de données indivisible que ClickHouse lit lors de la sélection de données. ClickHouse ne divise ni les lignes ni les valeurs, de sorte que chaque granule contient toujours un nombre entier de lignes. La première ligne d'un granule est marquée par la valeur de la clé primaire de cette ligne. Pour chaque partie de données, ClickHouse crée un fichier d'index qui stocke les marques. Pour chaque colonne, qu'elle fasse partie de la clé primaire ou non, ClickHouse stocke également les mêmes marques. Ces marques permettent de localiser directement les données dans les fichiers de colonnes.

La taille du granule est limitée par les paramètres `index_granularity` et `index_granularity_bytes` du moteur de table. Le nombre de lignes dans un granule se situe dans l'intervalle `[1, index_granularity]`, selon la taille des lignes. La taille d'un granule peut dépasser `index_granularity_bytes` si la taille d'une seule ligne est supérieure à la valeur du paramètre. Dans ce cas, la taille du granule est égale à celle de la ligne.

<div id="primary-keys-and-indexes-in-queries">
  ## Clés primaires et index dans les requêtes
</div>

Prenons la clé primaire `(CounterID, Date)` comme exemple. Dans ce cas, l’ordre de tri et l’index se présentent comme suit :

```text theme={null}
Whole data:     [---------------------------------------------]
CounterID:      [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll]
Date:           [1111111222222233331233211111222222333211111112122222223111112223311122333]
Marks:           |      |      |      |      |      |      |      |      |      |      |
                a,1    a,2    a,3    b,3    e,2    e,3    g,1    h,2    i,1    i,3    l,3
Marks numbers:   0      1      2      3      4      5      6      7      8      9      10
```

Si la requête de données spécifie :

* `CounterID in ('a', 'h')`, le serveur lit les données dans les plages de marques `[0, 3)` et `[6, 8)`.
* `CounterID IN ('a', 'h') AND Date = 3`, le serveur lit les données dans les plages de marques `[1, 3)` et `[7, 8)`.
* `Date = 3`, le serveur lit les données dans la plage de marques `[1, 10]`.

Les exemples ci-dessus montrent qu'il est toujours plus efficace d'utiliser un index plutôt qu'un parcours complet.

Un index épars permet de lire des données supplémentaires. Lors de la lecture d'une seule plage de la clé primaire, jusqu'à `index_granularity * 2` lignes supplémentaires dans chaque bloc de données peuvent être lues.

Les index épars permettent de travailler avec un très grand nombre de lignes de table, car dans la plupart des cas, ces index tiennent dans la RAM de l’ordinateur.

ClickHouse n'exige pas de clé primaire unique. Vous pouvez insérer plusieurs lignes avec la même clé primaire.

Vous pouvez utiliser des expressions de type `Nullable` dans les clauses `PRIMARY KEY` et `ORDER BY`, mais cela est fortement déconseillé. Pour autoriser cette fonctionnalité, activez le paramètre [allow\_nullable\_key](/docs/fr/reference/settings/merge-tree-settings#allow_nullable_key). Le principe [NULLS\_LAST](/docs/fr/reference/statements/select/order-by#sorting-of-special-values) s'applique aux valeurs `NULL` dans la clause `ORDER BY`.

<div id="selecting-a-primary-key">
  ### Sélection d’une clé primaire
</div>

Le nombre de colonnes dans la clé primaire n’est pas explicitement limité. Selon la structure des données, vous pouvez inclure plus ou moins de colonnes dans la clé primaire. Cela peut :

* Améliorer les performances d’un index.

  Si la clé primaire est `(a, b)`, l’ajout d’une autre colonne `c` améliorera les performances si les conditions suivantes sont réunies :

  * Il existe des requêtes avec une condition sur la colonne `c`.
  * De longues plages de données (plusieurs fois plus longues que `index_granularity`) avec des valeurs identiques pour `(a, b)` sont fréquentes. En d’autres termes, l’ajout d’une colonne supplémentaire permet de sauter des plages de données assez longues.

* Améliorer la compression.

  ClickHouse trie les données par clé primaire ; ainsi, plus la cohérence est élevée, meilleure est la compression.

* Fournir une logique supplémentaire lors de la fusion des parties de données dans les moteurs [CollapsingMergeTree](/docs/fr/reference/engines/table-engines/mergetree-family/collapsingmergetree) et [SummingMergeTree](/docs/fr/reference/engines/table-engines/mergetree-family/summingmergetree).

  Dans ce cas, il est judicieux de spécifier une *clé de tri* différente de la clé primaire.

Une clé primaire longue aura un impact négatif sur les performances d’insertion et la consommation de mémoire, mais les colonnes supplémentaires dans la clé primaire n’affectent pas les performances de ClickHouse lors des requêtes `SELECT`.

Vous pouvez créer une table sans clé primaire en utilisant la syntaxe `ORDER BY tuple()`. Dans ce cas, ClickHouse stocke les données dans l’ordre d’insertion. Si vous souhaitez conserver l’ordre des données lors de l’insertion via des requêtes `INSERT ... SELECT`, définissez [max\_insert\_threads = 1](/docs/fr/reference/settings/session-settings#max_insert_threads).

Pour sélectionner les données dans leur ordre initial, utilisez des requêtes `SELECT` [mono-thread](/docs/fr/reference/settings/session-settings#max_threads).

<div id="choosing-a-primary-key-that-differs-from-the-sorting-key">
  ### Choisir une clé primaire différente de la clé de tri
</div>

Il est possible de définir une clé primaire (une expression dont les valeurs sont écrites dans le fichier d'index pour chaque marqueur) différente de la clé de tri (une expression utilisée pour trier les lignes dans les parties de données). Dans ce cas, le tuple d'expressions de la clé primaire doit être un préfixe du tuple d'expressions de la clé de tri.

Cette fonctionnalité est utile avec les moteurs de table [SummingMergeTree](/docs/fr/reference/engines/table-engines/mergetree-family/summingmergetree) et
[AggregatingMergeTree](/docs/fr/reference/engines/table-engines/mergetree-family/aggregatingmergetree). Dans le cas d'usage le plus courant de ces moteurs, la table comporte deux types de colonnes : les *dimensions* et les *mesures*. Les requêtes typiques agrègent les valeurs des colonnes de mesure avec des `GROUP BY` arbitraires et un filtrage sur les dimensions. Comme SummingMergeTree et AggregatingMergeTree agrègent les lignes ayant la même valeur de clé de tri, il est naturel d'y inclure toutes les dimensions. Par conséquent, l'expression de clé se compose d'une longue liste de colonnes, et cette liste doit être mise à jour fréquemment à mesure que de nouvelles dimensions sont ajoutées.

Dans ce cas, il est judicieux de ne conserver dans la clé primaire que quelques colonnes permettant des lectures par plage efficaces, et d'ajouter les autres colonnes de dimension au tuple de la clé de tri.

La modification de la clé de tri avec [ALTER](/docs/fr/reference/statements/alter/index) est une opération légère, car lorsqu'une nouvelle colonne est ajoutée simultanément à la table et à la clé de tri, les parties de données existantes n'ont pas besoin d'être modifiées. Comme l'ancienne clé de tri est un préfixe de la nouvelle et qu'il n'y a pas de données dans la colonne nouvellement ajoutée, les données sont triées à la fois selon l'ancienne et la nouvelle clé de tri au moment de la modification de la table.

<div id="use-of-indexes-and-partitions-in-queries">
  ### Utilisation des index et des partitions dans les requêtes
</div>

Pour les requêtes `SELECT`, ClickHouse analyse si un index peut être utilisé. Un index peut être utilisé si la clause `WHERE/PREWHERE` contient une expression (comme l’un des termes de la conjonction, ou dans son intégralité) correspondant à une opération de comparaison d’égalité ou d’inégalité, ou si elle contient `IN` ou `LIKE` avec un préfixe fixe sur des colonnes ou des expressions faisant partie de la clé primaire ou de la clé de partitionnement, ou sur certaines fonctions partiellement répétitives de ces colonnes, ou encore sur des combinaisons logiques de ces expressions.

Il est ainsi possible d’exécuter rapidement des requêtes sur un ou plusieurs intervalles de la clé primaire. Dans cet exemple, les requêtes seront rapides lorsqu’elles portent sur un tag de suivi spécifique, sur un tag spécifique et une plage de dates, sur un tag spécifique et une date, sur plusieurs tags avec une plage de dates, etc.

Examinons le moteur configuré comme suit :

```sql theme={null}
ENGINE MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate)
SETTINGS index_granularity=8192
```

Dans ce cas, pour les requêtes :

```sql theme={null}
SELECT count() FROM table
WHERE EventDate = toDate(now())
AND CounterID = 34

SELECT count() FROM table
WHERE EventDate = toDate(now())
AND (CounterID = 34 OR CounterID = 42)

SELECT count() FROM table
WHERE ((EventDate >= toDate('2014-01-01')
AND EventDate <= toDate('2014-01-31')) OR EventDate = toDate('2014-05-01'))
AND CounterID IN (101500, 731962, 160656)
AND (CounterID = 101500 OR EventDate != toDate('2014-05-01'))
```

ClickHouse utilisera l’index de clé primaire pour écarter les données non pertinentes, ainsi que la clé de partitionnement mensuelle pour exclure les partitions dont les dates se situent hors de l’intervalle recherché.

Les requêtes ci-dessus montrent que l’index est utilisé même pour des expressions complexes. La lecture de la table est organisée de telle sorte que l’utilisation de l’index ne peut pas être plus lente qu’un parcours complet.

Dans l’exemple ci-dessous, l’index ne peut pas être utilisé.

```sql theme={null}
SELECT count() FROM table WHERE CounterID = 34 OR URL LIKE '%upyachka%'
```

Pour vérifier si ClickHouse peut utiliser l’index lors de l’exécution d’une requête, utilisez les paramètres [force\_index\_by\_date](/docs/fr/reference/settings/session-settings#force_index_by_date) et [force\_primary\_key](/docs/fr/reference/settings/session-settings#force_primary_key).

La clé de partitionnement par mois permet de lire uniquement les blocs de données qui contiennent des dates dans la plage voulue. Dans ce cas, le bloc de données peut contenir des données correspondant à de nombreuses dates (jusqu’à un mois entier). Dans un bloc, les données sont triées par clé primaire, qui peut ne pas contenir la date en première colonne. C’est pourquoi une requête ne comportant qu’une condition sur la date, sans préciser le préfixe de la clé primaire, entraînera la lecture de plus de données que pour une date unique.

<div id="use-of-index-for-deterministic-expressions-in-primary-keys">
  ### Utilisation de l’index avec des expressions déterministes dans les clés primaires
</div>

La clé primaire peut contenir des expressions, et pas seulement des noms de colonnes. Ces expressions ne se limitent pas à de simples chaînes de fonctions : il peut s’agir d’arbres d’expressions arbitraires (par exemple, des fonctions imbriquées et des expressions composées), à condition qu’elles soient déterministes.

Une expression est **déterministe** si elle renvoie toujours le même résultat pour les mêmes valeurs d’entrée (par exemple : `length()`, `toDate()`, `lower()`, `left()`, `cityHash64()`, `toUUID()` ; contrairement à `now()` ou `rand()`). Si la clé primaire contient des expressions déterministes, ClickHouse peut les appliquer aux valeurs constantes de la requête et utiliser le résultat pour construire des conditions sur l’index de clé primaire. Cela permet d’ignorer des données pour des prédicats comme `=`, `IN` et `has`.

Un cas d’utilisation courant consiste à garder la clé primaire compacte (par exemple, stocker un hash au lieu d’une longue `String`), tout en permettant à des prédicats sur la colonne d’origine d’utiliser l’index.

Exemple de clé primaire déterministe (mais non injective) :

```sql theme={null}
ENGINE = MergeTree()
ORDER BY length(user_id)
```

Exemples de prédicats pouvant utiliser l’index :

```sql theme={null}
SELECT * FROM table WHERE user_id = 'alice';
SELECT * FROM table WHERE user_id IN ('alice', 'bob');
SELECT * FROM table WHERE has(['alice', 'bob'], user_id);
```

Dans ces cas, ClickHouse calcule `length('alice')` (et les autres constantes) une seule fois et utilise les valeurs de longueur pour restreindre les intervalles dans l’index de clé primaire. Comme la longueur d’une chaîne **n’est pas injective**, différentes chaînes `user_id` peuvent avoir la même longueur, si bien que l’index peut lire des granules supplémentaires (faux positifs). Le résultat reste correct, car le prédicat d’origine (`user_id = ...`, `IN`, etc.) est toujours appliqué après la lecture.

Si l’expression déterministe est aussi **injective** (des entrées différentes ne peuvent pas produire la même sortie pour les types d’argument utilisés), ClickHouse peut en outre utiliser efficacement l’index pour les formes niées : `!=`, `NOT IN` et `NOT has(...)`. Par exemple, `reverse(p)` et `hex(p)` sont injectives pour `String`.

Exemple de clé primaire injective :

```sql theme={null}
ENGINE = MergeTree()
ORDER BY hex(p)
```

Des expressions injectives plus complexes sont elles aussi prises en charge, par exemple :

```sql theme={null}
ENGINE = MergeTree()
ORDER BY reverse(tuple(reverse(p), hex(p)))
```

Exemples de prédicats pouvant utiliser l’index :

```sql theme={null}
SELECT * FROM table WHERE p != 'abc';
SELECT * FROM table WHERE p NOT IN ('abc', '12345');
SELECT * FROM table WHERE NOT has(['abc', '12345'], p);
```

<div id="use-of-index-for-partially-monotonic-primary-keys">
  ### Utilisation de l’index pour les clés primaires partiellement monotones
</div>

Prenons, par exemple, les jours du mois. Ils forment une [suite monotone](https://en.wikipedia.org/wiki/Monotonic_function) sur un mois, mais ne le sont plus sur des périodes plus longues. Il s’agit d’une suite partiellement monotone. Si un utilisateur crée une table avec une clé primaire partiellement monotone, ClickHouse crée un index épars comme d’habitude. Lorsqu’un utilisateur sélectionne des données dans ce type de table, ClickHouse analyse les conditions de la requête. Si l’utilisateur veut obtenir des données entre deux marques de l’index et que ces deux marques se situent dans le même mois, ClickHouse peut utiliser l’index dans ce cas précis, car il peut calculer la distance entre les paramètres d’une requête et les marques de l’index.

ClickHouse ne peut pas utiliser l’index si les valeurs de la clé primaire dans la plage des paramètres de la requête ne représentent pas une suite monotone. Dans ce cas, ClickHouse effectue une analyse complète.

ClickHouse applique cette logique non seulement aux suites de jours du mois, mais aussi à toute clé primaire représentant une suite partiellement monotone.

<div id="table_engine-mergetree-data_skipping-indexes">
  ### Index de saut de données
</div>

La déclaration de l’index se trouve dans la section des colonnes de la requête `CREATE`.

```sql theme={null}
INDEX index_name expr TYPE type(...) [GRANULARITY granularity_value]
```

Pour les tables de la famille `*MergeTree`, des index de saut de données peuvent être spécifiés.

Ces index agrègent certaines informations sur l'expression spécifiée dans des blocs composés de `granularity_value` granules (la taille d'un granule est spécifiée à l'aide du paramètre `index_granularity` dans le moteur de table). Ces agrégats sont ensuite utilisés dans les requêtes `SELECT` afin de réduire la quantité de données à lire sur le disque en sautant de grands blocs de données pour lesquels la condition `where` ne peut pas être satisfaite.

La clause `GRANULARITY` peut être omise, la valeur par défaut de `granularity_value` est 1.

**Exemple**

```sql theme={null}
CREATE TABLE table_name
(
    u64 UInt64,
    i32 Int32,
    s String,
    ...
    INDEX idx1 u64 TYPE bloom_filter GRANULARITY 3,
    INDEX idx2 u64 * i32 TYPE minmax GRANULARITY 3,
    INDEX idx3 u64 * length(s) TYPE set(1000) GRANULARITY 4
) ENGINE = MergeTree()
...
```

Les index de l’exemple peuvent être utilisés par ClickHouse pour réduire la quantité de données à lire sur le disque dans les requêtes suivantes :

```sql theme={null}
SELECT count() FROM table WHERE u64 == 10;
SELECT count() FROM table WHERE u64 * i32 >= 1234
SELECT count() FROM table WHERE u64 * length(s) == 1234
```

Les index de saut de données peuvent également être créés sur des colonnes composites :

```sql theme={null}
-- on columns of type Map:
INDEX map_key_index mapKeys(map_column) TYPE bloom_filter
INDEX map_value_index mapValues(map_column) TYPE bloom_filter

-- on columns of type JSON:
INDEX json_paths_index JSONAllPaths(json_column) TYPE bloom_filter

-- on columns of type Tuple:
INDEX tuple_1_index tuple_column.1 TYPE bloom_filter
INDEX tuple_2_index tuple_column.2 TYPE bloom_filter

-- on columns of type Nested:
INDEX nested_1_index col.nested_col1 TYPE bloom_filter
INDEX nested_2_index col.nested_col2 TYPE bloom_filter
```

<div id="skip-index-types">
  ### Types de skip indexes
</div>

Le moteur de table `MergeTree` prend en charge les types de skip indexes suivants.
Pour en savoir plus sur l’utilisation des skip indexes pour optimiser les performances,
consultez ["Comprendre les index de saut de données de ClickHouse"](/docs/fr/concepts/features/performance/skip-indexes/skipping-indexes).

* index [`MinMax`](#minmax)
* index [`Set`](#set)
* index [`bloom_filter`](#bloom-filter)
* index [`ngrambf_v1`](#n-gram-bloom-filter) *(Obsolète)*
* index [`tokenbf_v1`](#token-bloom-filter) *(Obsolète)*
* index [`text`](#text)
* index [`vector_similarity`](#vector-similarity)

<div id="minmax">
  #### Index de saut MinMax
</div>

Pour chaque granule d’index, les valeurs minimale et maximale d’une expression sont stockées.
(Si l’expression est de type `tuple`, les valeurs minimale et maximale de chaque élément du tuple sont stockées.)

```text title="Syntax" theme={null}
minmax
```

<div id="set">
  #### Set
</div>

Pour chaque granule d’index, jusqu’à `max_rows` valeurs uniques de l’expression spécifiée sont stockées.
`max_rows = 0` signifie "stocker toutes les valeurs uniques".

```text title="Syntax" theme={null}
set(max_rows)
```

<div id="bloom-filter">
  #### Filtre de Bloom
</div>

Pour chaque granule d’index, un [filtre de Bloom](https://en.wikipedia.org/wiki/Bloom_filter) est stocké pour les colonnes spécifiées.

```text title="Syntax" theme={null}
bloom_filter([false_positive_rate])
```

Le paramètre `false_positive_rate` peut prendre une valeur comprise entre 0 et 1 (par défaut : `0.025`) et indique la probabilité de générer un résultat positif (ce qui augmente la quantité de données à lire).

Les types de données suivants sont pris en charge :

* `(U)Int*`
* `Float*`
* `Enum`
* `Date`
* `DateTime`
* `String`
* `FixedString`
* `Array`
* `LowCardinality`
* `Nullable`
* `UUID`
* `Map`

<Info>
  **Type de données Map : spécifier la création d’un index sur les clés ou les valeurs**

  Pour le type de données `Map`, le client peut indiquer si l’index doit être créé sur les clés ou sur les valeurs à l’aide des fonctions [`mapKeys`](/docs/fr/reference/functions/regular-functions/tuple-map-functions#mapKeys) ou [`mapValues`](/docs/fr/reference/functions/regular-functions/tuple-map-functions#mapValues).
</Info>

<Info>
  **Type de données JSON : indexation des chemins JSON**

  Pour le type de données [`JSON`](/docs/fr/reference/data-types/newjson), un index de type bloom filter peut être créé sur l’ensemble des chemins à l’aide de la fonction [`JSONAllPaths`](/docs/fr/reference/functions/regular-functions/json-functions#JSONAllPaths). Cela permet d’ignorer les granules dans lesquelles un chemin JSON interrogé est absent. Voir [les index de saut de données pour JSON](/docs/fr/reference/data-types/newjson#data-skipping-indexes-for-json) pour plus de détails.
</Info>

<div id="n-gram-bloom-filter">
  #### Filtre de Bloom par n-grammes *(Obsolète)*
</div>

<Note>
  À partir de ClickHouse 26.2, avec la disponibilité générale (GA) de l’index `text`, l’index `ngrambf_v1` n’est plus recommandé pour la recherche en texte intégral.

  Voir la page ["Recherche en texte intégral avec les index de texte"](/docs/fr/reference/engines/table-engines/mergetree-family/textindexes) pour plus de détails.
</Note>

Pour chaque granule d’index, un [filtre de Bloom](https://en.wikipedia.org/wiki/Bloom_filter) est stocké pour les [n-grammes](https://en.wikipedia.org/wiki/N-gram) des colonnes spécifiées.

```text title="Syntax" theme={null}
ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
```

| Paramètre                       | Description                                                                                                                               |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `n`                             | taille du ngram                                                                                                                           |
| `size_of_bloom_filter_in_bytes` | Taille du filtre de Bloom en octets. Vous pouvez utiliser ici une valeur élevée, par exemple `256` ou `512`, car elle se compresse bien). |
| `number_of_hash_functions`      | Nombre de fonctions de hachage utilisées dans le filtre de Bloom.                                                                         |
| `random_seed`                   | Graine utilisée par les fonctions de hachage du filtre de Bloom.                                                                          |

Cet index fonctionne uniquement avec les types de données suivants :

* [`String`](/docs/fr/reference/data-types/string)
* [`FixedString`](/docs/fr/reference/data-types/fixedstring)
* [`Map`](/docs/fr/reference/data-types/map)

Pour estimer les paramètres de `ngrambf_v1`, vous pouvez utiliser les [fonctions définies par l’utilisateur (UDFs)](/docs/fr/reference/statements/create/function).

```sql title="UDFs for ngrambf_v1" theme={null}
CREATE FUNCTION bfEstimateFunctions [ON CLUSTER cluster]
AS
(total_number_of_all_grams, size_of_bloom_filter_in_bits) -> round((size_of_bloom_filter_in_bits / total_number_of_all_grams) * log(2));

CREATE FUNCTION bfEstimateBmSize [ON CLUSTER cluster]
AS
(total_number_of_all_grams, probability_of_false_positives) -> ceil((total_number_of_all_grams * log(probability_of_false_positives)) / log(1 / pow(2, log(2))));

CREATE FUNCTION bfEstimateFalsePositive [ON CLUSTER cluster]
AS
(total_number_of_all_grams, number_of_hash_functions, size_of_bloom_filter_in_bytes) -> pow(1 - exp(-number_of_hash_functions/ (size_of_bloom_filter_in_bytes / total_number_of_all_grams)), number_of_hash_functions);

CREATE FUNCTION bfEstimateGramNumber [ON CLUSTER cluster]
AS
(number_of_hash_functions, probability_of_false_positives, size_of_bloom_filter_in_bytes) -> ceil(size_of_bloom_filter_in_bytes / (-number_of_hash_functions / log(1 - exp(log(probability_of_false_positives) / number_of_hash_functions))))
```

Pour utiliser ces fonctions, vous devez spécifier au moins deux paramètres :

* `total_number_of_all_grams`
* `probability_of_false_positives`

Par exemple, il y a `4300` ngrams dans le granule et vous souhaitez que les faux positifs restent inférieurs à `0.0001`.
Les autres paramètres peuvent ensuite être estimés en exécutant les requêtes suivantes :

```sql theme={null}
--- estimate number of bits in the filter
SELECT bfEstimateBmSize(4300, 0.0001) / 8 AS size_of_bloom_filter_in_bytes;

┌─size_of_bloom_filter_in_bytes─┐
│                         10304 │
└───────────────────────────────┘

--- estimate number of hash functions
SELECT bfEstimateFunctions(4300, bfEstimateBmSize(4300, 0.0001)) as number_of_hash_functions

┌─number_of_hash_functions─┐
│                       13 │
└──────────────────────────┘
```

Bien sûr, vous pouvez également utiliser ces fonctions pour estimer les paramètres d’autres conditions.
Les fonctions ci-dessus s’appuient sur le calculateur de filtre de Bloom disponible [ici](https://hur.st/bloomfilter).

<div id="token-bloom-filter">
  #### Filtre de Bloom de jetons
</div>

<Note>
  Avec la disponibilité générale (GA) de l’index `text` à partir de la version 26.2 de ClickHouse, l’index `tokenbf_v1` n’est plus recommandé pour la recherche en texte intégral.

  Voir la page ["Recherche en texte intégral avec des index de texte"](/docs/fr/reference/engines/table-engines/mergetree-family/textindexes) pour plus de détails.
</Note>

```text title="Syntax" theme={null}
tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
```

<div id="sparse-grams-bloom-filter">
  #### Filtre de Bloom de sparse grams
</div>

Le filtre de Bloom de sparse grams est similaire à `ngrambf_v1`, mais utilise des [tokens sparse grams](/docs/fr/reference/functions/regular-functions/string-functions#sparseGrams) au lieu de ngrams.

```text title="Syntax" theme={null}
sparse_grams(min_ngram_length, max_ngram_length, min_cutoff_length, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed)
```

<div id="text">
  ### Index de texte
</div>

Crée un index inversé sur des données textuelles tokenisées, permettant une recherche en texte intégral efficace et déterministe. Voir [ici](/docs/fr/reference/engines/table-engines/mergetree-family/textindexes) pour plus de détails.

<div id="vector-similarity">
  #### Similarité vectorielle
</div>

Prend en charge la recherche approximative des plus proches voisins ; voir [ici](/docs/fr/reference/engines/table-engines/mergetree-family/annindexes) pour plus de détails.

<div id="functions-support">
  ### Prise en charge des fonctions
</div>

Les conditions de la clause `WHERE` contiennent des appels à des fonctions qui s'appliquent aux colonnes. Si la colonne fait partie d'un index, ClickHouse essaie d'utiliser cet index lors de l'évaluation des fonctions. ClickHouse prend en charge différents sous-ensembles de fonctions pour l'utilisation des index.

Les index de type `set` peuvent être utilisés par toutes les fonctions. Les autres types d'index sont pris en charge comme suit :

| Fonction (opérateur) / index                                                                                                           | clé primaire | minmax | ngrambf\_v1 | tokenbf\_v1 | bloom\_filter | sparse\_grams | text |
| -------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------ | ----------- | ----------- | ------------- | ------------- | ---- |
| [equals (=, ==)](/docs/fr/reference/functions/regular-functions/comparison-functions#equals)                                                | ✔            | ✔      | ✔           | ✔           | ✔             | ✔             | ✔    |
| [notEquals(!=, \<>)](/docs/fr/reference/functions/regular-functions/comparison-functions#notEquals)                                         | ✔            | ✔      | ✔           | ✔           | ✔             | ✔             | ✗    |
| [like](/docs/fr/reference/functions/regular-functions/string-search-functions#like)                                                         | ✔            | ✔      | ✔           | ✔           | ✗             | ✔             | ✔    |
| [notLike](/docs/fr/reference/functions/regular-functions/string-search-functions#notLike)                                                   | ✔            | ✔      | ✔           | ✔           | ✗             | ✔             | ✗    |
| [match](/docs/fr/reference/functions/regular-functions/string-search-functions#match)                                                       | ✗            | ✗      | ✔           | ✔           | ✗             | ✔             | ✔    |
| [startsWith](/docs/fr/reference/functions/regular-functions/string-functions#startsWith)                                                    | ✔            | ✔      | ✔           | ✔           | ✗             | ✔             | ✔    |
| [endsWith](/docs/fr/reference/functions/regular-functions/string-functions#endsWith)                                                        | ✗            | ✗      | ✔           | ✔           | ✗             | ✔             | ✔    |
| [multiSearchAny](/docs/fr/reference/functions/regular-functions/string-search-functions#multiSearchAny)                                     | ✗            | ✗      | ✔           | ✗           | ✗             | ✗             | ✔    |
| [multiSearchAnyUTF8](/docs/fr/reference/functions/regular-functions/string-search-functions#multiSearchAnyUTF8)                             | ✗            | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [multiMatchAny](/docs/fr/reference/functions/regular-functions/string-search-functions#multiMatchAny)                                       | ✗            | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [in](/docs/fr/reference/functions/regular-functions/in-functions)                                                                           | ✔            | ✔      | ✔           | ✔           | ✔             | ✔             | ✔    |
| [notIn](/docs/fr/reference/functions/regular-functions/in-functions)                                                                        | ✔            | ✔      | ✔           | ✔           | ✔             | ✔             | ✗    |
| [less (`<`)](/docs/fr/reference/functions/regular-functions/comparison-functions#less)                                                      | ✔            | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [greater (`>`)](/docs/fr/reference/functions/regular-functions/comparison-functions#greater)                                                | ✔            | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [lessOrEquals (`<=`)](/docs/fr/reference/functions/regular-functions/comparison-functions#lessOrEquals)                                     | ✔            | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [greaterOrEquals (`>=`)](/docs/fr/reference/functions/regular-functions/comparison-functions#greaterOrEquals)                               | ✔            | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [empty](/docs/fr/reference/functions/regular-functions/array-functions#empty)                                                               | ✔            | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [notEmpty](/docs/fr/reference/functions/regular-functions/array-functions#notEmpty)                                                         | ✗            | ✔      | ✗           | ✗           | ✗             | ✔             | ✗    |
| [has](/docs/fr/reference/functions/regular-functions/array-functions#has)                                                                   | ✔            | ✔      | ✔           | ✔           | ✔             | ✔             | ✔    |
| [hasAny](/docs/fr/reference/functions/regular-functions/array-functions#hasAny)                                                             | ✗            | ✗      | ✔           | ✔           | ✔             | ✔             | ✗    |
| [hasAll](/docs/fr/reference/functions/regular-functions/array-functions#hasAll)                                                             | ✗            | ✗      | ✔           | ✔           | ✔             | ✔             | ✗    |
| [hasToken](/docs/fr/reference/functions/regular-functions/string-search-functions#hasToken)                                                 | ✗            | ✗      | ✗           | ✔           | ✗             | ✗             | ✔    |
| [hasTokenOrNull](/docs/fr/reference/functions/regular-functions/string-search-functions#hasTokenOrNull)                                     | ✗            | ✗      | ✗           | ✔           | ✗             | ✗             | ✔    |
| [hasTokenCaseInsensitive (`*`)](/docs/fr/reference/functions/regular-functions/string-search-functions#hasTokenCaseInsensitive)             | ✗            | ✗      | ✗           | ✔           | ✗             | ✗             | ✗    |
| [hasTokenCaseInsensitiveOrNull (`*`)](/docs/fr/reference/functions/regular-functions/string-search-functions#hasTokenCaseInsensitiveOrNull) | ✗            | ✗      | ✗           | ✔           | ✗             | ✗             | ✗    |
| [hasAnyTokens](/docs/fr/reference/functions/regular-functions/string-search-functions#hasAnyTokens)                                         | ✗            | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [hasAllTokens](/docs/fr/reference/functions/regular-functions/string-search-functions#hasAllTokens)                                         | ✗            | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [pointInPolygon](/docs/fr/reference/functions/regular-functions/geo/coordinates#pointinpolygon)                                             | ✔            | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [mapContains (mapContainsKey)](/docs/fr/reference/functions/regular-functions/tuple-map-functions#mapContainsKey)                           | ✗            | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [mapContainsKeyLike](/docs/fr/reference/functions/regular-functions/tuple-map-functions#mapContainsKeyLike)                                 | ✗            | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [mapContainsValue](/docs/fr/reference/functions/regular-functions/tuple-map-functions#mapContainsValue)                                     | ✗            | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [mapContainsValueLike](/docs/fr/reference/functions/regular-functions/tuple-map-functions#mapContainsValueLike)                             | ✗            | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |

Les fonctions avec un argument constant inférieur à la taille du ngram ne peuvent pas être utilisées par `ngrambf_v1` pour l’optimisation des requêtes.

(\*) Pour que `hasTokenCaseInsensitive` et `hasTokenCaseInsensitiveOrNull` soient efficaces, l’index `tokenbf_v1` doit être créé sur des données converties en minuscules, par exemple `INDEX idx (lower(str_col)) TYPE tokenbf_v1(512, 3, 0)`.

<Note>
  Les filtres de Bloom peuvent produire des faux positifs. Les index `ngrambf_v1`, `tokenbf_v1`, `sparse_grams` et `bloom_filter` ne peuvent donc pas être utilisés pour optimiser les requêtes lorsque le résultat attendu d’une fonction est faux.

  Par exemple :

  * Peut être optimisé :
    * `s LIKE '%test%'`
    * `NOT s NOT LIKE '%test%'`
    * `s = 1`
    * `NOT s != 1`
    * `startsWith(s, 'test')`
  * Ne peut pas être optimisé :
    * `NOT s LIKE '%test%'`
    * `s NOT LIKE '%test%'`
    * `NOT s = 1`
    * `s != 1`
    * `NOT startsWith(s, 'test')`
</Note>

<div id="projections">
  ## Projections
</div>

Les projections s'apparentent aux [vues matérialisées](/docs/fr/reference/statements/create/view), mais sont définies au niveau des parts. Elles garantissent la cohérence et sont utilisées automatiquement dans les requêtes.

<Note>
  Lorsque vous implémentez des projections, vous devez également prendre en compte le paramètre [force\_optimize\_projection](/docs/fr/reference/settings/session-settings#force_optimize_projection).
</Note>

Les projections ne sont pas prises en charge dans les instructions `SELECT` utilisant le modificateur [FINAL](/docs/fr/reference/statements/select/from#final-modifier).

<div id="projection-query">
  ### Requête de projection
</div>

C’est la requête de projection qui définit une projection. Elle sélectionne implicitement les données de la table parente.
**Syntaxe**

```sql theme={null}
SELECT <column list expr> [GROUP BY] <group keys expr> [ORDER BY] <expr>
```

Les projections peuvent être modifiées ou supprimées à l’aide de l’instruction [ALTER](/docs/fr/reference/statements/alter/projection).

<div id="projection-index">
  ### Index de projection
</div>

Les index de projection étendent le sous-système des projections en fournissant un moyen léger et explicite de définir des index au niveau de la projection.
Vu de l’extérieur, un index de projection reste une projection, mais avec une syntaxe simplifiée et une intention plus claire : il définit une expression dédiée au filtrage, plutôt qu’au service de données matérialisées.
En interne, un index de projection ne matérialise pas la table d’origine dans un ordre de lignes permuté, contrairement à une projection classique.
À la place, la permutation est stockée sous la forme d’une colonne numérique de permutation `_part_offset`, c.-à-d. `SELECT _part_offset ORDER BY <index_expr>`.

<div id="projection-index-syntax">
  #### Syntaxe
</div>

```sql theme={null}
PROJECTION <name> INDEX <index_expr> TYPE <index_type>
```

Exemple :

```sql theme={null}
CREATE TABLE example
(
    id UInt64,
    region String,
    user_id UInt32,
    PROJECTION region_proj INDEX region TYPE basic,
    PROJECTION uid_proj INDEX user_id TYPE basic
)
ENGINE = MergeTree
ORDER BY id;
```

<div id="projection-index-types">
  #### Types d’index
</div>

Actuellement pris en charge :

* **basic** : équivalent à un index MergeTree standard sur l’expression.

Le framework permettra d’ajouter d’autres types d’index à l’avenir.

<div id="projection-storage">
  ### Stockage des projections
</div>

Les projections sont stockées dans le répertoire de la part. C’est similaire à un index, mais avec un sous-répertoire qui stocke la part d’une table `MergeTree` anonyme. La table est déduite de la requête de définition de la projection. S’il y a une clause `GROUP BY`, le moteur de stockage sous-jacent devient [AggregatingMergeTree](/docs/fr/reference/engines/table-engines/mergetree-family/aggregatingmergetree), et toutes les fonctions d’agrégation sont converties en `AggregateFunction`. S’il y a une clause `ORDER BY`, la table `MergeTree` l’utilise comme expression de clé primaire. Lors du merge process, la projection part est fusionnée au moyen de la routine de fusion de son stockage. La checksum de la part de la table parent est combinée avec celle de la projection. Les autres tâches de maintenance sont similaires à celles des index de saut.

<div id="projection-query-analysis">
  ### Analyse de requête
</div>

1. Vérifiez si la projection peut être utilisée pour répondre à la requête donnée, c'est-à-dire si elle produit le même résultat qu'une requête sur la table de base.
2. Sélectionnez la meilleure correspondance possible, c'est-à-dire celle qui contient le moins de granules à lire.
3. Le pipeline de requête qui utilise des projections sera différent de celui qui utilise les parts d'origine. Si la projection est absente dans certaines parts, nous pouvons ajouter le pipeline pour la « projeter » à la volée.

<div id="concurrent-data-access">
  ## Accès concurrent aux données
</div>

Pour l'accès concurrent aux tables, nous utilisons le multiversionnement. Autrement dit, lorsqu'une table est lue et mise à jour simultanément, les données sont lues à partir d'un ensemble de parts tel qu'il existe au moment de la requête. Il n'y a pas de verrous de longue durée. Les insertions ne gênent pas les opérations de lecture.

La lecture d'une table est automatiquement parallélisée.

<div id="table_engine-mergetree-ttl">
  ## TTL pour les colonnes et les tables
</div>

Détermine la durée de vie des valeurs.

La clause `TTL` peut être définie pour l’ensemble de la table ainsi que pour chaque colonne individuellement. Un `TTL` au niveau de la table peut également spécifier la logique de déplacement automatique des données entre les disques et les volumes, ou de recompression des parts dont toutes les données ont expiré.

Les expressions doivent s’évaluer en type de données [Date](/docs/fr/reference/data-types/date), [Date32](/docs/fr/reference/data-types/date32), [DateTime](/docs/fr/reference/data-types/datetime) ou [DateTime64](/docs/fr/reference/data-types/datetime64).

<Tip>
  **Évitez les fonctions non déterministes dans les expressions TTL**

  TTL est évalué lors des merges en arrière-plan, et non au moment de l’insertion.
  Les fonctions comme `rand()`, `now()` ou `now64()` sont réévaluées à chaque merge, ce qui entraîne un comportement de suppression imprévisible.
  ClickHouse bloque les expressions sans aucune dépendance à une colonne, mais ne rejette pas actuellement les fonctions non déterministes combinées à une référence de colonne (par ex. `ts + rand()`). Les expressions TTL doivent reposer uniquement sur des valeurs déterministes dérivées des colonnes afin d’obtenir des résultats prévisibles.
</Tip>

**Syntaxe**

Définition de la durée de vie d’une colonne :

```sql theme={null}
TTL time_column
TTL time_column + interval
```

Pour définir `interval`, utilisez les opérateurs d’[intervalle de temps](/docs/fr/reference/operators/index#operators-for-working-with-dates-and-times), par exemple :

```sql theme={null}
TTL date_time + INTERVAL 1 MONTH
TTL date_time + INTERVAL 15 HOUR
```

<div id="mergetree-column-ttl">
  ### TTL de colonne
</div>

Lorsque les valeurs de la colonne expirent, ClickHouse les remplace par les valeurs par défaut du type de données de la colonne. Si toutes les valeurs de la colonne d'une part de données expirent, ClickHouse supprime cette colonne de la part de données dans le système de fichiers.

La clause `TTL` ne peut pas être utilisée pour les colonnes de clé.

**Exemples**

<div id="creating-a-table-with-ttl">
  #### Création d’une table avec `TTL` :
</div>

```sql theme={null}
CREATE TABLE tab
(
    d DateTime,
    a Int TTL d + INTERVAL 1 MONTH,
    b Int TTL d + INTERVAL 1 MONTH,
    c String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d;
```

<div id="adding-ttl-to-a-column-of-an-existing-table">
  #### Ajout d’un TTL à une colonne d’une table existante
</div>

```sql theme={null}
ALTER TABLE tab
    MODIFY COLUMN
    c String TTL d + INTERVAL 1 DAY;
```

<div id="altering-ttl-of-the-column">
  #### Modification du TTL d’une colonne
</div>

```sql theme={null}
ALTER TABLE tab
    MODIFY COLUMN
    c String TTL d + INTERVAL 1 MONTH;
```

<div id="mergetree-table-ttl">
  ### TTL de table
</div>

Une table peut comporter une expression de suppression des lignes expirées, ainsi que plusieurs expressions de déplacement automatique des parts entre [disques ou volumes](#table_engine-mergetree-multiple-volumes). Lorsque des lignes de la table expirent, ClickHouse supprime toutes les lignes correspondantes. Pour le déplacement ou la recompression des parts, toutes les lignes d'une part doivent satisfaire aux critères de l'expression `TTL`.

```sql theme={null}
TTL expr
    [DELETE|RECOMPRESS codec_name1|TO DISK 'xxx'|TO VOLUME 'xxx'][, DELETE|RECOMPRESS codec_name2|TO DISK 'aaa'|TO VOLUME 'bbb'] ...
    [WHERE conditions]
    [GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ]
```

Un type de règle TTL peut être spécifié après chaque expression TTL. Il détermine l'action à effectuer une fois que l'expression est satisfaite (qu'elle atteint l'heure actuelle) :

* `DELETE` - supprime les lignes expirées (action par défaut) ;
* `RECOMPRESS codec_name` - recompresse la part de données avec `codec_name` ;
* `TO DISK 'aaa'` - déplace la part vers le disque `aaa` ;
* `TO VOLUME 'bbb'` - déplace la part vers le disque `bbb` ;
* `GROUP BY` - agrège les lignes expirées.

L'action `DELETE` peut être utilisée avec la clause `WHERE` pour supprimer uniquement certaines lignes expirées en fonction d'une condition de filtrage :

```sql theme={null}
TTL time_column + INTERVAL 1 MONTH DELETE WHERE column = 'value'
```

L’expression `GROUP BY` doit être un préfixe de la clé primaire de la table.

Si une colonne ne fait pas partie de l’expression `GROUP BY` et n’est pas définie explicitement dans la clause `SET`, alors, dans la ligne de résultat, elle contient une valeur quelconque issue des lignes regroupées (comme si la fonction d’agrégation `any` lui était appliquée).

**Exemples**

<div id="creating-a-table-with-ttl">
  #### Création d’une table avec `TTL` :
</div>

```sql theme={null}
CREATE TABLE tab
(
    d DateTime,
    a Int
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d
TTL d + INTERVAL 1 MONTH DELETE,
    d + INTERVAL 1 WEEK TO VOLUME 'aaa',
    d + INTERVAL 2 WEEK TO DISK 'bbb';
```

<div id="altering-ttl-of-the-table">
  #### Modification du `TTL` de la table :
</div>

```sql theme={null}
ALTER TABLE tab
    MODIFY TTL d + INTERVAL 1 DAY;
```

Création d’une table dont les lignes expirent au bout d’un mois. Les lignes expirées dont la date tombe un lundi sont supprimées :

```sql theme={null}
CREATE TABLE table_with_where
(
    d DateTime,
    a Int
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d
TTL d + INTERVAL 1 MONTH DELETE WHERE toDayOfWeek(d) = 1;
```

<div id="creating-a-table-where-expired-rows-are-recompressed">
  #### Création d’une table avec recompression des lignes expirées :
</div>

```sql theme={null}
CREATE TABLE table_for_recompression
(
    d DateTime,
    key UInt64,
    value String
) ENGINE MergeTree()
ORDER BY tuple()
PARTITION BY key
TTL d + INTERVAL 1 MONTH RECOMPRESS CODEC(ZSTD(17)), d + INTERVAL 1 YEAR RECOMPRESS CODEC(LZ4HC(10))
SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0;
```

Création d’une table dans laquelle les lignes expirées sont agrégées. Dans les lignes de résultat, `x` contient la valeur maximale des lignes regroupées, `y` la valeur minimale, et `d` une valeur quelconque prise parmi les lignes regroupées.

```sql theme={null}
CREATE TABLE table_for_aggregation
(
    d DateTime,
    k1 Int,
    k2 Int,
    x Int,
    y Int
)
ENGINE = MergeTree
ORDER BY (k1, k2)
TTL d + INTERVAL 1 MONTH GROUP BY k1, k2 SET x = max(x), y = min(y);
```

<div id="mergetree-removing-expired-data">
  ### Suppression des données expirées
</div>

Les données dont le `TTL` a expiré sont supprimées lorsque ClickHouse fusionne des parts de données.

Lorsque ClickHouse détecte que des données ont expiré, il effectue une fusion non planifiée. Pour contrôler la fréquence de ces fusions, vous pouvez définir `merge_with_ttl_timeout`. Si cette valeur est trop faible, de nombreuses fusions non planifiées pourront être effectuées, ce qui risque de consommer beaucoup de ressources.

Si vous exécutez la requête `SELECT` entre deux fusions, il se peut que vous obteniez des données expirées. Pour l’éviter, utilisez la requête [OPTIMIZE](/docs/fr/reference/statements/optimize) avant `SELECT`.

**Voir aussi**

* paramètre [ttl\_only\_drop\_parts](/docs/fr/reference/settings/merge-tree-settings#ttl_only_drop_parts)

<div id="disk-types">
  ## Types de disque
</div>

En plus des périphériques de bloc locaux, ClickHouse prend en charge les types de stockage suivants :

* [`s3` pour S3 et MinIO](#table_engine-mergetree-s3)
* [`gcs` pour GCS](/docs/fr/integrations/connectors/data-sources/gcs#creating-a-disk)
* [`blob_storage_disk` pour Azure Blob Storage](/docs/fr/concepts/features/configuration/server-config/storing-data#azure-blob-storage)
* [`hdfs` pour HDFS](/docs/fr/reference/engines/table-engines/integrations/hdfs)
* [`web` pour un accès en lecture seule depuis le Web](/docs/fr/concepts/features/configuration/server-config/storing-data#web-storage)
* [`cache` pour la mise en cache locale](/docs/fr/concepts/features/configuration/server-config/storing-data#using-local-cache)
* [`s3_plain` pour les sauvegardes sur S3](/docs/fr/concepts/features/backup-restore/local-disk)
* [`s3_plain_rewritable` pour les tables immuables non répliquées sur S3](/docs/fr/concepts/features/configuration/server-config/storing-data#s3-plain-rewritable-storage)

<div id="table_engine-mergetree-multiple-volumes">
  ## Utiliser plusieurs périphériques bloc pour le stockage des données
</div>

<div id="introduction">
  ### Introduction
</div>

Les moteurs de table de la famille `MergeTree` peuvent stocker les données sur plusieurs périphériques de stockage par blocs. Cela peut par exemple être utile lorsque les données d’une table donnée se répartissent implicitement entre données « chaudes » et « froides ». Les données les plus récentes sont régulièrement consultées, mais ne nécessitent qu’un faible espace de stockage. À l’inverse, le volume important de données historiques est rarement sollicité. Si plusieurs disques sont disponibles, les données « chaudes » peuvent être placées sur des disques rapides (par exemple, des SSD NVMe ou en mémoire), tandis que les données « froides » peuvent être stockées sur des supports relativement lents (par exemple, des HDD).

Cela s’applique à tous les types de disques, y compris S3 et les autres disques de stockage objet. Par exemple, vous pouvez répartir les données entre plusieurs buckets S3 au sein d’un même volume, ou créer des politiques hiérarchisées qui déplacent les données des disques locaux vers S3. Voir [Using S3 disks with multiple volumes](#s3-multiple-volumes) pour plus de détails.

Une part de données est la plus petite unité déplaçable pour les moteurs de table `MergeTree`. Les données appartenant à une même part sont stockées sur un seul disque. Les parts de données peuvent être déplacées entre les disques en arrière-plan (selon les paramètres utilisateur), ainsi qu’au moyen des requêtes [ALTER](/docs/fr/reference/statements/alter/partition).

<div id="terms">
  ### Termes
</div>

* Disque — Périphérique de bloc monté sur le système de fichiers.
* Disque par défaut — Disque qui stocke le chemin spécifié dans le paramètre du serveur [path](/docs/fr/reference/settings/server-settings/settings#path).
* Volume — Ensemble ordonné de disques équivalents (semblable à [JBOD](https://en.wikipedia.org/wiki/Non-RAID_drive_architectures)).
* Politique de stockage — Ensemble de volumes et règles de déplacement des données entre eux.

Les noms attribués aux entités décrites figurent dans les tables système [system.storage\_policies](/docs/fr/reference/system-tables/storage_policies) et [system.disks](/docs/fr/reference/system-tables/disks). Pour appliquer à une table l'une des politiques de stockage configurées, utilisez le paramètre `storage_policy` des tables de la famille de moteurs `MergeTree`.

<div id="table_engine-mergetree-multiple-volumes_configure">
  ### Configuration
</div>

Les disques, les volumes et les politiques de stockage doivent être déclarés dans la balise `<storage_configuration>`, par exemple dans un fichier du répertoire `config.d`.

<Tip>
  Les disques peuvent également être déclarés dans la section `SETTINGS` d’une requête. Cela est utile
  pour une analyse ponctuelle afin d’attacher temporairement un disque hébergé, par exemple, à une URL.
  Consultez [stockage dynamique](/docs/fr/concepts/features/configuration/server-config/storing-data#dynamic-configuration) pour plus de détails.
</Tip>

Structure de la configuration :

```xml theme={null}
<storage_configuration>
    <disks>
        <disk_name_1> <!-- disk name -->
            <path>/mnt/fast_ssd/clickhouse/</path>
        </disk_name_1>
        <disk_name_2>
            <path>/mnt/hdd1/clickhouse/</path>
            <keep_free_space_bytes>10485760</keep_free_space_bytes>
        </disk_name_2>
        <disk_name_3>
            <path>/mnt/hdd2/clickhouse/</path>
            <keep_free_space_bytes>10485760</keep_free_space_bytes>
        </disk_name_3>

        ...
    </disks>

    ...
</storage_configuration>
```

Balises :

* `<disk_name_N>` — Nom du disque. Les noms doivent être différents pour tous les disques.
* `path` — chemin sous lequel le serveur stockera les données (dossiers `data` et `shadow`), doit se terminer par '/'.
* `keep_free_space_bytes` — quantité d’espace disque libre à réserver.

L’ordre de définition des disques n’a pas d’importance.

Balisage de configuration des politiques de stockage :

```xml theme={null}
<storage_configuration>
    ...
    <policies>
        <policy_name_1>
            <volumes>
                <volume_name_1>
                    <disk>disk_name_from_disks_configuration</disk>
                    <max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
                    <load_balancing>round_robin</load_balancing>
                </volume_name_1>
                <volume_name_2>
                    <!-- configuration -->
                </volume_name_2>
                <!-- more volumes -->
            </volumes>
            <move_factor>0.2</move_factor>
        </policy_name_1>
        <policy_name_2>
            <!-- configuration -->
        </policy_name_2>

        <!-- more policies -->
    </policies>
    ...
</storage_configuration>
```

Tags :

* `policy_name_N` — Nom de la politique. Les noms de politique doivent être uniques.
* `volume_name_N` — Nom du volume. Les noms de volume doivent être uniques.
* `disk` — un disque au sein d’un volume.
* `max_data_part_size_bytes` — la taille maximale d’une part pouvant être stockée sur l’un des disques du volume. Si la taille estimée d’une part fusionnée dépasse `max_data_part_size_bytes`, cette part sera écrite sur le volume suivant. En pratique, cette fonctionnalité permet de conserver les parts nouvelles ou de petite taille sur un volume rapide (SSD) et de les déplacer vers un volume lent (HDD) lorsqu’elles deviennent volumineuses. N’utilisez pas ce paramètre si votre politique ne comporte qu’un seul volume.
* `move_factor` — lorsque l’espace disponible devient inférieur à ce facteur, les données commencent automatiquement à être déplacées vers le volume suivant, s’il existe (par défaut, 0.1). ClickHouse trie les parts existantes par taille, de la plus grande à la plus petite (ordre décroissant), et sélectionne les parts dont la taille totale est suffisante pour satisfaire la condition `move_factor`. Si la taille totale de toutes les parts est insuffisante, toutes les parts seront déplacées.
* `perform_ttl_move_on_insert` — Désactive le TTL move lors de l’INSERT d’une part de données. Par défaut (si cette option est activée), si vous insérez une part de données déjà expirée selon la règle de TTL move, elle est immédiatement placée sur un volume/disque déclaré dans la règle de déplacement. Cela peut considérablement ralentir l’insert si le volume/disque de destination est lent (par ex. S3). Si cette option est désactivée, la part de données déjà expirée est écrite sur le volume par défaut, puis déplacée juste après vers le volume TTL.
* `load_balancing` - Politique d’équilibrage des disques, `round_robin` ou `least_used`.
* `least_used_ttl_ms` - Configure le timeout (en millisecondes) de mise à jour de l’espace disponible sur tous les disques (`0` - mise à jour systématique, `-1` - ne jamais mettre à jour, la valeur par défaut est `60000`). Notez que, si le disque ne peut être utilisé que par ClickHouse et n’est pas sujet à un redimensionnement/réduction à chaud du système de fichiers, vous pouvez utiliser `-1` ; dans tous les autres cas, cela n’est pas recommandé, car cela finira par entraîner une répartition incorrecte de l’espace.
* `prefer_not_to_merge` — Vous ne devriez pas utiliser ce paramètre. Il désactive la fusion des parts de données sur ce volume (ce qui est nuisible et entraîne une dégradation des performances). Lorsque ce paramètre est activé (ne le faites pas), la fusion des données sur ce volume n’est pas autorisée (ce qui est mauvais). Cela permet (mais vous n’en avez pas besoin) de contrôler (si vous voulez contrôler quelque chose, vous faites une erreur) la manière dont ClickHouse fonctionne avec des disques lents (mais ClickHouse sait mieux faire, donc n’utilisez pas ce paramètre).
* `volume_priority` — Définit la priorité (ordre) selon laquelle les volumes sont remplis. Une valeur plus faible signifie une priorité plus élevée. Les valeurs du paramètre doivent être des nombres naturels et couvrir collectivement l’intervalle de 1 à N (la priorité la plus faible étant attribuée à N), sans en sauter aucune.
  * Si *tous* les volumes sont marqués, ils sont priorisés dans l’ordre indiqué.
  * Si seulement *certains* volumes sont marqués, ceux qui ne le sont pas ont la priorité la plus faible et sont priorisés dans l’ordre où ils sont définis dans la config.
  * Si *aucun* volume n’est marqué, leur priorité est définie en fonction de l’ordre dans lequel ils sont déclarés dans la configuration.
  * Deux volumes ne peuvent pas avoir la même valeur de priorité.

Exemples de configuration :

```xml theme={null}
<storage_configuration>
    ...
    <policies>
        <hdd_in_order> <!-- policy name -->
            <volumes>
                <single> <!-- volume name -->
                    <disk>disk1</disk>
                    <disk>disk2</disk>
                </single>
            </volumes>
        </hdd_in_order>

        <moving_from_ssd_to_hdd>
            <volumes>
                <hot>
                    <disk>fast_ssd</disk>
                    <max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
                </hot>
                <cold>
                    <disk>disk1</disk>
                </cold>
            </volumes>
            <move_factor>0.2</move_factor>
        </moving_from_ssd_to_hdd>

        <small_jbod_with_external_no_merges>
            <volumes>
                <main>
                    <disk>jbod1</disk>
                </main>
                <external>
                    <disk>external</disk>
                </external>
            </volumes>
        </small_jbod_with_external_no_merges>
    </policies>
    ...
</storage_configuration>
```

Dans l’exemple donné, la policy `hdd_in_order` implémente l’approche [round-robin](https://en.wikipedia.org/wiki/Round-robin_scheduling). Ainsi, cette policy ne définit qu’un seul volume (`single`) ; les parties de données sont stockées sur tous ses disques selon un ordre circulaire. Une telle policy peut être très utile si plusieurs disques similaires sont montés sur le système, mais que RAID n’est pas configuré. Gardez à l’esprit que chaque disque pris individuellement n’est pas fiable et que vous pouvez compenser cela avec un facteur de réplication de 3 ou plus.

S’il existe différents types de disques sur le système, la policy `moving_from_ssd_to_hdd` peut être utilisée à la place. Le volume `hot` se compose d’un disque SSD (`fast_ssd`), et la taille maximale d’une part pouvant être stockée sur ce volume est de 1GB. Toutes les parts dont la taille est supérieure à 1GB seront stockées directement sur le volume `cold`, qui contient un disque HDD `disk1`.
De plus, une fois que le disque `fast_ssd` est rempli à plus de 80 %, les données seront transférées vers `disk1` par un processus d’arrière-plan.

L’ordre d’énumération des volumes au sein d’une politique de stockage est important si au moins l’un des volumes listés n’a pas de paramètre `volume_priority` explicite.
Une fois qu’un volume est trop rempli, les données sont déplacées vers le suivant. L’ordre d’énumération des disques est également important, car les données y sont stockées à tour de rôle.

Lors de la création d’une table, il est possible de lui appliquer l’une des stratégies de stockage configurées :

```sql theme={null}
CREATE TABLE table_with_non_default_policy (
    EventDate Date,
    OrderID UInt64,
    BannerID UInt64,
    SearchPhrase String
) ENGINE = MergeTree
ORDER BY (OrderID, BannerID)
PARTITION BY toYYYYMM(EventDate)
SETTINGS storage_policy = 'moving_from_ssd_to_hdd'
```

La politique de stockage `default` signifie qu’un seul volume est utilisé, lequel se compose d’un seul disque indiqué dans `<path>`.
Vous pouvez modifier la politique de stockage après la création de la table avec la requête \[ALTER TABLE ... MODIFY SETTING] ; la nouvelle politique doit inclure tous les anciens disques et volumes en conservant les mêmes noms.

Le nombre de threads qui effectuent les déplacements en arrière-plan des parties de données peut être modifié via le paramètre [background\_move\_pool\_size](/docs/fr/reference/settings/server-settings/settings#background_move_pool_size).

<div id="details">
  ### Détails
</div>

Dans le cas des tables `MergeTree`, les données sont écrites sur le disque de différentes manières :

* À la suite d’une insertion (requête `INSERT`).
* Pendant les fusions en arrière-plan et les [mutations](/docs/fr/reference/statements/alter/index#mutations).
* Lors d’un téléchargement depuis une autre réplique.
* À la suite du gel d’une partition [ALTER TABLE ... FREEZE PARTITION](/docs/fr/reference/statements/alter/partition#freeze-partition).

Dans tous ces cas, à l’exception des mutations et du gel de partition, une part est stockée sur un volume et un disque conformément à la politique de stockage définie :

1. Le premier volume (dans l’ordre de définition) qui dispose de suffisamment d’espace disque pour stocker une part (`unreserved_space > current_part_size`) et autorise le stockage de parts d’une taille donnée (`max_data_part_size_bytes > current_part_size`) est choisi.
2. Au sein de ce volume, on choisit le disque qui suit celui utilisé pour stocker le fragment de données précédent et qui dispose d’un espace libre supérieur à la taille de la part (`unreserved_space - keep_free_space_bytes > current_part_size`).

En interne, les mutations et le gel de partition utilisent des [liens physiques](https://en.wikipedia.org/wiki/Hard_link). Les liens physiques entre différents disques ne sont pas pris en charge ; par conséquent, dans ces cas, les parts résultantes sont stockées sur les mêmes disques que les parts initiales.

En arrière-plan, les parts sont déplacées entre les volumes en fonction de la quantité d’espace libre (paramètre `move_factor`), selon l’ordre dans lequel les volumes sont déclarés dans le fichier de configuration.
Les données ne sont jamais transférées depuis le dernier volume vers le premier. Il est possible d’utiliser les tables système [system.part\_log](/docs/fr/reference/system-tables/part_log) (champ `type = MOVE_PART`) et [system.parts](/docs/fr/reference/system-tables/parts) (champs `path` et `disk`) pour surveiller les déplacements en arrière-plan. Des informations détaillées sont également disponibles dans les logs du serveur.

L’utilisateur peut forcer le déplacement d’une part ou d’une partition d’un volume vers un autre à l’aide de la requête [ALTER TABLE ... MOVE PART|PARTITION ... TO VOLUME|DISK ...](/docs/fr/reference/statements/alter/partition), toutes les restrictions applicables aux opérations en arrière-plan étant prises en compte. La requête lance elle-même le déplacement et n’attend pas la fin des opérations en arrière-plan. L’utilisateur recevra un message d’erreur si l’espace libre disponible n’est pas suffisant ou si l’une des conditions requises n’est pas remplie.

Le déplacement des données n’interfère pas avec la réplication des données. Par conséquent, différentes politiques de stockage peuvent être spécifiées pour la même table sur différentes répliques.

Une fois les fusions et mutations en arrière-plan terminées, les anciennes parts ne sont supprimées qu’après un certain délai (`old_parts_lifetime`).
Pendant cette période, elles ne sont pas déplacées vers d’autres volumes ou disques. Par conséquent, jusqu’à leur suppression définitive, elles sont toujours prises en compte dans l’évaluation de l’espace disque occupé.

L’utilisateur peut répartir de nouvelles parts volumineuses entre différents disques d’un volume [JBOD](https://en.wikipedia.org/wiki/Non-RAID_drive_architectures) de manière équilibrée à l’aide du paramètre [min\_bytes\_to\_rebalance\_partition\_over\_jbod](/docs/fr/reference/settings/merge-tree-settings#min_bytes_to_rebalance_partition_over_jbod).

<div id="table_engine-mergetree-s3">
  ## Utilisation du stockage externe pour les données
</div>

Les moteurs de table de la famille [MergeTree](/docs/fr/reference/engines/table-engines/mergetree-family/mergetree) peuvent stocker des données dans `S3`, `AzureBlobStorage` et `HDFS` à l’aide d’un disque de type `s3`, `azure_blob_storage` ou `hdfs`, respectivement. Consultez [la configuration des options de stockage externe](/docs/fr/concepts/features/configuration/server-config/storing-data#configuring-external-storage) pour plus de détails.

Exemple d’utilisation de [S3](https://aws.amazon.com/s3/) comme stockage externe à l’aide d’un disque de type `s3`.

Extrait de configuration :

```xml theme={null}
<storage_configuration>
    ...
    <disks>
        <s3>
            <type>s3</type>
            <support_batch_delete>true</support_batch_delete>
            <endpoint>https://clickhouse-public-datasets.s3.amazonaws.com/my-bucket/root-path/</endpoint>
            <access_key_id>your_access_key_id</access_key_id>
            <secret_access_key>your_secret_access_key</secret_access_key>
            <region></region>
            <header>Authorization: Bearer SOME-TOKEN</header>
            <server_side_encryption_customer_key_base64>your_base64_encoded_customer_key</server_side_encryption_customer_key_base64>
            <server_side_encryption_kms_key_id>your_kms_key_id</server_side_encryption_kms_key_id>
            <server_side_encryption_kms_encryption_context>your_kms_encryption_context</server_side_encryption_kms_encryption_context>
            <server_side_encryption_kms_bucket_key_enabled>true</server_side_encryption_kms_bucket_key_enabled>
            <proxy>
                <uri>http://proxy1</uri>
                <uri>http://proxy2</uri>
            </proxy>
            <connect_timeout_ms>10000</connect_timeout_ms>
            <request_timeout_ms>5000</request_timeout_ms>
            <retry_attempts>10</retry_attempts>
            <single_read_retries>4</single_read_retries>
            <min_bytes_for_seek>1000</min_bytes_for_seek>
            <metadata_path>/var/lib/clickhouse/disks/s3/</metadata_path>
            <skip_access_check>false</skip_access_check>
        </s3>
        <s3_cache>
            <type>cache</type>
            <disk>s3</disk>
            <path>/var/lib/clickhouse/disks/s3_cache/</path>
            <max_size>10Gi</max_size>
        </s3_cache>
    </disks>
    ...
</storage_configuration>
```

Voir aussi [la configuration des options de stockage externe](/docs/fr/concepts/features/configuration/server-config/storing-data#configuring-external-storage).

<div id="s3-multiple-volumes">
  ### Utiliser des disques S3 avec plusieurs volumes
</div>

Les disques S3 (et les autres disques de stockage d’objets) peuvent être utilisés dans des politiques de stockage à plusieurs disques et à plusieurs volumes de la même manière que les disques locaux. Cela permet de répartir les données sur plusieurs buckets S3 au sein d’un même volume (de type JBOD), ou de mettre en place des politiques de stockage hiérarchisé avec des volumes S3.

Par exemple, pour répartir les données entre deux buckets S3 en round-robin :

```xml theme={null}
<storage_configuration>
    <disks>
        <s3_bucket1>
            <type>s3</type>
            <endpoint>https://s3.amazonaws.com/bucket-1/data/</endpoint>
            <access_key_id>your_access_key_id</access_key_id>
            <secret_access_key>your_secret_access_key</secret_access_key>
        </s3_bucket1>
        <s3_bucket2>
            <type>s3</type>
            <endpoint>https://s3.amazonaws.com/bucket-2/data/</endpoint>
            <access_key_id>your_access_key_id</access_key_id>
            <secret_access_key>your_secret_access_key</secret_access_key>
        </s3_bucket2>
    </disks>
    <policies>
        <s3_multi_bucket>
            <volumes>
                <main>
                    <disk>s3_bucket1</disk>
                    <disk>s3_bucket2</disk>
                </main>
            </volumes>
        </s3_multi_bucket>
    </policies>
</storage_configuration>
```

Vous pouvez également combiner des volumes locaux et S3 dans une politique hiérarchisée, par exemple en déplaçant les données d’un SSD local vers S3 au fil du temps :

```xml theme={null}
<storage_configuration>
    <disks>
        <local_ssd>
            <path>/mnt/fast_ssd/clickhouse/</path>
        </local_ssd>
        <s3_cold>
            <type>s3</type>
            <endpoint>https://s3.amazonaws.com/cold-storage/data/</endpoint>
            <access_key_id>your_access_key_id</access_key_id>
            <secret_access_key>your_secret_access_key</secret_access_key>
        </s3_cold>
    </disks>
    <policies>
        <local_to_s3>
            <volumes>
                <hot>
                    <disk>local_ssd</disk>
                    <max_data_part_size_bytes>1073741824</max_data_part_size_bytes>
                </hot>
                <cold>
                    <disk>s3_cold</disk>
                </cold>
            </volumes>
            <move_factor>0.2</move_factor>
        </local_to_s3>
    </policies>
</storage_configuration>
```

<Note>
  Lorsque `use_environment_credentials` est utilisé pour l'authentication S3, les identifiants d'environnement (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`) sont partagés entre tous les disques S3. Il n'est pas possible d'utiliser des identifiants d'environnement différents selon les disques. Si vous avez besoin d'identifiants distincts pour chaque disque S3, utilisez plutôt des paramètres `access_key_id` et `secret_access_key` explicites pour chaque disque.
</Note>

Il est possible de configurer des tables MergeTree non répliquées dans un scénario avec un seul writer et plusieurs lecteurs sur un stockage partagé. Cela est rendu possible par l'actualisation automatique de la liste des parts, qui peut être configurée sur les lecteurs. Notez que cela nécessite des métadonnées de système de fichiers partagées entre les répliques (ou `table_disk = true` avec un disque local à la table). Voir [refresh\_parts\_interval and table\_disk](/docs/fr/concepts/features/configuration/server-config/storing-data#refresh-parts-interval-and-table-disk).

<Info>
  **configuration du cache**

  Les versions 22.3 à 22.7 de ClickHouse utilisent une configuration du cache différente ; consultez [using local cache](/docs/fr/concepts/features/configuration/server-config/storing-data#using-local-cache) si vous utilisez l'une de ces versions.
</Info>

<div id="virtual-columns">
  ## Colonnes virtuelles
</div>

* `_part` — Nom d’une part.
* `_part_index` — Indice séquentiel de la part dans le résultat de la requête.
* `_part_starting_offset` — Ligne de début cumulée de la part dans le résultat de la requête.
* `_part_offset` — Numéro de la ligne dans la part.
* `_part_granule_offset` — Numéro du granule dans la part.
* `_partition_id` — Nom d’une partition.
* `_part_uuid` — Identifiant unique de la part (si le paramètre MergeTree `assign_part_uuids` est activé).
* `_part_data_version` — Version des données de la part (soit le numéro de bloc minimal, soit la version de la mutation).
* `_partition_value` — Valeurs (un tuple) d’une expression `partition by`.
* `_sample_factor` — Facteur d’échantillonnage (issu de la requête).
* `_block_number` — Numéro d’origine du bloc pour la ligne, attribué lors de l’insertion et conservé lors des fusions lorsque le paramètre `enable_block_number_column` est activé.
* `_block_offset` — Numéro d’origine de la ligne dans le bloc, attribué lors de l’insertion et conservé lors des fusions lorsque le paramètre `enable_block_offset_column` est activé.
* `_disk_name` — Nom du disque utilisé pour le stockage.

<div id="column-statistics">
  ## Statistiques de colonne
</div>

La déclaration des statistiques figure dans la section des colonnes de la requête `CREATE` des tables de la famille `*MergeTree*` :

```sql theme={null}
CREATE TABLE tab
(
    a Int64 STATISTICS(tdigest, uniq),
    b Float64
)
ENGINE = MergeTree
ORDER BY a
```

Nous pouvons également manipuler les statistiques à l’aide d’instructions `ALTER` :

```sql theme={null}
ALTER TABLE tab ADD STATISTICS b TYPE tdigest, uniq;
ALTER TABLE tab DROP STATISTICS a;
```

Ces statistiques légères agrègent des informations sur la répartition des valeurs dans les colonnes. Elles sont stockées dans chaque part et mises à jour à chaque insertion.
Elles ne peuvent être utilisées pour l’optimisation PREWHERE que si l’on active `set use_statistics = 1`.

<div id="part-pruning-with-statistics">
  #### Élagage des parts avec des statistiques
</div>

Lorsque `use_statistics_for_part_pruning` est activé, les statistiques peuvent être utilisées pour l'élagage des parts.
Actuellement, seules les statistiques `basic` (ainsi que les statistiques `minmax` obsolètes) prennent en charge l'élagage des parts. Lorsque de telles statistiques sont définies sur une colonne, ClickHouse enregistre les valeurs minimale et maximale de cette colonne dans chaque part.
L'élagage des parts permet d'éviter la lecture de parts de données entières lorsque la condition de filtre de la requête ne peut correspondre à aucune ligne dans cette part.

**Exemple :**

```sql theme={null}
-- Create a table with basic statistics on the 'value' column
CREATE TABLE test_stats
(
    id UInt64,
    value Int64 STATISTICS(basic)
)
ENGINE = MergeTree
ORDER BY id;

SYSTEM STOP MERGES test_stats;

-- Insert data in separate inserts to create multiple parts
INSERT INTO test_stats SELECT number, number FROM numbers(1000); -- Part 1: value range [0, 999]
INSERT INTO test_stats SELECT number, number + 10000 FROM numbers(1000); -- Part 2: value range [10000, 10999]

SET use_statistics_for_part_pruning = 1;

-- This query will skip Part 1 entirely because its max value (999) < 5000
SELECT count() FROM test_stats WHERE value > 5000;

-- Use EXPLAIN to see the pruning effect
EXPLAIN indexes = 1 SELECT count() FROM test_stats WHERE value > 5000;
-- The output will show "Parts: 1/2" indicating one part was pruned
```

<div id="available-types-of-column-statistics">
  ### Types disponibles de statistiques de colonne
</div>

* `basic`

  Un ensemble compact de résumés à valeur unique dérivés d’une colonne. Selon le type de la colonne, les éléments suivants sont renseignés :

  * pour toute colonne dont les valeurs sont représentées par un nombre (entiers, flottants, `Decimal*`, `Date*`, `DateTime*`, `Enum*`, `IPv4`, ...) : les valeurs minimale et maximale, qui permettent d’estimer la sélectivité des filtres de plage et d’effectuer l’élagage des parts ;
  * pour les colonnes `String` et `FixedString` : la longueur totale en octets des valeurs non `NULL` (à partir de laquelle la longueur moyenne des chaînes peut être déduite) ;
  * pour les colonnes `Nullable` et `LowCardinality(Nullable)` : le nombre de valeurs `NULL`, que l’optimiseur utilise pour exclure les lignes `NULL` des estimations de sélectivité.

    Une seule statistique `basic` peut renseigner plusieurs de ces éléments à la fois — par exemple, sur une colonne `Nullable(UInt32)`, elle suit à la fois les min/max numériques et le nombre de valeurs nulles. Par rapport à `minmax`, `basic` fonctionne aussi sur les colonnes `String` / `FixedString` et peut être déclarée sur des wrappers `Nullable` de types comme `UUID` ou `IPv6` uniquement pour suivre le nombre de valeurs nulles.

* `minmax` (obsolète)

<Note>
  Les statistiques `minmax` sont obsolètes et ne peuvent plus être créées (`CREATE TABLE ... STATISTICS(minmax)` et `ALTER TABLE ... ADD/MODIFY STATISTICS ... TYPE minmax` renvoient une erreur). Les tables et parts existantes avec des statistiques `minmax` continuent de fonctionner. Utilisez plutôt les statistiques `basic`.
</Note>

* `tdigest`

<Warning>
  Les statistiques de type `tdigest` ont un coût de création élevé et peuvent ralentir l’ingestion des données.
</Warning>

Des sketches [TDigest](https://github.com/tdunning/t-digest) qui permettent de calculer des percentiles approximatifs (par ex. le 90e percentile) pour les colonnes numériques.

* `uniq`

  Des sketches [BJKST](https://people.iith.ac.in/aravind/Files-CS5120/pc-lec14-BJKST.pdf) qui fournissent une estimation du nombre de valeurs distinctes contenues dans une colonne. Utilise en interne [`uniq`](/docs/fr/reference/functions/aggregate-functions/uniq).

* `uniq_v2`

  Similaire à `uniq`, mais utilise en interne [`uniqCombined`](/docs/fr/reference/functions/aggregate-functions/uniqCombined)`(12)` (une variante de [HyperLogLog](https://en.wikipedia.org/wiki/HyperLogLog)). Consomme moins de mémoire que `uniq` et peut être compilé plus rapidement.

* `countmin`

<Warning>
  Les statistiques de type `countmin` ont un coût de création élevé et peuvent ralentir l’ingestion des données.
</Warning>

Des sketches [CountMin](https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch) qui fournissent un décompte approximatif de la fréquence de chaque valeur dans une colonne.

<div id="supported-data-types">
  ### Types de données pris en charge
</div>

|          | (U)Int\*, Float\*, Decimal(*), Date*, Boolean, Enum\* | IPv4 | String ou FixedString |
| -------- | ----------------------------------------------------- | ---- | --------------------- |
| basic    | ✔                                                     | ✔    | ✔                     |
| countmin | ✔                                                     | ✔    | ✔                     |
| minmax   | ✔                                                     | ✔    | ✗                     |
| tdigest  | ✔                                                     | ✗    | ✗                     |
| uniq     | ✔                                                     | ✔    | ✔                     |
| uniq\_v2 | ✔                                                     | ✔    | ✔                     |

Tous les éléments ci-dessus acceptent également les wrappers `Nullable` et `LowCardinality(Nullable)` des types indiqués. `Basic` peut aussi être déclaré sur des wrappers `Nullable` de types comme `UUID` ou `IPv6`, uniquement pour suivre le nombre de valeurs nulles.

<div id="supported-operations">
  ### Opérations prises en charge
</div>

|          | Filtres d'égalité (==) | Filtres par plage (`>, >=, <, <=`) |
| -------- | ---------------------- | ---------------------------------- |
| basic    | ✗                      | ✔ (colonnes numériques uniquement) |
| countmin | ✔                      | ✗                                  |
| minmax   | ✗                      | ✔ (colonnes numériques uniquement) |
| tdigest  | ✗                      | ✔ (colonnes numériques uniquement) |
| uniq     | ✔                      | ✗                                  |
| uniq\_v2 | ✔                      | ✗                                  |

Pour `basic` sur les colonnes `String` / `FixedString`, la statistique n'enregistre que la longueur totale en octets des valeurs non-NULL
(utilisée pour estimer la longueur moyenne des chaînes) et le nombre de valeurs NULL ;
les filtres par plage et l'élagage des parts ne s'appuient pas sur elle.

<div id="column-level-settings">
  ## Paramètres au niveau des colonnes
</div>

Certains paramètres de MergeTree peuvent être redéfinis au niveau des colonnes :

* `max_compress_block_size` — Taille maximale des blocs de données non compressées avant leur compression pour l’écriture dans une table.
* `min_compress_block_size` — Taille minimale des blocs de données non compressées requise pour la compression lors de l’écriture du mark suivant.

Exemple :

```sql theme={null}
CREATE TABLE tab
(
    id Int64,
    document String SETTINGS (min_compress_block_size = 16777216, max_compress_block_size = 16777216)
)
ENGINE = MergeTree
ORDER BY id
```

Les paramètres au niveau des colonnes peuvent être modifiés ou supprimés à l’aide de [ALTER MODIFY COLUMN](/docs/fr/reference/statements/alter/column), par exemple :

* Supprimez `SETTINGS` de la déclaration de la colonne :

```sql theme={null}
ALTER TABLE tab MODIFY COLUMN document REMOVE SETTINGS;
```

* Modifiez un paramètre :

```sql theme={null}
ALTER TABLE tab MODIFY COLUMN document MODIFY SETTING min_compress_block_size = 8192;
```

* Réinitialise un ou plusieurs paramètres et supprime également la déclaration du paramètre dans l’expression de colonne de la requête CREATE de la table.

```sql theme={null}
ALTER TABLE tab MODIFY COLUMN document RESET SETTING min_compress_block_size;
```
