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

> Documentação do tipo de dado Map no ClickHouse

# Map(K, V)

O tipo de dado `Map(K, V)` armazena pares chave-valor.

Ao contrário de outros bancos de dados, os maps no ClickHouse não exigem chaves únicas, ou seja, um map pode conter dois elementos com a mesma chave.
(O motivo é que os maps são implementados internamente como `Array(Tuple(K, V))`.)

Você pode usar a sintaxe `m[k]` para obter o valor da chave `k` no map `m`.
Além disso, `m[k]` faz uma varredura no map, ou seja, o runtime da operação é linear em relação ao tamanho do map.

**Parâmetros**

* `K` — O tipo das chaves do Map. Qualquer tipo, exceto [Nullable](/pt-BR/reference/data-types/nullable) e [LowCardinality](/pt-BR/reference/data-types/lowcardinality) aninhado com tipos [Nullable](/pt-BR/reference/data-types/nullable).
* `V` — O tipo dos valores do Map. Qualquer tipo.

**Exemplos**

Crie uma tabela com uma coluna do tipo 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});
```

Para selecionar os valores de `key2`:

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

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

Se a chave solicitada `k` não estiver presente no map, `m[k]` retornará o default value do tipo de valor, por exemplo, `0` para tipos inteiros e `''` para tipos String.
Para verificar se uma chave existe em um map, você pode usar a função [mapContains](/pt-BR/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">
  ## Convertendo Tuple para Map
</div>

Valores do tipo `Tuple()` podem ser convertidos para o tipo `Map()` usando a função [CAST](/pt-BR/reference/functions/regular-functions/type-conversion-functions#CAST):

**Exemplo**

```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">
  ## Leitura das subcolunas de Map
</div>

Para evitar ler o Map inteiro, em alguns casos você pode usar as subcolunas `keys` e `values`.

**Exemplo**

```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; --   mesmo que mapKeys(m)
SELECT m.values FROM tab; -- mesmo que mapValues(m)
```

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

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

<div id="bucketed-map-serialization">
  ## Serialização em buckets de Map no MergeTree
</div>

Por padrão, uma coluna `Map` no MergeTree é armazenada como um único fluxo `Array(Tuple(K, V))`.
Ler uma única chave com `m['key']` exige varrer a coluna inteira — cada par chave-valor de cada linha — mesmo quando apenas uma chave é necessária.
Em maps com muitas chaves distintas, isso se torna um gargalo.

A serialização em buckets (`with_buckets`) divide os pares chave-valor em vários subfluxos independentes (buckets) com base no hash da chave.
Quando uma consulta acessa `m['key']`, apenas o bucket que contém essa chave é lido do disco, ignorando todos os outros buckets.

<div id="enabling-bucketed-serialization">
  ### Habilitando a serialização em buckets
</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';
```

Para evitar deixar as inserções mais lentas, você pode manter a serialização `basic` para as partes de nível zero (criadas durante `INSERT`) e usar `with_buckets` apenas para as partes mescladas:

```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">
  ### Como funciona
</div>

Quando uma parte de dados é gravada com a serialização `with_buckets`:

