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

> Documentation du type de données Map dans ClickHouse

# Map(K, V)

Le type de données `Map(K, V)` stocke des paires clé-valeur.

Contrairement à d'autres bases de données, les maps ne sont pas uniques dans ClickHouse, c.-à-d. qu'une map peut contenir deux éléments ayant la même clé.
(Cela s'explique par le fait que les maps sont implémentées en interne sous la forme de `Array(Tuple(K, V))`.)

Vous pouvez utiliser la syntaxe `m[k]` pour obtenir la valeur associée à la clé `k` dans la map `m`.
De plus, `m[k]` parcourt la map, c.-à-d. que le temps d'exécution de l'opération est linéaire en fonction de la taille de la map.

**Paramètres**

* `K` — Le type des clés de la Map. N'importe quel type, à l'exception de [Nullable](/fr/reference/data-types/nullable) et de [LowCardinality](/fr/reference/data-types/lowcardinality) imbriqué avec des types [Nullable](/fr/reference/data-types/nullable).
* `V` — Le type des valeurs de la Map. N'importe quel type.

**Exemples**

Créez une table avec une colonne de type map :

```sql title="Query" theme={null}
CREATE TABLE tab (m Map(String, UInt64)) ENGINE=Memory;
INSERT INTO tab VALUES ({'key1':1, 'key2':10}), ({'key1':2,'key2':20}), ({'key1':3,'key2':30});
```

Pour sélectionner les valeurs de `key2` :

```sql title="Query" theme={null}
SELECT m['key2'] FROM tab;
```

```text title="Response" theme={null}
┌─arrayElement(m, 'key2')─┐
│                      10 │
│                      20 │
│                      30 │
└─────────────────────────┘
```

Si la clé demandée `k` n’est pas présente dans la map, `m[k]` renvoie la valeur par défaut du type de valeur, par exemple `0` pour les types entiers et `''` pour les types String.
Pour vérifier si une clé existe dans une map, vous pouvez utiliser la fonction [mapContains](/fr/reference/functions/regular-functions/tuple-map-functions#mapContainsKey).

```sql title="Query" theme={null}
CREATE TABLE tab (m Map(String, UInt64)) ENGINE=Memory;
INSERT INTO tab VALUES ({'key1':100}), ({});
SELECT m['key1'] FROM tab;
```

```text title="Response" theme={null}
┌─arrayElement(m, 'key1')─┐
│                     100 │
│                       0 │
└─────────────────────────┘
```

<div id="converting-tuple-to-map">
  ## Conversion de Tuple en Map
</div>

Les valeurs de type `Tuple()` peuvent être converties en valeurs de type `Map()` à l’aide de la fonction [CAST](/fr/reference/functions/regular-functions/type-conversion-functions#CAST) :

**Exemple**

```sql title="Query" theme={null}
SELECT CAST(([1, 2, 3], ['Ready', 'Steady', 'Go']), 'Map(UInt8, String)') AS map;
```

```text title="Response" theme={null}
┌─map───────────────────────────┐
│ {1:'Ready',2:'Steady',3:'Go'} │
└───────────────────────────────┘
```

<div id="reading-subcolumns-of-map">
  ## Lire les sous-colonnes d’une Map
</div>

Pour éviter de lire toute la map, vous pouvez utiliser, dans certains cas, les sous-colonnes `keys` et `values`.

**Exemple**

```sql title="Query" theme={null}
CREATE TABLE tab (m Map(String, UInt64)) ENGINE = Memory;
INSERT INTO tab VALUES (map('key1', 1, 'key2', 2, 'key3', 3));

SELECT m.keys FROM tab; --   same as mapKeys(m)
SELECT m.values FROM tab; -- same as mapValues(m)
```

```text title="Response" theme={null}
┌─m.keys─────────────────┐
│ ['key1','key2','key3'] │
└────────────────────────┘

┌─m.values─┐
│ [1,2,3]  │
└──────────┘
```

<div id="bucketed-map-serialization">
  ## Sérialisation des maps par buckets dans MergeTree
</div>

Par défaut, une colonne `Map` dans MergeTree est stockée dans un unique flux `Array(Tuple(K, V))`.
Lire une seule clé avec `m['key']` nécessite de parcourir toute la colonne — chaque paire clé-valeur de chaque ligne — même si une seule clé est nécessaire.
Pour les maps contenant de nombreuses clés distinctes, cela devient un goulot d’étranglement.

La sérialisation par buckets (`with_buckets`) répartit les paires clé-valeur dans plusieurs sous-flux indépendants (buckets) en appliquant un hash à la clé.
Lorsqu’une requête accède à `m['key']`, seul le bucket qui contient cette clé est lu sur le disque, les autres buckets étant ignorés.

<div id="enabling-bucketed-serialization">
  ### Activation de la sérialisation par bucket
</div>

```sql theme={null}
CREATE TABLE tab (id UInt64, m Map(String, UInt64))
ENGINE = MergeTree ORDER BY id
SETTINGS
    map_serialization_version = 'with_buckets',
    max_buckets_in_map = 32,
    map_buckets_strategy = 'sqrt';
```

Pour éviter de ralentir les insertions, vous pouvez conserver la sérialisation `basic` pour les parts de niveau zéro (créées lors de `INSERT`) et n’utiliser `with_buckets` que pour les parts fusionnées :

```sql theme={null}
CREATE TABLE tab (id UInt64, m Map(String, UInt64))
ENGINE = MergeTree ORDER BY id
SETTINGS
    map_serialization_version = 'with_buckets',
    map_serialization_version_for_zero_level_parts = 'basic',
    max_buckets_in_map = 32,
    map_buckets_strategy = 'sqrt';
```

<div id="how-it-works">
  ### Fonctionnement
</div>

Lorsqu'une part de données est écrite avec la sérialisation `with_buckets` :

1. Le nombre moyen de clés par ligne est calculé à partir des statistiques du block.
2. Le nombre de buckets est déterminé par la stratégie configurée (voir [Paramètres](#bucketed-map-settings)).
3. Chaque paire clé-valeur est affectée à un bucket en hachant la clé : `bucket = hash(key) % num_buckets`.
4. Chaque bucket est stocké sous forme de sous-flux indépendant, avec ses propres clés, valeurs et offsets.
5. Un flux de métadonnées `buckets_info` enregistre le nombre de buckets et les statistiques.

Lorsqu'une requête lit une clé spécifique (`m['key']`), l'optimiseur réécrit l'expression en sous-colonne de clé (`m.key_<serialized_key>`).
La couche de sérialisation détermine à quel bucket appartient la clé demandée et ne lit que ce bucket sur le disque.

Lorsque la map complète est lue (par ex. `SELECT m`), tous les buckets sont lus puis réassemblés dans la map d'origine. Cette opération est plus lente qu'avec la sérialisation `basic`, en raison de la surcharge liée à la lecture et à la fusion de plusieurs sous-flux.

<Note>
  L'ordre des clés dans une valeur de map peut différer de l'ordre d'insertion initial lors de l'utilisation de la sérialisation `with_buckets`. Les clés sont réparties entre les buckets par hachage, puis réassemblées dans l'ordre des buckets, et non dans l'ordre d'insertion. Avec la sérialisation `basic`, l'ordre des clés des maps insérées est conservé.
</Note>

Le nombre de buckets peut varier d'une part à l'autre. Lorsque des parts avec des nombres de buckets différents sont fusionnées, le nombre de buckets de la nouvelle part est recalculé à partir des statistiques fusionnées. Des parts avec les sérialisations `basic` et `with_buckets` peuvent coexister dans la même table et être fusionnées de manière transparente.

<div id="bucketed-map-settings">
  ### Paramètres
</div>

| Paramètre                                        | Valeur par défaut | Description                                                                                                                                                                                                                                                                                              |
| ------------------------------------------------ | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `map_serialization_version`                      | `basic`           | Format de sérialisation des colonnes `Map`. `basic` stocke les données dans un seul flux de type tableau. `with_buckets` répartit les clés dans des buckets pour accélérer la lecture d’une clé unique.                                                                                                  |
| `map_serialization_version_for_zero_level_parts` | `basic`           | Format de sérialisation pour les parts de niveau zéro (créées par `INSERT`). Permet de conserver `basic` pour les insertions afin d’éviter la surcharge d’écriture, tandis que les parts fusionnées utilisent `with_buckets`.                                                                            |
| `max_buckets_in_map`                             | `32`              | Limite supérieure du nombre de buckets. Le nombre réel dépend de `map_buckets_strategy`. La valeur maximale autorisée est 256.                                                                                                                                                                           |
| `map_buckets_strategy`                           | `sqrt`            | Stratégie de calcul du nombre de buckets à partir de la taille moyenne de la map : `constant` — utilise toujours `max_buckets_in_map` ; `sqrt` — utilise `round(coefficient * sqrt(avg_size))` ; `linear` — utilise `round(coefficient * avg_size)`. Le résultat est limité à `[1, max_buckets_in_map]`. |
| `map_buckets_coefficient`                        | `1.0`             | Multiplicateur pour les stratégies `sqrt` et `linear`. Ignoré lorsque la stratégie est `constant`.                                                                                                                                                                                                       |
| `map_buckets_min_avg_size`                       | `32`              | Nombre moyen minimal de clés par ligne pour activer le partitionnement en buckets. Si la moyenne est inférieure à ce seuil, un seul bucket est utilisé quels que soient les autres paramètres. Définissez cette valeur sur `0` pour désactiver le seuil.                                                 |

<div id="performance-trade-offs">
  ### Compromis en matière de performances
</div>

Le tableau suivant résume l'impact sur les performances de `with_buckets` par rapport à la sérialisation `basic` pour différentes tailles de Map (de 10 à 10 000 clés par ligne). Le nombre de buckets a été déterminé à l'aide de la stratégie `sqrt`, plafonnée à 32. Les chiffres exacts dépendent des types de clés/valeurs, de la distribution des données et du matériel.

| Opération                                      | 10 clés              | 100 clés             | 1 000 clés           | 10 000 clés          | Remarques                                                                                                                                                                                                                                       |
| ---------------------------------------------- | -------------------- | -------------------- | -------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Recherche d'une seule clé** (`m['key']`)     | 1.6–3.2x plus rapide | 4.5–7.7x plus rapide | 16–39x plus rapide   | 21–49x plus rapide   | Lit un seul bucket au lieu de la colonne entière.                                                                                                                                                                                               |
| **5 recherches de clés**                       | \~1x                 | 1.5–3.1x plus rapide | 2.9–8.3x plus rapide | 4.5–6.7x plus rapide | Chaque clé lit son propre bucket ; certains buckets peuvent se chevaucher.                                                                                                                                                                      |
| **PREWHERE** (`SELECT m WHERE m['key'] = ...`) | 1.5–3.0x plus rapide | 2.9–7.3x plus rapide | 5.3–31x plus rapide  | 20–45x plus rapide   | Le filtre PREWHERE ne lit qu'un seul bucket ; la Map complète n'est lue que pour les lignes correspondantes. Le gain de performance dépend de la sélectivité — moins il y a de granules correspondants, moins il y a d'E/S sur la Map complète. |
| **Parcours complet de la Map** (`SELECT m`)    | \~2x plus lent       | \~2x plus lent       | \~2x plus lent       | \~2x plus lent       | Nécessite de lire et de réassembler tous les buckets.                                                                                                                                                                                           |
| **INSERT**                                     | 1.5–2.5x plus lent   | 1.5–2.5x plus lent   | 1.5–2.5x plus lent   | 1.5–2.5x plus lent   | Surcoût lié au hachage des clés et à l'écriture dans plusieurs sous-flux.                                                                                                                                                                       |

<div id="recommendations">
  ### Recommandations
</div>

* **Petites maps (\< 32 clés en moyenne) :** Conservez la sérialisation `basic`. Le surcoût de la répartition en buckets ne se justifie pas pour les petites maps. La valeur par défaut `map_buckets_min_avg_size = 32` l’impose automatiquement.
* **Maps moyennes (32–100 clés) :** Utilisez `with_buckets` avec la stratégie `sqrt` si les requêtes accèdent fréquemment à des clés individuelles. Le gain de performance est de 4 à 8x pour les recherches sur une seule clé.
* **Grandes maps (100+ clés) :** Utilisez `with_buckets`. Les recherches sur une seule clé sont 16 à 49x plus rapides. Envisagez `map_serialization_version_for_zero_level_parts = 'basic'` pour conserver une vitesse d’insert proche de la référence.
* **Les scans complets de maps dominent la charge de travail :** Conservez `basic`. La sérialisation par buckets ajoute un surcoût d’environ 2x pour les scans complets.
* **Charge de travail mixte (certaines recherches de clé, certains scans complets) :** Utilisez `with_buckets` avec les parts de niveau zéro définies sur `basic`. L’optimisation `PREWHERE` ne lit que le bucket pertinent pour le filtre, puis ne lit la map complète que pour les lignes correspondantes, ce qui se traduit au final par un gain de performance net significatif.

<div id="map-alternatives">
  ### Approches alternatives
</div>

Si la sérialisation `Map` par buckets ne correspond pas à votre cas d'utilisation, il existe deux autres approches pour améliorer les performances d'accès au niveau des clés :

<div id="using-the-json-data-type">
  #### Utilisation du type de données JSON
</div>

Le type de données [JSON](/fr/reference/data-types/newjson) stocke chaque chemin fréquent dans une sous-colonne dynamique distincte. Les chemins qui dépassent la limite `max_dynamic_paths` sont placés dans une [structure de données partagée](/fr/reference/data-types/newjson#shared-data-structure), qui peut utiliser la sérialisation `advanced` pour optimiser la lecture d’un chemin unique. Consultez le [billet de blog](https://clickhouse.com/blog/json-data-type-gets-even-better) pour une présentation détaillée de la sérialisation `advanced`.

| Aspect                           | `Map` with buckets                                                                                          | `JSON`                                                                                                                                                                                                                      |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Lecture d’une seule clé          | Lit un bucket (qui peut contenir d’autres clés). Toutes les paires clé-valeur du bucket sont désérialisées. | Les chemins fréquents sont lus directement depuis les sous-colonnes dynamiques. Les chemins peu fréquents vont dans les données partagées ; avec la sérialisation `advanced`, seules les données du chemin exact sont lues. |
| Types de valeur                  | Toutes les valeurs partagent le même type `V`                                                               | Chaque chemin peut avoir son propre type. Les chemins sans indication de type utilisent `Dynamic`.                                                                                                                          |
| Prise en charge des skip indexes | Fonctionne avec certains types d’index créés sur `mapKeys`/`mapValues`                                      | Les skip indexes ne peuvent être créés que sur des sous-colonnes de chemin spécifiques, et non sur tous les chemins/valeurs à la fois.                                                                                      |
| Lecture de la colonne complète   | Environ 2x plus lent que `basic` en raison du réassemblage des buckets                                      | Surcoût dû à l’encodage du type `Dynamic` et à la reconstruction des chemins.                                                                                                                                               |
| Surcoût de stockage              | Métadonnées supplémentaires minimales                                                                       | Plus élevé en raison de l’encodage du type `Dynamic`, du stockage des noms de chemins et des métadonnées supplémentaires liées à la sérialisation `advanced`.                                                               |
| Flexibilité du schéma            | Types de clé et de valeur fixés lors de la création de la table                                             | Entièrement dynamique — les clés et les types de valeur peuvent varier d’une ligne à l’autre. Des indications de type pour les chemins connus peuvent être déclarées pour un accès direct aux sous-colonnes.                |

Utilisez `JSON` lorsque différentes clés nécessitent différents types de valeur, lorsque l’ensemble des clés varie fortement d’une ligne à l’autre, ou lorsque les clés fréquemment consultées sont connues à l’avance et peuvent être déclarées comme chemins typés pour un accès direct aux sous-colonnes.

<div id="manual-sharding-into-multiple-map-columns">
  #### Partitionnement manuel en plusieurs colonnes Map
</div>

Vous pouvez diviser manuellement une seule `Map` en plusieurs colonnes selon le hachage des clés, au niveau de l’application :

```sql theme={null}
CREATE TABLE tab (
    id UInt64,
    m0 Map(String, UInt64),
    m1 Map(String, UInt64),
    m2 Map(String, UInt64),
    m3 Map(String, UInt64)
) ENGINE = MergeTree ORDER BY id;
```

Lors de l’insertion, acheminez chaque paire clé-valeur vers la colonne `m{hash(key) % 4}`. Lors des requêtes, lisez la colonne correspondante : `m{hash('target_key') % 4}['target_key']`.

| Aspect                  | `Map` avec buckets                                                      | Sharding manuel                                                                                   |
| ----------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Facilité d’utilisation  | Transparent — géré par le moteur de stockage                            | Nécessite une logique de routage au niveau de l’application pour les inserts et les selects       |
| Vertical Merge          | Non pris en charge — tous les buckets appartiennent à une seule colonne | Pris en charge — chaque colonne `Map` est indépendante et peut être fusionnée verticalement       |
| Modifications du schéma | Le nombre de buckets s’adapte automatiquement par part                  | Modifier le nombre de shards nécessite de réécrire les données ou d’ajouter de nouvelles colonnes |
| Syntaxe de requête      | `m['key']` fonctionne directement                                       | Il faut calculer la bonne colonne : `m0['key']`, `m1['key']`, etc.                                |
| Granularité des buckets | Par part, s’adapte aux statistiques des données                         | Fixe à la création de la table                                                                    |

Le sharding manuel est utile lorsque les Vertical Merges sont importantes pour réduire l’utilisation mémoire lors des merges de tables comportant de nombreuses colonnes, ou lorsque le nombre de shards doit être fixe et explicitement contrôlé. Pour la plupart des cas d’usage, la sérialisation automatique par buckets est plus simple et suffisante.

**Voir aussi**

* fonction [map()](/fr/reference/functions/regular-functions/tuple-map-functions#map)
* fonction [CAST()](/fr/reference/functions/regular-functions/type-conversion-functions#CAST)
* [combinateur -Map pour le type de données Map](/fr/reference/functions/aggregate-functions/combinators#-map)

<div id="related-content">
  ## Contenu associé
</div>

* Blog : [Créer une solution d'Observability avec ClickHouse - Partie 2 - Traces](https://clickhouse.com/blog/storing-traces-and-spans-open-telemetry-in-clickhouse)
