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

# Trabalhando com o tipo Map no ClickHouse

> Aprenda a usar o tipo Map no ClickHouse para armazenar, consultar e agregar dados dinâmicos no formato chave-valor, usando atributos de recurso do OTel como exemplo prático.

export const e_1 = undefined

export const e_0 = undefined

<a href="/docs/get-started/quickstarts/home" onClick={(e_0) => { e_0.preventDefault(); window.location.href = (window.location.pathname.startsWith('/docs') ? '/docs' : '') + '/get-started/quickstarts/home'; }} className="inline-flex items-center gap-1.5 text-sm text-gray-500 dark:text-zinc-500 hover:text-gray-900 dark:hover:text-[#fdff75] transition-colors font-normal no-underline"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="shrink-0"><path d="M19 12H5" /><path d="M12 19l-7-7 7-7" /></svg>All quickstarts</a>

<div className="mt-2 flex flex-wrap gap-2">
  <Badge size="lg" color="blue">Observabilidade</Badge>
  <Badge size="lg" color="orange">OSS</Badge>
</div>

<div id="prerequisites">
  ## Pré-requisitos
</div>

* **clickhouse-local** instalado na sua máquina. Consulte o [guia de configuração do clickhouse-local](/docs/pt-BR/concepts/features/tools-and-utilities/clickhouse-local) para começar.

<div id="what-youll-build">
  ## O que você vai criar
</div>

No OpenTelemetry, cada span de trace carrega um conjunto de **atributos de recurso** — metadados de chave-valor que descrevem a entidade que produziu a telemetria (nome do serviço, host, região de nuvem, pod do Kubernetes etc.). O conjunto de chaves varia entre serviços e ambientes, o que torna o tipo `Map` do ClickHouse uma escolha natural: as chaves são dinâmicas e específicas da aplicação, mas cada linha normalmente tem apenas algumas delas.

Neste guia de início rápido, você usará o `clickhouse-local` para carregar dados reais de traces do OTel de um arquivo CSV em uma tabela com colunas `Map(LowCardinality(String), String)` e aprenderá a consultar, filtrar, agregar e otimizar dados em map.

