> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-parallel-read-in-order-multi-part.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Un moteur de table qui stocke des séries temporelles, c’est-à-dire un ensemble de valeurs associées à des horodatages et à des tags (ou labels).

# Moteur de table TimeSeries

export const PrivatePreviewBadge = () => {
  return <div className="privatePreviewBadge">
            <div className="privatePreviewIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M5.33301 6.66667V4.66667V4.66667C5.33301 3.194 6.52701 2 7.99967 2V2C9.47234 2 10.6663 3.194 10.6663 4.66667V4.66667V6.66667" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path d="M8.00033 9.33337V11.3334" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path fillRule="evenodd" clipRule="evenodd" d="M11.333 14H4.66634C3.92967 14 3.33301 13.4033 3.33301 12.6666V7.99996C3.33301 7.26329 3.92967 6.66663 4.66634 6.66663H11.333C12.0697 6.66663 12.6663 7.26329 12.6663 7.99996V12.6666C12.6663 13.4033 12.0697 14 11.333 14Z" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            {'Aperçu privé'}
        </div>;
};

<PrivatePreviewBadge />

Un moteur de table qui stocke des séries temporelles, c’est-à-dire un ensemble de valeurs associées à des horodatages et à des tags (ou labels) :

```sql theme={null}
metric_name1[tag1=value1, tag2=value2, ...] = {timestamp1: value1, timestamp2: value2, ...}
metric_name2[...] = ...
```

<Info>
  Il s'agit d'une fonctionnalité en private preview qui pourra, dans les versions ultérieures, évoluer de manière incompatible avec les versions précédentes.
  Activez l'utilisation du moteur de table TimeSeries
  à l'aide du paramètre `enable_time_series_table`.
  Saisissez la commande `set enable_time_series_table = 1`.
</Info>

<Note>
  Le moteur de table `TimeSeries` est disponible dans ClickHouse Cloud en tant que fonctionnalité en private preview.
  Les services qui participent à la private preview disposent déjà du paramètre
  `enable_time_series_table` configuré. Les autres services ClickHouse Cloud
  ne possèdent pas cette configuration, et vous ne pouvez pas activer le moteur vous-même sur
  un tel service.
</Note>

## Syntaxe

```sql theme={null}
CREATE TABLE name [(columns)] ENGINE=TimeSeries
[SETTINGS var1=value1, ...]
[SAMPLES db.samples_table_name | [SAMPLES INNER COLUMNS (...)] [SAMPLES INNER ENGINE engine(arguments)]]
[RECENT SAMPLES db.recent_samples_table_name | [RECENT SAMPLES INNER COLUMNS (...)] [RECENT SAMPLES INNER ENGINE engine(arguments)]]
[TAGS db.tags_table_name | [TAGS INNER COLUMNS (...)] [TAGS INNER ENGINE engine(arguments)]]
[METRIC FAMILIES db.metric_families_table_name | [METRIC FAMILIES INNER COLUMNS (...)] [METRIC FAMILIES INNER ENGINE engine(arguments)]]
```