1. O número médio de chaves por linha é calculado com base nas estatísticas do bloco.
2. O número de buckets é determinado pela estratégia configurada (consulte [Configurações](#bucketed-map-settings)).
3. Cada par chave-valor é atribuído a um bucket por meio do hash da chave: `bucket = hash(key) % num_buckets`.
4. Cada bucket é armazenado como um subfluxo independente com suas próprias chaves, valores e offsets.
5. Um fluxo de metadados `buckets_info` registra a contagem de buckets e as estatísticas.

Quando uma consulta lê uma chave específica (`m['key']`), o otimizador reescreve a expressão para uma subcoluna de chave (`m.key_<serialized_key>`).
A camada de serialização calcula a qual bucket a chave solicitada pertence e lê apenas esse bucket do disco.

Quando o map completo é lido (por exemplo, `SELECT m`), todos os buckets são lidos e remontados no map original. Isso é mais lento do que a serialização `basic` devido à sobrecarga de ler e mesclar vários subfluxos.

<Note>
  A ordem das chaves dentro de um valor de map pode diferir da ordem de inserção original ao usar a serialização `with_buckets`. As chaves são distribuídas entre buckets por hash e remontadas na ordem dos buckets, não na ordem de inserção. Com a serialização `basic`, a ordem das chaves dos maps inseridos é preservada.
</Note>

A contagem de buckets pode variar entre partes. Quando partes com diferentes contagens de buckets são mescladas, a contagem de buckets da nova parte é recalculada com base nas estatísticas mescladas. Partes com serialização `basic` e `with_buckets` podem coexistir na mesma tabela e são mescladas de forma transparente.

<div id="bucketed-map-settings">
  ### Configurações
</div>

| Configuração                                     | Padrão  | Descrição                                                                                                                                                                                                                                                                                           |
| ------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `map_serialization_version`                      | `basic` | Formato de serialização para colunas `Map`. `basic` armazena os dados como um único fluxo de array. `with_buckets` divide as chaves em buckets para leituras mais rápidas de chaves individuais.                                                                                                    |
| `map_serialization_version_for_zero_level_parts` | `basic` | Formato de serialização para partes de nível zero (criadas por `INSERT`). Permite manter `basic` para operações de INSERT a fim de evitar sobrecarga de gravação, enquanto as partes mescladas usam `with_buckets`.                                                                                 |
| `max_buckets_in_map`                             | `32`    | Limite superior para o número de buckets. A quantidade real depende de `map_buckets_strategy`. O valor máximo permitido é 256.                                                                                                                                                                      |
| `map_buckets_strategy`                           | `sqrt`  | Estratégia para calcular a quantidade de buckets com base no tamanho médio do map: `constant` — sempre usa `max_buckets_in_map`; `sqrt` — usa `round(coefficient * sqrt(avg_size))`; `linear` — usa `round(coefficient * avg_size)`. O resultado é limitado ao intervalo `[1, max_buckets_in_map]`. |
| `map_buckets_coefficient`                        | `1.0`   | Multiplicador para as estratégias `sqrt` e `linear`. Ignorado quando a estratégia é `constant`.                                                                                                                                                                                                     |
| `map_buckets_min_avg_size`                       | `32`    | Número médio mínimo de chaves por linha para habilitar o uso de buckets. Se a média ficar abaixo desse limite, um único bucket será usado independentemente das outras configurações. Defina como `0` para desabilitar esse limite.                                                                 |

<div id="performance-trade-offs">
  ### Trade-offs de desempenho
</div>

A tabela a seguir resume o impacto no desempenho de `with_buckets` em comparação com a serialização `basic` em diferentes tamanhos de map (de 10 a 10.000 chaves por linha). A quantidade de buckets foi determinada pela estratégia `sqrt`, limitada a 32. Os números exatos dependem dos tipos de chave/valor, da distribuição dos dados e do hardware.

| Operação                                       | 10 chaves            | 100 chaves           | 1.000 chaves         | 10.000 chaves        | Observações                                                                                                                                                                                                                         |
| ---------------------------------------------- | -------------------- | -------------------- | -------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Busca de uma única chave** (`m['key']`)      | 1.6–3.2x mais rápido | 4.5–7.7x mais rápido | 16–39x mais rápido   | 21–49x mais rápido   | Lê apenas um bucket em vez da coluna inteira.                                                                                                                                                                                       |
| **Busca de 5 chaves**                          | \~1x                 | 1.5–3.1x mais rápido | 2.9–8.3x mais rápido | 4.5–6.7x mais rápido | Cada chave lê seu próprio bucket; alguns buckets podem se sobrepor.                                                                                                                                                                 |
| **PREWHERE** (`SELECT m WHERE m['key'] = ...`) | 1.5–3.0x mais rápido | 2.9–7.3x mais rápido | 5.3–31x mais rápido  | 20–45x mais rápido   | O filtro PREWHERE lê apenas um bucket; a leitura completa do map ocorre apenas para as linhas correspondentes. O ganho de desempenho depende da seletividade — menos grânulos correspondentes significam menos E/S do map completo. |
| **Varredura completa do map** (`SELECT m`)     | \~2x mais lento      | \~2x mais lento      | \~2x mais lento      | \~2x mais lento      | É necessário ler e remontar todos os buckets.                                                                                                                                                                                       |
| **INSERT**                                     | 1.5–2.5x mais lento  | 1.5–2.5x mais lento  | 1.5–2.5x mais lento  | 1.5–2.5x mais lento  | Sobrecarga de aplicar hash às chaves e gravar em vários subfluxos.                                                                                                                                                                  |

<div id="recommendations">
  ### Recomendações
</div>

* **Maps pequenas (\< 32 chaves em média):** Mantenha a serialização `basic`. A sobrecarga da divisão em buckets não se justifica para maps pequenas. O valor padrão `map_buckets_min_avg_size = 32` garante isso automaticamente.
* **Maps médias (32–100 chaves):** Use `with_buckets` com a estratégia `sqrt` se as consultas acessarem chaves individuais com frequência. O ganho de velocidade é de 4–8x para buscas de uma única chave.
* **Maps grandes (100+ chaves):** Use `with_buckets`. Buscas de uma única chave são 16–49x mais rápidos. Considere `map_serialization_version_for_zero_level_parts = 'basic'` para manter a velocidade de inserção próxima à referência.
* **Varreduras completas do map dominam o workload:** Mantenha `basic`. A serialização em buckets adiciona \~2x de sobrecarga para varreduras completas.
* **Workload misto (alguns buscas de chave, algumas varreduras completas):** Use `with_buckets` com as partes de nível zero definidas como `basic`. A otimização `PREWHERE` lê apenas o bucket relevante para o filtro e depois lê o map completo somente para as linhas correspondentes, proporcionando um ganho líquido de velocidade significativo.

<div id="map-alternatives">
  ### Abordagens alternativas
</div>

Se a serialização `Map` em buckets não se adequar ao seu caso de uso, há duas abordagens alternativas para melhorar o desempenho do acesso por chave:

<div id="using-the-json-data-type">
  #### Usando o tipo de dados JSON
</div>

O tipo de dados [JSON](/pt-BR/reference/data-types/newjson) armazena cada caminho frequente como uma subcoluna dinâmica separada. Os caminhos que excedem o limite de `max_dynamic_paths` vão para uma [estrutura de dados compartilhada](/pt-BR/reference/data-types/newjson#shared-data-structure), que pode usar a serialização `advanced` para otimizar leituras de um único caminho. Consulte o [artigo no blog](https://clickhouse.com/blog/json-data-type-gets-even-better) para ter uma visão geral detalhada da serialização `advanced`.

| Aspecto                     | `Map` com buckets                                                                                       | `JSON`                                                                                                                                                                                                  |
| --------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Leitura de uma única chave  | Lê um bucket (que pode conter outras chaves). Todos os pares chave-valor no bucket são desserializados. | Caminhos frequentes são lidos diretamente das subcolunas dinâmicas. Caminhos pouco frequentes vão para dados compartilhados; com a serialização `advanced`, apenas os dados do caminho exato são lidos. |
| Tipos de valor              | Todos os valores compartilham o mesmo tipo `V`                                                          | Cada caminho pode ter seu próprio tipo. Caminhos sem dica de tipo usam `Dynamic`.                                                                                                                       |
| Suporte a índice de salto   | Funciona com alguns tipos de índice criados em `mapKeys`/`mapValues`                                    | Índices de salto só podem ser criados em subcolunas de caminhos específicos, não em todos os caminhos/valores de uma só vez.                                                                            |
| Leitura da coluna completa  | \~2x mais lenta que `basic` devido à remontagem dos buckets                                             | Sobrecarga da codificação do tipo `Dynamic` e da reconstrução dos caminhos.                                                                                                                             |
| Sobrecarga de armazenamento | Metadados adicionais mínimos                                                                            | Maior devido à codificação do tipo `Dynamic`, ao armazenamento dos nomes dos caminhos e aos metadados adicionais na serialização `advanced`.                                                            |
| Flexibilidade de esquema    | Tipos fixos de chave e valor na criação da tabela                                                       | Totalmente dinâmica — chaves e tipos de valor podem variar por linha. Dicas de caminhos tipados podem ser declaradas para caminhos conhecidos, permitindo acesso direto por subcoluna.                  |

Use `JSON` quando chaves diferentes precisarem de tipos de valor diferentes, quando o conjunto de chaves variar significativamente entre as linhas ou quando chaves acessadas com frequência forem conhecidas de antemão e puderem ser declaradas como caminhos tipados para acesso direto por subcoluna.

<div id="manual-sharding-into-multiple-map-columns">
  #### Sharding manual em várias colunas do tipo `Map`
</div>

Você pode dividir manualmente um único `Map` em várias colunas com base no hash da chave, no nível da aplicação:

```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;
```

Durante a inserção, direcione cada par chave-valor para a coluna `m{hash(key) % 4}`. Nas consultas, leia da coluna específica: `m{hash('target_key') % 4}['target_key']`.

| Aspecto                 | `Map` com buckets                                           | Sharding manual                                                                              |
| ----------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| Facilidade de uso       | Transparente — gerenciado pelo mecanismo de armazenamento   | Requer lógica de roteamento no nível da aplicação para inserções e consultas                 |
| Mesclagem vertical      | Não suportada — todos os buckets pertencem a uma coluna     | Suportada — cada coluna `Map` é uma coluna independente e pode passar por mesclagem vertical |
| Alterações de schema    | A quantidade de buckets se adapta automaticamente por parte | Alterar o número de shards exige reescrever os dados ou adicionar novas colunas              |
| Sintaxe de consulta     | `m['key']` funciona diretamente                             | É preciso calcular a coluna correta: `m0['key']`, `m1['key']` etc.                           |
| Granularidade do bucket | Por parte, adapta-se às estatísticas dos dados              | Fixa na criação da tabela                                                                    |

O sharding manual é vantajoso quando as mesclagens verticais são importantes para reduzir o uso de memória durante mesclagens de tabelas com muitas colunas, ou quando o número de shards precisa ser fixo e controlado explicitamente. Para a maioria dos casos de uso, a serialização automática em buckets é mais simples e suficiente.

**Veja também**

* função [map()](/pt-BR/reference/functions/regular-functions/tuple-map-functions#map)
* função [CAST()](/pt-BR/reference/functions/regular-functions/type-conversion-functions#CAST)
* [combinador -Map para o tipo de dado Map](/pt-BR/reference/functions/aggregate-functions/combinators#-map)

<div id="related-content">
  ## Conteúdo relacionado
</div>

* Blog: [Como criar uma solução de observabilidade com ClickHouse - Parte 2 - Traces](https://clickhouse.com/blog/storing-traces-and-spans-open-telemetry-in-clickhouse)