<Steps titleSize="h3">
  <Step title="Baixe os dados de exemplo" id="download-the-sample-data">
    O conjunto de dados contém 6.120 trace spans do OTel exportados de um aplicativo de demonstração com microsserviços. Cada linha inclui as colunas `ResourceAttributes` e `SpanAttributes`, que contêm pares chave-valor dinâmicos em maps JSON.
    Salve o arquivo em um diretório fácil de referenciar, por exemplo `~/data/data-otel-traces.csv`.

    <a href="https://clickhouse-docs-assets.s3.us-east-1.amazonaws.com/data-otel-traces.csv" download className="inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium rounded-lg border border-gray-300 dark:border-white/20 bg-white dark:bg-[#1B1B18] text-black dark:text-white hover:border-[#FAFF69] transition-all no-underline mb-4">
      <svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M8 1v10M8 11L4.5 7.5M8 11l3.5-3.5M2 13h12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
      </svg>

      Baixar data-otel-traces.csv (2.9 MB)
    </a>

    Veja como é uma única linha:

    ```response theme={null}
    Timestamp:          2025-12-26 00:00:45.759467000
    TraceId:            0da128e6e3c01bc38b6b43a33e5fa522
    SpanId:             3774f759424e4006
    ParentSpanId:       2fdd1e5b66605098
    SpanName:           orders receive
    SpanKind:           SPAN_KIND_CONSUMER
    ServiceName:        accountingservice
    Duration:           5361
    StatusCode:         STATUS_CODE_UNSET
    ResourceAttributes: {"host.name":"f19476836e47","os.type":"linux","process.pid":"1","process.command_args":"[\"./accountingservice\"]","process.executable.path":"...
    SpanAttributes:     {"network.transport":"tcp","messaging.destination.name":"orders","messaging.kafka.message.offset":"232260","messaging.message.body.size":"216"...
    ```
  </Step>

  <Step title="Crie a tabela e carregue os dados" id="create-the-table-and-load-the-data">
    Inicie o `clickhouse-local` e crie a tabela a seguir com um esquema correspondente ao CSV.
    A coluna-chave é `ResourceAttributes Map(LowCardinality(String), String)` - usando `LowCardinality` no tipo da chave porque as chaves de atributo do OTel vêm de um conjunto relativamente pequeno e recorrente.

    ```sql highlight={12} theme={null}
    CREATE TABLE otel_traces
    (
        Timestamp          DateTime64(9),
        TraceId            String,
        SpanId             String,
        ParentSpanId       String,
        SpanName           LowCardinality(String),
        SpanKind           LowCardinality(String),
        ServiceName        LowCardinality(String),
        Duration           UInt64,
        StatusCode         LowCardinality(String),
        ResourceAttributes Map(LowCardinality(String), String),
        SpanAttributes     Map(LowCardinality(String), String)
    )
    ENGINE = MergeTree()
    ORDER BY (ServiceName, SpanName, toUnixTimestamp(Timestamp));
    ```

    Agora, carregue o CSV usando o engine de tabela `file`. Ajuste o caminho para o local em que você salvou o arquivo:

    ```sql theme={null}
    INSERT INTO otel_traces
    SELECT * FROM file('~/data/data-otel-traces.csv', CSVWithNames);
    ```

    Confirme se os dados foram carregados:

    ```sql theme={null}
    SELECT count() FROM otel_traces;
    ```

    Você verá 6.120 linhas.
  </Step>

  <Step title="Consulte os dados" id="query-the-data">
    **Acesse uma chave específica** — use a sintaxe de colchetes para obter um valor do map. Se a chave não existir em uma determinada linha, você receberá o valor padrão do tipo do valor (string vazia para `String`):

    ```sql theme={null}
    SELECT
        ServiceName,
        SpanName,
        ResourceAttributes['host.name']             AS host,
        ResourceAttributes['k8s.pod.name']          AS pod,
        ResourceAttributes['deployment.environment'] AS env
    FROM otel_traces
    LIMIT 10;
    ```

    **Filtre por um valor de `map`** — encontre todos os spans com um nome de serviço específico:

    ```sql theme={null}
    SELECT
        Timestamp,
        SpanName,
        Duration / 1e6 AS duration_ms
    FROM otel_traces
    WHERE ResourceAttributes['service.name'] = 'cartservice'
    ORDER BY Timestamp
    LIMIT 10;
    ```

    **Verifique se uma chave está presente** — nem todo span tem metadados do Kubernetes. Use `mapContains` para descobrir quais têm:

    ```sql theme={null}
    SELECT
        ServiceName,
        SpanName,
        mapContains(ResourceAttributes, 'k8s.node.name') AS has_node_info
    FROM otel_traces
    LIMIT 10;
    ```

    **Inspecione todas as chaves presentes no conjunto de dados** — útil para entender o que a instrumentação está produzindo:

    ```sql theme={null}
    SELECT DISTINCT arrayJoin(mapKeys(ResourceAttributes)) AS key
    FROM otel_traces
    ORDER BY key;
    ```

    **Desdobre um map em linhas com ARRAY JOIN** — transforme cada par chave-valor em uma linha própria, útil para criar inventários de atributos ou alimentar dashboards:

    ```sql theme={null}
    SELECT
        ServiceName,
        key,
        value
    FROM otel_traces
    ARRAY JOIN
        mapKeys(ResourceAttributes)  AS key,
        mapValues(ResourceAttributes) AS value
    WHERE ServiceName = 'cartservice'
    LIMIT 20;
    ```

    **Filtre maps com mapFilter** â extraia apenas os atributos do Kubernetes de cada span:

    ```sql theme={null}
    SELECT
        ServiceName,
        mapFilter((k, v) -> k LIKE 'k8s.%', ResourceAttributes) AS k8s_attrs
    FROM otel_traces
    WHERE mapContains(ResourceAttributes, 'k8s.pod.name')
    LIMIT 10;
    ```

    **Encontre spans com erro e seu contexto de recurso** — combine filtros de coluna comuns com acesso a `map`:

    ```sql theme={null}
    SELECT
        Timestamp,
        ServiceName,
        SpanName,
        ResourceAttributes['host.name']    AS host,
        ResourceAttributes['k8s.pod.name'] AS pod,
        SpanAttributes['error.type']       AS error_type,
        SpanAttributes['error.message']    AS error_message
    FROM otel_traces
    WHERE StatusCode = 'STATUS_CODE_ERROR';
    ```
  </Step>

  <Step title="Agregação em maps com o combinador -Map" id="aggregate-across-maps-with-the--map-combinator">
    O combinador de agregação `-Map` do ClickHouse permite aplicar qualquer função de agregação a uma coluna `Map` e fazer com que ela opere em cada chave de forma independente. O resultado também é um `Map` — uma entrada por chave, com o valor agregado. Isso é especialmente útil para métricas OTel, em que counters ou gauges são armazenados como valores de map.

    Para demonstrar, crie uma pequena tabela de métricas em que cada linha registra contagens de códigos de status HTTP como um `Map(String, UInt64)`:

    ```sql theme={null}
    CREATE TABLE otel_http_status_counts
    (
        Timestamp    DateTime,
        ServiceName  LowCardinality(String),
        StatusCounts Map(String, UInt64)
    )
    ENGINE = MergeTree()
    ORDER BY (ServiceName, Timestamp);

    INSERT INTO otel_http_status_counts VALUES
        ('2025-12-26 10:00:00', 'cart-service',      {'2xx': 150, '4xx': 12, '5xx': 3}),
        ('2025-12-26 10:01:00', 'cart-service',      {'2xx': 200, '4xx': 8,  '5xx': 1}),
        ('2025-12-26 10:00:00', 'inventory-service', {'2xx': 90,  '4xx': 5}),
        ('2025-12-26 10:01:00', 'inventory-service', {'2xx': 110, '4xx': 3,  '5xx': 2}),
        ('2025-12-26 10:00:00', 'payment-service',   {'2xx': 50,  '5xx': 10}),
        ('2025-12-26 10:01:00', 'payment-service',   {'2xx': 45,  '4xx': 2,  '5xx': 15});
    ```

    Agora use `sumMap` para somar as contagens por código de status de cada serviço:

    ```sql theme={null}
    SELECT
        ServiceName,
        sumMap(StatusCounts) AS total_by_status
    FROM otel_http_status_counts
    GROUP BY ServiceName;
    ```

    O sufixo `-Map` funciona com qualquer função de agregação, então você pode usar `minMap`, `maxMap` ou `avgMap` com a mesma facilidade:

    ```sql theme={null}
    SELECT
        ServiceName,
        avgMap(StatusCounts) AS avg_by_status,
        maxMap(StatusCounts) AS peak_by_status
    FROM otel_http_status_counts
    GROUP BY ServiceName;
    ```

    Você também pode combiná-lo com outros combinadores. Por exemplo, `sumMapIf` permite fazer agregações condicionais — aqui, somando apenas as janelas de um minuto em que o serviço já apresentava erros:

    ```sql theme={null}
    SELECT
        ServiceName,
        sumMapIf(StatusCounts, StatusCounts['5xx'] > 0) AS totals_in_error_windows
    FROM otel_http_status_counts
    GROUP BY ServiceName;
    ```

    **Por que isso é importante para OTel:** Quando seu OTel Collector grava, no ClickHouse, a discriminação por minuto dos códigos de status, `sumMap` permite consolidá-la em totais por hora ou por dia em uma única consulta — sem `ARRAY JOIN`, sem fazer unpivot, sem precisar conhecer de antemão o conjunto completo de chaves. Qualquer chave que apareça em alguma linha é incluída automaticamente no resultado.
  </Step>

  <Step title="Otimize para chaves usadas com frequência em consultas" id="optimise-for-frequently-queried-keys">
    Se você perceber que está sempre filtrando pela mesma chave do map — `host.name` é um exemplo comum —, pode extraí-la para uma coluna materializada. Assim, você evita a varredura linear no map a cada consulta:

    ```sql theme={null}
    ALTER TABLE otel_traces
        ADD COLUMN HostName String
        MATERIALIZED ResourceAttributes['host.name'];
    ```

    Para os dados existentes, faça o backfill da coluna:

    ```sql theme={null}
    ALTER TABLE otel_traces MATERIALIZE COLUMN HostName;
    ```

    Agora, `WHERE HostName = 'prod-cart-01'` lê uma única coluna dedicada, em vez do map inteiro. Esse é o padrão recomendado no schema do ClickHouse para OTel para qualquer atributo consultado com frequência.
  </Step>