<Note>
  Le mot-clé `SAMPLES` a pour alias `DATA`, et le mot-clé `METRIC FAMILIES` a pour alias `METRICS`, tous deux étant maintenus pour assurer la rétrocompatibilité.
  La définition d'une table d'une [version](#schema-versioning) antérieure à 4 est écrite avec `METRICS`, afin qu'un serveur plus ancien puisse la lire.
</Note>

## Utilisation

Il est plus facile de commencer en laissant tous les paramètres par défaut (il est possible de créer une table `TimeSeries` sans préciser de liste de colonnes) :

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
```

Cette table peut ensuite être utilisée avec les protocoles suivants (un port doit être défini dans la configuration du serveur) :

* [prometheus remote-write](/fr/concepts/features/interfaces/prometheus#remote-write)
* [prometheus remote-read](/fr/concepts/features/interfaces/prometheus#remote-read)

### Colonnes externes

Les colonnes d’une table TimeSeries sont générées automatiquement. Ce sont des colonnes externes : elles ne stockent aucune donnée et servent uniquement d’interface pour SELECT/INSERT. Les données réelles sont stockées dans les [tables cibles](#target-tables). Voici la liste des colonnes externes :

| Name | Type | Description |
| - | - | - |
| `metric_name` | `String` | Le nom de la métrique |
| `tags` | `Map(String, String)` | Map de tags (labels) pour la série temporelle |
| `samples` | `Array(Tuple(DateTime64(3), Float64))` par défaut | Tableau de paires (horodatage, valeur) pour une série temporelle. Les types d’élément de l’horodatage et du scalaire du tuple peuvent être déduits de la déclaration `INNER COLUMNS` des échantillons (voir [Spécification des colonnes externes](#specifying-outer-columns)). La colonne est nommée `time_series` dans les tables de [version](#schema-versioning) 2 et antérieures |
| `metric_family` | `String` | Le nom de la famille de métriques (pour les métadonnées de métriques) |
| `type` | `String` | Le type de la métrique (par ex. "counter", "gauge") |
| `unit` | `String` | L’unité de la métrique |
| `help` | `String` | La description de la métrique |

Exemple :

```sql theme={null}
INSERT INTO my_table (metric_name, tags, samples) VALUES
    ('cpu_usage', {'job': 'node_exporter', 'instance': 'host1:9100'},
     [(toDateTime64('2024-01-01 00:00:00', 3), 0.5), (toDateTime64('2024-01-01 00:01:00', 3), 0.7)])
```

`metric_name` peut être vide lors de l’insertion, ce qui signifie que le nom de la métrique est indiqué dans `tags` sous `__name__`, par exemple :

```sql theme={null}
INSERT INTO my_table (tags, samples) VALUES
    ({'__name__': 'cpu_usage', 'job': 'test'},
     [(toDateTime64('2024-01-01 00:00:00', 3), 0.5)])
```

Pour insérer les métadonnées des métriques, insérez-les dans les colonnes `metric_family`, `type`, `unit` et `help` :

```sql theme={null}
INSERT INTO my_table (metric_name, tags, samples, metric_family, type, unit, help) VALUES
    ('http_requests_total', {'method': 'GET'}, [(now64(), 100.0)],
     'http_requests_total', 'counter', 'requests', 'Total HTTP requests')
```

### Spécification des colonnes externes

La colonne externe `samples` peut être déclarée explicitement dans une instruction `CREATE TABLE` afin de remplacer son type par défaut `Array(Tuple(DateTime64(3), Float64))` (son ancien nom `time_series` est également accepté). ClickHouse extrait du tuple le type d’horodatage et le type scalaire, puis les propage à la table samples interne :

```sql theme={null}
CREATE TABLE my_table (samples Array(Tuple(UInt32, Float32))) ENGINE=TimeSeries
```

Cela revient à déclarer directement les types des colonnes `timestamp` et `value` dans la clause `INNER COLUMNS` de `samples` :

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp UInt32 CODEC(Delta, T64, ZSTD(3)), value Float32 CODEC(ALP, ZSTD(3)))
```

Si les deux formes sont utilisées dans la même instruction `CREATE TABLE`, les types déclarés doivent être identiques.

## Tables cibles

Une table `TimeSeries` ne possède pas ses propres données : tout est stocké dans ses tables cibles.
Son fonctionnement est similaire à celui d’une [vue matérialisée](/fr/reference/statements/create/view#materialized-view),
à la différence qu’une vue matérialisée n’a qu’une seule table cible,
tandis qu’une table `TimeSeries` a trois tables cibles obligatoires nommées [samples](#samples-table), [tags](#tags-table) et [familles de métriques](#metric-families-table),
ainsi qu’une table cible facultative d’[échantillons récents](#recent-samples-table), activée par défaut
(consultez le paramètre [recent\_samples\_ttl\_seconds](#settings)).

Les tables cibles peuvent être spécifiées explicitement dans la requête `CREATE TABLE`,
ou le moteur de table `TimeSeries` peut générer automatiquement des tables cibles internes.

Les lignes insérées dans une table `TimeSeries` sont transformées, découpées en blocs, puis insérées dans ces tables cibles.

Les tables cibles sont les suivantes :

### Table *samples*

La table *samples* contient des séries temporelles associées à un certain identifiant.

La table *samples* doit comporter les colonnes suivantes :

| Nom | Obligatoire ? | Type par défaut | Types possibles | Description |
| - | - | - | - | - |
| `id` | \[x] | `Tuple(UInt64, LowCardinality(UUID))` | tout type | Identifie une combinaison de noms de métriques et de tags |
| `timestamp` | \[x] | `DateTime64(3)` | `DateTime64(X)` | Un instant donné |
| `value` | \[x] | `Float64` | `Float32` ou `Float64` | Une valeur associée au `timestamp` |

Les colonnes créées par le moteur lui-même reçoivent des codecs de compression pour séries temporelles :
`timestamp CODEC(Delta, T64, ZSTD(3))` et `value CODEC(ALP, ZSTD(3))`. Les horodatages quasi monotones se
compressent très peu avec des codecs génériques et peuvent sinon représenter l'essentiel de la taille sur disque de la table *samples*.
Le moteur active `ALP` pour ses tables internes *samples* et *recent samples* sans qu'il soit nécessaire de définir `enable_alp_codec`.
Voir aussi [Ajustement des types de colonnes](#adjusting-column-types).

### Table des échantillons récents

La table des *échantillons récents* est facultative et activée par défaut (voir le paramètre [recent\_samples\_ttl\_seconds](#settings) ; définir sa valeur sur zéro désactive la table). Elle contient une copie des échantillons dont l’ancienneté est inférieure à la TTL définie par ce paramètre et doit comporter les mêmes colonnes que la table [samples](#samples-table).
La colonne générée `timestamp` utilise `CODEC(Delta, T64, ZSTD(3))`,
et la colonne générée `value` utilise `CODEC(ALP, ZSTD(3))`.

Chaque échantillon inséré est écrit à la fois dans la table samples et dans la table des échantillons récents.
Les requêtes dont l’intervalle de temps est compris dans la fenêtre TTL lisent la table des échantillons récents plutôt que la table samples principale,
car elle est bien plus petite (ce comportement peut être désactivé à l’aide du paramètre au niveau de la requête `time_series_prefer_recent_samples_table`).

La TTL de la table interne des échantillons récents est toujours dérivée du paramètre [recent\_samples\_ttl\_seconds](#settings).

### Table des tags

La table *tags* contient des identifiants calculés pour chaque combinaison d'un nom de métrique et de tags.

La table *tags* doit contenir les colonnes suivantes :

| Nom | Obligatoire ? | Type par défaut | Types possibles | Description |
| - | - | - | - | - |
| `id` | \[x] | `Tuple(UInt64, LowCardinality(UUID))` | tout type (doit correspondre au type de `id` dans la table [samples](#samples-table)) | Un `id` identifie une combinaison d'un nom de métrique et de tags. L'expression DEFAULT indique comment calculer un tel identifiant |
| `metric_name` | \[x] | `LowCardinality(String)` | `String` ou `LowCardinality(String)` | Le nom d'une métrique |
| `<tag_value_column>` | \[ ] | `String` | `String` ou `LowCardinality(String)` ou `LowCardinality(Nullable(String))` | La valeur d'un tag spécifique ; le nom du tag et celui de la colonne correspondante sont indiqués dans le paramètre [tags\_to\_columns](#settings) |
| `tags` | \[x] | `Map(LowCardinality(String), String)` | `Map(String, String)` ou `Map(LowCardinality(String), String)` ou `Map(LowCardinality(String), LowCardinality(String))` | Map de tous les tags, y compris le tag `__name__` qui contient le nom d'une métrique et les tags dont les noms sont énumérés dans le paramètre [tags\_to\_columns](#settings). Les tables créées par des versions antérieures de ClickHouse ne stockaient dans cette colonne que les tags sans colonnes dédiées et sans le nom de la métrique ; la lecture gère les deux cas |
| `min_time` | \[ ] | `Nullable(DateTime64(3))` | `DateTime64(X)` ou `Nullable(DateTime64(X))` | Horodatage minimal des séries temporelles associées à cet `id`. La colonne est créée si [store\_min\_time\_and\_max\_time](#settings) vaut `true` |
| `max_time` | \[ ] | `Nullable(DateTime64(3))` | `DateTime64(X)` ou `Nullable(DateTime64(X))` | Horodatage maximal des séries temporelles associées à cet `id`. La colonne est créée si [store\_min\_time\_and\_max\_time](#settings) vaut `true` |

Les nouvelles tables internes de tags de [version](#schema-versioning) 5 et ultérieures, dotées d'un moteur de la famille `MergeTree`, possèdent un index de texte inversé sur `tags` :
`INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs')`. Il accélère les correspondances exactes de labels, telles que
`{job="api"}` dans PromQL, en recherchant simultanément la clé et la valeur. Les comparaisons avec une chaîne vide correspondent également
aux labels manquants et n'utilisent pas cet index.

Les index explicites déclarés dans `TAGS INNER COLUMNS` remplacent l'index par défaut. Les tables existantes et les tables de tags
externes conservent leurs index ; ajoutez et matérialisez l'index sur leur table de tags cible pour l'activer.

### Table *familles de métriques*

La table *familles de métriques* contient des informations sur les familles de métriques collectées, leurs types et leurs descriptions.
Une famille de métriques est un groupe de métriques portant le même nom (le tag `__name__`) et ayant le même type. Par exemple, un histogramme est une famille de métriques composée de plusieurs métriques.

La table *familles de métriques* doit comporter les colonnes suivantes :

| Nom | Obligatoire ? | Type par défaut | Types possibles | Description |
| - | - | - | - | - |
| `metric_family_name` | \[x] | `String` | `String` ou `LowCardinality(String)` | Le nom d’une famille de métriques |
| `type` | \[x] | `LowCardinality(String)` | `String` ou `LowCardinality(String)` | Le type d’une famille de métriques : « counter », « gauge », « summary », « stateset », « histogram » ou « gaugehistogram » |
| `unit` | \[x] | `LowCardinality(String)` | `String` ou `LowCardinality(String)` | L’unité utilisée pour une métrique |
| `help` | \[x] | `String` | `String` ou `LowCardinality(String)` | La description d’une métrique |

## Création

Il existe plusieurs façons de créer une table avec le moteur de table `TimeSeries`.
L'instruction la plus simple

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
```

créera en fait la table suivante (vous pouvez le vérifier en exécutant `SHOW CREATE TABLE my_table`) :

```sql theme={null}
CREATE TABLE my_table
(
    `metric_name` String,
    `tags` Map(String, String),
    `samples` Array(Tuple(DateTime64(3), Float64)),
    `metric_family` String,
    `type` String,
    `unit` String,
    `help` String
)
ENGINE = TimeSeries
SETTINGS version = 5, recent_samples_ttl_seconds = 345600
SAMPLES INNER COLUMNS
(
    `id` Tuple(UInt64, LowCardinality(UUID)),
    `timestamp` DateTime64(3) CODEC(Delta, T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
SAMPLES INNER ENGINE = MergeTree ORDER BY (id, timestamp) SETTINGS index_granularity = 32768
RECENT SAMPLES INNER COLUMNS
(
    `id` Tuple(UInt64, UUID),
    `timestamp` DateTime64(3) CODEC(Delta, T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
RECENT SAMPLES INNER ENGINE = MergeTree PARTITION BY toStartOfInterval(toDateTime(timestamp), toIntervalHour(5)) ORDER BY (id, timestamp) TTL toDateTime(timestamp) + toIntervalSecond(345600) SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1
TAGS INNER COLUMNS
(
    `id` Tuple(UInt64, LowCardinality(UUID)) DEFAULT tuple(sipHash64(metric_name), toLowCardinality(reinterpretAsUUID(sipHash128(tags)))),
    `metric_name` LowCardinality(String),
    `tags` Map(LowCardinality(String), String),
    `min_time` SimpleAggregateFunction(min, Nullable(DateTime64(3))),
    `max_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
    INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs') GRANULARITY 100000000
)
TAGS INNER ENGINE = AggregatingMergeTree PRIMARY KEY metric_name ORDER BY (metric_name, id) SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192
METRIC FAMILIES INNER COLUMNS
(
    `metric_family_name` String,
    `type` LowCardinality(String),
    `unit` LowCardinality(String),
    `help` String
)
METRIC FAMILIES INNER ENGINE = ReplacingMergeTree ORDER BY metric_family_name
```

Les colonnes ont donc été générées automatiquement, et il existe également quatre tables cibles internes, chacune avec ses propres définitions de colonnes
stockées dans les clauses `INNER COLUMNS`. Le paramètre `recent_samples_ttl_seconds` a été écrit dans la clause `SETTINGS`
avec sa valeur par défaut : il définit le TTL de la table des échantillons récents, et sa valeur effective est donc fixée lors de la création.
De plus, la dernière version du schéma a été fixée dans le paramètre `version` (voir [Versionnement du schéma](#schema-versioning)).

Les tables cibles internes portent des noms tels que `.inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`,
`.inner_id.recentsamples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, `.inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`,
`.inner_id.metricfamilies.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
et chaque table cible possède son propre ensemble de colonnes :

```sql theme={null}
CREATE TABLE default.`.inner_id.samples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, LowCardinality(UUID)),
    `timestamp` DateTime64(3) CODEC(Delta(8), T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
ENGINE = MergeTree
ORDER BY (id, timestamp)
SETTINGS index_granularity = 32768
```

```sql theme={null}
CREATE TABLE default.`.inner_id.recentsamples.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, UUID),
    `timestamp` DateTime64(3) CODEC(Delta(8), T64, ZSTD(3)),
    `value` Float64 CODEC(ALP, ZSTD(3))
)
ENGINE = MergeTree
PARTITION BY toStartOfInterval(toDateTime(timestamp), toIntervalHour(5))
ORDER BY (id, timestamp)
TTL toDateTime(timestamp) + toIntervalSecond(345600)
SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1
```

```sql theme={null}
CREATE TABLE default.`.inner_id.tags.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `id` Tuple(UInt64, LowCardinality(UUID)) DEFAULT tuple(sipHash64(metric_name), toLowCardinality(reinterpretAsUUID(sipHash128(tags)))),
    `metric_name` LowCardinality(String),
    `tags` Map(LowCardinality(String), String),
    `min_time` SimpleAggregateFunction(min, Nullable(DateTime64(3))),
    `max_time` SimpleAggregateFunction(max, Nullable(DateTime64(3))),
    INDEX tags_idx tags TYPE text(tokenizer = 'keyValuePairs') GRANULARITY 100000000
)
ENGINE = AggregatingMergeTree
PRIMARY KEY metric_name
ORDER BY (metric_name, id)
SETTINGS allow_dimensions_outside_sorting_key = 1, index_granularity = 8192
```

```sql theme={null}
CREATE TABLE default.`.inner_id.metricfamilies.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
(
    `metric_family_name` String,
    `type` LowCardinality(String),
    `unit` LowCardinality(String),
    `help` String
)
ENGINE = ReplacingMergeTree
ORDER BY metric_family_name
SETTINGS index_granularity = 8192
```

## Créer une table AS une table existante

L'instruction `CREATE TABLE new_table AS existing_table` crée une table `TimeSeries` configurée comme `existing_table`,
qui doit elle-même être une table `TimeSeries`. Les cibles externes d'`existing_table` ne sont pas copiées : l'instruction doit déclarer
ces cibles elle-même.

L'instruction reprend d'`existing_table` :

* la clause `SETTINGS`, à l'exception de `version` : la nouvelle table reçoit toujours la dernière version. Les paramètres indiqués dans l'instruction
  elle-même sont fusionnés par nom avec ceux qui sont copiés, si bien qu'un paramètre indiqué explicitement prime sur le paramètre copié, et que `name = DEFAULT`
  réinitialise un paramètre copié à sa valeur par défaut ;
* les clauses `INNER COLUMNS` et `INNER ENGINE` de chaque table interne. Les colonnes personnalisées (par exemple des colonnes supplémentaires, des colonnes
  dotées d'un codec ou d'une expression DEFAULT) ainsi que les éléments de moteur personnalisés (par exemple un moteur avec des arguments, une clé de tri personnalisée
  ou un paramètre de moteur) sont conservés ; les autres colonnes et éléments de moteur sont alignés sur les paramètres de la nouvelle table, de sorte
  que `tags_to_columns`, `aggregate_min_time_and_max_time` ou `tags_index_granularity` indiqués dans l'instruction prennent effet.

Les types des colonnes `id`, timestamp et valeur, ainsi que le type de replication des moteurs internes (`MergeTree`,
`ReplicatedMergeTree` ou `SharedMergeTree`), sont également repris d'`existing_table`, sauf si l'instruction les déclare elle-même.
La liste des colonnes externes est régénérée et non copiée.

Une table créée par une version plus ancienne de ClickHouse peut servir d'`existing_table` : la nouvelle table adopte la
structure actuelle, par exemple le type `id` actuel et l'expression d'identifiant par défaut.

## Ajustement des types de colonnes

Vous pouvez modifier le type des colonnes dans les tables cibles internes à l’aide de la clause `INNER COLUMNS`. Par exemple, pour stocker les horodatages en microsecondes et les valeurs en `Float32`, utilisez :

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6) CODEC(Delta, T64, ZSTD(3)), value Float32 CODEC(ALP, ZSTD(3)))
```

Spécifier des colonnes internes sans codec revient à utiliser le codec par défaut pour celles-ci :

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES INNER COLUMNS (timestamp DateTime64(6), value Float32)
```

## La colonne `id`

La colonne `id` contient des identifiants ; chacun d’eux est calculé à partir d’une combinaison d’un nom de métrique et de tags.
Le type et l’expression `DEFAULT` utilisés pour générérer les identifiants peuvent être personnalisés via la clause `TAGS INNER COLUMNS` :

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
TAGS INNER COLUMNS (id UInt64 DEFAULT sipHash64(tags))
```

La colonne `id` peut être de tout type comparable non-`Nullable`. Les types de `id` déclarés dans les tables internes samples et tags doivent correspondre.

Si aucune expression `DEFAULT` n’est définie pour la colonne `id` et que le paramètre `id_generator` n’est pas défini, ClickHouse choisira automatiquement l’expression `DEFAULT` en fonction du type de `id`, mais uniquement si celui-ci est `UUID`, `UInt64`, `UInt128`, `FixedString(16)`, ces mêmes types encapsulés dans `LowCardinality` ou un tuple de deux de ces types. Pour un tel tuple, l’expression choisie automatiquement calcule un hash du nom de métrique dans le premier composant et un hash de tous les tags dans le second composant.

Un type d’identifiant `LowCardinality`, par exemple `Tuple(UInt64, LowCardinality(UUID))`, conserve les identifiants encodés par dictionnaire : la table samples stocke de petits dictionnaires par bloc avec des index de dictionnaire au lieu de répéter l’identifiant complet dans chaque ligne, ce qui réduit la quantité de données lues par les requêtes.

Le paramètre `id_generator` offre la même possibilité de personnalisation sans utiliser la clause `INNER COLUMNS` :

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SETTINGS id_generator = 'sipHash64(tags)'
```

Si ce paramètre est défini, il est utilisé pour générer `id`, même si le `DEFAULT` de la colonne contient une expression différente.

Le type de la colonne `id` peut également être spécifié dans le paramètre `id_type` au lieu de la clause `INNER COLUMNS` :

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SETTINGS id_type = 'UInt64', id_generator = 'sipHash64(tags)'
```

Lorsque le paramètre `id_generator` est défini, le paramètre `id_type` est enregistré automatiquement au moment du `CREATE`,
de sorte que la définition conserve le type pour lequel l’expression a été écrite.

## La colonne `tags`

La colonne `tags` contient tous les tags d’une série temporelle, y compris le tag `__name__` avec le nom d’une métrique.

Le paramètre `tags_to_columns` permet de spécifier qu’un tag donné doit également être stocké dans une colonne distincte
en plus de la map au sein de la colonne `tags` :

```sql theme={null}
CREATE TABLE my_table
ENGINE = TimeSeries
SETTINGS tags_to_columns = {'instance': 'instance', 'job': 'job'}
```

Cette instruction ajoute les colonnes `instance` et `job` à la table cible interne des [tags](#tags-table).
Les valeurs des tags `instance` et `job` seront stockées à la fois dans ces colonnes et dans la colonne `tags`.

<Note>
  Dans les tables créées par d’anciennes versions de ClickHouse, la colonne `tags` contient uniquement les tags sans colonnes
  dédiées et sans le nom de la métrique, et la colonne `all_tags` est une colonne éphémère qui était remplie lors de l’insertion
  avec tous les tags à l’exception du nom de la métrique.
</Note>

## Moteurs des tables cibles internes

Par défaut, les tables cibles internes utilisent les moteurs de table suivants :

* la table [samples](#samples-table) utilise [MergeTree](/fr/reference/engines/table-engines/mergetree-family/mergetree) ;
* la table [recent samples](#recent-samples-table) utilise [MergeTree](/fr/reference/engines/table-engines/mergetree-family/mergetree), partitionné en compartiments de 5 heures (voir le paramètre [recent\_samples\_partition\_by](#settings)), avec un `TTL` dérivé du
  paramètre [recent\_samples\_ttl\_seconds](#settings) et avec `ttl_only_drop_parts` activé, de sorte que les parties expirées sont supprimées dans leur intégralité ;
* la table [tags](#tags-table) utilise [AggregatingMergeTree](/fr/reference/engines/table-engines/mergetree-family/aggregatingmergetree), car les mêmes données sont souvent insérées plusieurs fois dans cette table ; il faut donc
  un moyen de supprimer les doublons, et ce moteur est également nécessaire pour effectuer une agrégation sur les colonnes `min_time` et `max_time` ;
* la table [familles de métriques](#metric-families-table) utilise [ReplacingMergeTree](/fr/reference/engines/table-engines/mergetree-family/replacingmergetree), car les mêmes données sont souvent insérées plusieurs fois dans cette table ; il faut donc
  un moyen de supprimer les doublons.

La famille de moteurs des tables internes générées suit le paramètre `default_table_engine` au niveau de la requête :
avec `default_table_engine = ReplicatedMergeTree` ou `SharedMergeTree`, les tables internes utilisent les moteurs
`Replicated` ou `Shared` correspondants. Avec `default_table_engine = None` (ou toute autre valeur), les moteurs des tables internes
doivent être spécifiés explicitement.

Toutes les tables internes doivent avoir le même type de réplication : si l’une d’elles est répliquée (ou partagée), les autres tables internes
doivent également être répliquées (ou partagées), sans quoi leur contenu divergerait entre les répliques. Par exemple,
déclarer `SAMPLES INNER ENGINE = ReplicatedMergeTree(...)` exige que les autres moteurs internes soient également répliqués -
soit déclarés explicitement, soit générés avec `default_table_engine = ReplicatedMergeTree`.

D’autres moteurs de table peuvent également être utilisés pour les tables cibles internes si cela est explicitement spécifié :

```sql theme={null}
CREATE TABLE my_table ENGINE=TimeSeries
SAMPLES ENGINE=ReplicatedMergeTree
RECENT SAMPLES ENGINE=ReplicatedMergeTree
TAGS ENGINE=ReplicatedAggregatingMergeTree
METRIC FAMILIES ENGINE=ReplicatedReplacingMergeTree
```

La table [tags](#tags-table) conserve les colonnes de tag (et le Map `tags`) en dehors de sa clé de tri,
ce que `AggregatingMergeTree` refuse par défaut (voir [`allow_dimensions_outside_sorting_key`](/fr/reference/engines/table-engines/mergetree-family/aggregatingmergetree)).
C’est sans danger ici, car ces colonnes dépendent fonctionnellement de `id`, qui fait partie de la clé de tri, de sorte que toutes les
lignes qu’une fusion en arrière-plan regroupe partagent les mêmes valeurs. Lorsque la table interne de tags est générée ou que son
moteur est spécifié en intégré comme ci-dessus, `TimeSeries` y définit automatiquement `allow_dimensions_outside_sorting_key = 1` ;
pour une table de tags d’agrégation [externe](#external-target-tables) créée manuellement, vous devez le définir vous-même.

## Tables cibles externes

Il est possible de faire en sorte qu'une table `TimeSeries` utilise une table créée manuellement :

```sql theme={null}
CREATE TABLE samples_for_my_table
(
    `id` UUID,
    `timestamp` DateTime64(3),
    `value` Float64
)
ENGINE = MergeTree
ORDER BY (id, timestamp);

CREATE TABLE tags_for_my_table ...

CREATE TABLE metric_families_for_my_table ...

CREATE TABLE my_table ENGINE=TimeSeries SAMPLES samples_for_my_table TAGS tags_for_my_table METRIC FAMILIES metric_families_for_my_table;
```

Une table externe peut également servir de cible pour les [échantillons récents](#recent-samples-table) (la clause `RECENT SAMPLES my_recent_samples_table`).
Une telle table doit comporter les mêmes colonnes qu'une table samples externe et doit conserver au moins
[recent\_samples\_ttl\_seconds](#settings) secondes de données, ce qui relève de la responsabilité de l'utilisateur.

Les types de colonnes des tables externes (`id`, `timestamp`, `value` et les `<tag_value_column>` répertoriées dans [`tags_to_columns`](#settings)) doivent correspondre à ceux que la table `TimeSeries` générerait sinon en interne (voir [table samples](#samples-table), [table des tags](#tags-table) et [table des familles de métriques](#metric-families-table) pour les contraintes de type). Les incompatibilités de type sont signalées lors de `CREATE`.

Le type de la colonne `id` d'une table tags externe et l'expression générant les identifiants sont enregistrés dans les paramètres [`id_type`](#settings) et [`id_generator`](#settings) lors de `CREATE` (à partir de la [version](#schema-versioning) 2), de sorte que la définition de la table `TimeSeries` les conserve : par exemple, `CREATE TABLE ... AS my_table` lit le type de `id` depuis la définition de `my_table` sans lire ses tables cibles externes. Si le paramètre `id_generator` n'est pas spécifié, il prend la valeur `DEFAULT` déclarée sur la colonne `id` de la table externe (le cas échéant), sinon celle du générateur canonique dérivé du type de `id`. L'expression enregistrée est utilisée pour générer `id` même si la valeur `DEFAULT` de la table externe change par la suite — voir [la colonne `id`](#id-column) pour plus de détails.

## Modifier les paramètres

Deux paramètres peuvent être modifiés après `CREATE` :

* `id_generator`
* `filter_by_min_time_and_max_time`

```sql theme={null}
ALTER TABLE my_table MODIFY SETTING id_generator = 'sipHash64(tags)';
ALTER TABLE my_table MODIFY SETTING filter_by_min_time_and_max_time = 0;
ALTER TABLE my_table RESET SETTING filter_by_min_time_and_max_time;
```

Notez que la modification de `id_generator` alors que des données sont déjà présentes dans la table Tags peut produire des ID différents pour la même combinaison métrique+tag — les anciennes lignes conservent leurs anciens ID, les nouvelles lignes utilisent le nouveau générateur.

Les autres paramètres ne peuvent pas être modifiés avec `ALTER ... MODIFY SETTING` : la plupart sont figés dans le schéma des tables internes au moment du `CREATE`,
et le paramètre `version` est fixé automatiquement au moment du `CREATE` et identifie le schéma lui-même (voir [versionnement du schéma](#schema-versioning)).

## Paramètres

Voici la liste des paramètres qui peuvent être spécifiés lors de la définition d'une table `TimeSeries` :

| Nom | Type | Par défaut | Description |
| - | - | - | - |
| `id_type` | Type de données | dépend de la colonne `id` | Le type de la colonne `id` des tables cibles. Normalement, le type est déclaré dans les clauses `INNER COLUMNS` des tables internes ou dans une table tags [externe](#external-target-tables) ; le paramètre est enregistré automatiquement lors du `CREATE` si le type n'est pas conservé autrement dans la définition : si la cible tags est une table externe, ou si le paramètre `id_generator` est défini. Le paramètre peut aussi être spécifié explicitement à la place de `TAGS INNER COLUMNS (id <type>)`. Nécessite que `version` soit au moins égale à 2 |
| `id_generator` | Expression | dépend du type de `id` | Expression qui calcule l'identifiant (empreinte) d'une série temporelle à partir de ses tags. Si elle n'est pas définie, l'expression par défaut de la colonne `id` est utilisée. Si l'expression par défaut de la colonne `id` n'est pas définie non plus, l'expression est choisie automatiquement. Pour une table tags externe, le paramètre est enregistré automatiquement lors du `CREATE` si `version` vaut au moins 2 (voir [Tables cibles externes](#external-target-tables)) |
| `tags_to_columns` | Map | {} | Map indiquant quels tags doivent être placés dans des colonnes distinctes de la table [tags](#tags-table). Syntaxe : `{'tag1': 'column1', 'tag2' : column2, ...}` |
| `use_all_tags_column_to_generate_id` | Bool | false | Paramètre obsolète, ne fait rien |
| `store_min_time_and_max_time` | Bool | true | Si la valeur est true, la table stocke `min_time` et `max_time` pour chaque série temporelle |
| `aggregate_min_time_and_max_time` | Bool | true | Lors de la création de la table cible interne `tags`, ce paramètre active l'utilisation de `SimpleAggregateFunction(min, Nullable(DateTime64(3)))` au lieu de `Nullable(DateTime64(3))` comme type de la colonne `min_time`, et de même pour la colonne `max_time` |
| `filter_by_min_time_and_max_time` | Bool | true | Si la valeur est true, la table utilise les colonnes `min_time` et `max_time` pour filtrer les séries temporelles |
| `samples_index_granularity` | UInt64 | 32768 | Définit `index_granularity` de la table interne [samples](#samples-table). Lorsqu'il est défini explicitement, il surcharge `index_granularity` de la déclaration du moteur. Ignoré pour une table samples externe et un moteur autre que MergeTree |
| `recent_samples_ttl_seconds` | UInt64 | 345600 | Durée de conservation de la table cible supplémentaire `échantillon récent`, dans laquelle chaque échantillon inséré est également écrit. Une table interne échantillon récent reçoit toujours le `TTL toDateTime(timestamp) + toIntervalSecond(recent_samples_ttl_seconds)` dérivé de ce paramètre (surchargeant tout TTL de la déclaration du moteur) ; une table externe échantillon récent doit conserver au moins ce nombre de secondes de données. Les requêtes dont l’intervalle de temps tient dans la fenêtre TTL privilégient la table échantillon récent à la table samples principale (voir le paramètre au niveau de la requête `time_series_prefer_recent_samples_table`). La valeur par défaut est de 4 jours ; la valeur effective est figée dans la définition de la table lors de CREATE. Définissez la valeur sur 0 pour désactiver la table échantillon récent |
| `recent_samples_partition_by` | Expression | `toStartOfInterval(toDateTime(timestamp), toIntervalHour(5))` | Clé de partition de la table interne `échantillon récent`, par exemple `toStartOfHour(timestamp)`. Lorsqu'elle est définie explicitement, elle surcharge la clé de partition de la déclaration du moteur ; si aucune des deux n'est définie, une partition par tranche de 5 heures est utilisée. Ignoré pour une table externe échantillon récent. Nécessite que `recent_samples_ttl_seconds` soit différent de zéro |
| `recent_samples_index_granularity` | UInt64 | 8192 | Définit `index_granularity` de la table interne `échantillon récent`. Lorsqu'il est défini explicitement, il surcharge `index_granularity` de la déclaration du moteur. Ignoré pour une table externe échantillon récent et un moteur autre que MergeTree. Nécessite que `recent_samples_ttl_seconds` soit différent de zéro |
| `tags_index_granularity` | UInt64 | 8192 | Définit `index_granularity` de la table interne [tags](#tags-table). Lorsqu'il est défini explicitement, il surcharge `index_granularity` de la déclaration du moteur. Ignoré pour une table tags externe et un moteur autre que MergeTree |
| `version` | UInt64 | 5 | La version de la table : elle identifie l'ensemble des tables cibles et leur structure. La version est figée automatiquement lors de la création d'une table et ne peut pas être modifiée ensuite ; normalement, elle doit être omise dans la requête `CREATE TABLE` (voir [Versionnement du schéma](#schema-versioning)) |

## versionnement du schéma

Le moteur de table `TimeSeries` et la couche d'exécution PromQL sont en cours de développement actif :
l'ensemble des tables cibles et leur structure peuvent évoluer d'une version de ClickHouse à l'autre.
Afin de détecter ces changements, chaque table `TimeSeries` stocke sa version dans le paramètre [version](#settings).
La version est automatiquement inscrite dans la requête `CREATE` lors de la création d'une table ; sa valeur correspond à la dernière version connue du serveur (actuellement 5), elle est
conservée dans les métadonnées de la table et ne peut pas être modifiée par `ALTER`. Les tables créées avant l'introduction du paramètre sont considérées comme étant en version 0.
En règle générale, il suffit d'omettre le paramètre dans la requête `CREATE TABLE` : la table reçoit alors la dernière version.
Une `version` explicite est acceptée si le serveur la prend en charge ; la table est alors définie selon les règles de cette version (voir [Historique des versions](#version-history)).
`CREATE TABLE ... AS other_table` ne copie pas la version de l'autre table, voir [Créer une table AS une table existante](#create-as).

Un serveur prend en charge une plage de versions, et la version minimale peut différer selon qu'il s'agit de lire avec `SELECT`, d'écrire avec `INSERT`,
d'utiliser le protocole remote-write de Prometheus ou d'évaluer des requêtes PromQL (les fonctions de table [prometheusQuery](/fr/reference/functions/table-functions/prometheusQuery),
[prometheusQueryRange](/fr/reference/functions/table-functions/prometheusQueryRange)
et [timeSeriesSelector](/fr/reference/functions/table-functions/timeSeriesSelector), le
dialecte `promql` et l'API HTTP de requêtes de Prometheus) :

* Si la version d'une table `TimeSeries` est trop ancienne pour PromQL, les requêtes PromQL qui l'utilisent sont rejetées. L'exception suggère de recréer la table :
  créez une nouvelle table `TimeSeries`, copiez les données à l'aide d'une requête `INSERT ... SELECT`, puis remplacez l'ancienne table par la nouvelle.
* Si la version est trop ancienne pour permettre l'écriture, les requêtes `INSERT` et le protocole remote-write de Prometheus sont rejetés, tandis que les requêtes `SELECT` continuent de fonctionner.
* Si la version est trop ancienne pour le serveur, toute requête sur la table (à l'exception de `SHOW CREATE TABLE`, `DETACH` et `DROP`) est rejetée.

### Historique des versions

| Version | Changements |
| - | - |
| 0 | Tables créées avant l'introduction du paramètre `version`, y compris les tables « prealpha » (qui déclaraient les colonnes des tables cibles comme [colonnes externes](#outer-columns)) et les tables dépourvues de la table [échantillon récent](#recent-samples-table) |
| 1 | Introduction du paramètre `version` |
| 2 | Introduction du paramètre [`id_type`](#settings) : une table associée à une table des tags externe enregistre le type de la colonne `id` dans `id_type` et l'expression générant les identifiants dans [`id_generator`](#settings), de sorte que sa définition ne dépend pas de la table externe. `id_type` est également enregistré lorsque `id_generator` est défini (voir [La colonne `id`](#id-column)) |
| 3 | La colonne externe `time_series` a été renommée `samples` (voir [Colonnes externes](#outer-columns)). Les tables des versions antérieures conservent l'ancien nom de la colonne, et les fonctions de table [prometheusQuery](/fr/reference/functions/table-functions/prometheusQuery) et [prometheusQueryRange](/fr/reference/functions/table-functions/prometheusQueryRange) renvoient la colonne sous le nom utilisé par la table. Les données stockées restent inchangées |
| 4 | La target table `metrics` a été renommée `famille de métriques` : la table interne est nommée `.inner_id.metricfamilies.<uuid>` au lieu de `.inner_id.metrics.<uuid>`, et la définition utilise le mot-clé `METRIC FAMILIES` au lieu de `METRICS`. Les données stockées restent inchangées |
| 5 | Les nouvelles tables internes de tags utilisant un moteur de la famille `MergeTree` reçoivent par défaut un index de texte `keyValuePairs` sur la map `tags` (voir [Table des tags](#tags-table)) |

# Fonctions

Voici une liste de fonctions qui acceptent une table `TimeSeries` comme argument :

* [timeSeriesSamples](/fr/reference/functions/table-functions/timeSeriesSamples)
* [timeSeriesTags](/fr/reference/functions/table-functions/timeSeriesTags)
* [timeSeriesMetricFamilies](/fr/reference/functions/table-functions/timeSeriesMetricFamilies)