</Steps>

<div id="key-takeaways">
  ## Principais conclusões
</div>

* **`Map(LowCardinality(String), String)`** é o tipo idiomático para atributos do OTel — flexível o bastante para lidar com conjuntos de chaves variáveis, e `LowCardinality` mantém o armazenamento dessas chaves eficiente.
* **A sintaxe com colchetes** (`map['key']`) é a forma mais comum de acessar valores, mas lembre-se de que ela faz uma varredura linear — funciona bem para maps com dezenas de chaves, mas não é ideal para centenas.
* **Colunas materializadas** são a saída: quando uma chave do map se torna um alvo frequente de filtro, promova-a a uma coluna real para ter acesso indexado e colunar.
* **`mapContains`, `mapKeys`, `mapValues`, `mapFilter`** e `ARRAY JOIN` oferecem um conjunto poderoso de ferramentas para explorar e transformar dados de map sem sair do SQL.
* **O combinador de agregação `-Map`** (`sumMap`, `avgMap`, `maxMap`, etc.) agrega cada chave de forma independente em todas as linhas — ideal para consolidar contadores de métricas do OTel sem precisar conhecer o conjunto de chaves com antecedência. Ele também se combina com outros combinadores (por exemplo, `sumMapIf`).

<div id="next-steps">
  ## Próximos passos
</div>

Confira estes guias de início rápido:

* [Crie sua primeira tabela MergeTree](/docs/pt-BR/get-started/quickstarts/create-your-first-mergetree-table)
* [Crie sua primeira visão materializada](/docs/pt-BR/get-started/quickstarts/create-your-first-materialized-view)
* [Problemas comuns ao dar os primeiros passos](/docs/pt-BR/get-started/quickstarts/home)

Ou aprofunde-se com a documentação de referência:

* [Referência do tipo map](/docs/pt-BR/reference/data-types/map)
* [Exportador OTel do ClickHouse](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/clickhouseexporter)
* [Combinadores de funções de agregação](/docs/pt-BR/reference/functions/aggregate-functions/combinators)

<Frame caption="Check out the ClickHouse academy for on-demand and live training">
  <a href="https://learn.clickhouse.com/" target="_blank">
    <img src="https://mintcdn.com/private-7c7dfe99/EDr8ydtGBgFPOQea/images/academy.webp?fit=max&auto=format&n=EDr8ydtGBgFPOQea&q=85&s=27e92fc656183cc2f176211907a7aa49" alt="ClickHouse Academy — Master ClickHouse with expert-designed training for every skill level" width="560" noZoom data-path="images/academy.webp" />
  </a>
</Frame>

<div className="mt-8">
  <a href="/docs/get-started/quickstarts/home" onClick={(e_1) => { e_1.preventDefault(); window.location.href = (window.location.pathname.startsWith('/docs') ? '/docs' : '') + '/get-started/quickstarts/home'; }} className="inline-flex items-center gap-1.5 text-sm text-gray-500 dark:text-zinc-500 hover:text-gray-900 dark:hover:text-[#fdff75] transition-colors font-normal no-underline"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="shrink-0"><path d="M19 12H5" /><path d="M12 19l-7-7 7-7" /></svg>All quickstarts</a>
</div>
