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

> Os motores de tabela da família `MergeTree` foram projetados para altas taxas de ingestão de dados e grandes volumes de dados.

# Motor de tabela MergeTree

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

        </div>
            Sem suporte no ClickHouse Cloud
        </div>;
};

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

O motor `MergeTree` e outros motores da família `MergeTree` (por exemplo, `ReplacingMergeTree`, `AggregatingMergeTree`) são os motores de tabela mais usados e mais robustos no ClickHouse.

Os motores de tabela da família `MergeTree` foram projetados para altas taxas de ingestão de dados e volumes massivos de dados.
As operações de inserção criam partes de tabela, que são mescladas por um processo em segundo plano com outras partes de tabela.

Principais recursos dos motores de tabela da família `MergeTree`.

* A chave primária da tabela determina a ordem de classificação dentro de cada parte de tabela (índice clusterizado). A chave primária também não faz referência a linhas individuais, mas a blocos de 8192 linhas chamados grânulos. Isso torna as chaves primárias de grandes conjuntos de dados pequenas o suficiente para permanecerem carregadas na memória principal, ao mesmo tempo em que ainda fornecem acesso rápido aos dados em disco.

* As tabelas podem ser particionadas usando uma expressão de partição arbitrária. O partition pruning garante que as partições sejam excluídas da leitura quando a consulta permitir.

* Os dados podem ser replicados em vários nós do cluster para alta disponibilidade, failover e upgrades sem indisponibilidade. Consulte [Replicação de dados](/docs/pt-BR/reference/engines/table-engines/mergetree-family/replication).

* Os motores de tabela `MergeTree` oferecem suporte a vários tipos de estatísticas e métodos de amostragem para ajudar na otimização de consultas.

<Note>
  Apesar do nome semelhante, o motor [Merge](/docs/pt-BR/reference/engines/table-engines/special/merge) é diferente dos motores `*MergeTree`.
</Note>

<div id="table_engine-mergetree-creating-a-table">
  ## Criando tabelas
</div>

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

Para uma descrição detalhada dos parâmetros, consulte a instrução [CREATE TABLE](/docs/pt-BR/reference/statements/create/table)

<div id="mergetree-query-clauses">
  ### Cláusulas de consulta
</div>

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

`ENGINE` — Nome e parâmetros do motor. `ENGINE = MergeTree()`. O motor `MergeTree` não tem parâmetros.

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

`ORDER BY` — A chave de ordenação.

Uma tupla de nomes de colunas ou expressões arbitrárias. Exemplo: `ORDER BY (CounterID + 1, EventDate)`.

Se nenhuma chave primária estiver definida (ou seja, `PRIMARY KEY` não tiver sido especificada), ClickHouse usa a chave de ordenação como chave primária.

Se nenhuma ordenação for necessária, você pode usar a sintaxe `ORDER BY tuple()`.
Como alternativa, se a configuração `create_table_empty_primary_key_by_default` estiver habilitada, `ORDER BY ()` é adicionado implicitamente às instruções `CREATE TABLE`. Consulte [Selecionando uma chave primária](#selecting-a-primary-key).

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

`PARTITION BY` — A [chave de particionamento](/docs/pt-BR/reference/engines/table-engines/mergetree-family/custom-partitioning-key). Opcional. Na maioria dos casos, você não precisa de uma chave de particionamento e, se precisar particionar, em geral não precisa de uma chave de particionamento com granularidade maior do que por mês. O particionamento não acelera as consultas (ao contrário da expressão ORDER BY). Nunca use um particionamento granular demais. Não particione seus dados por identificadores ou nomes de clientes (em vez disso, use o identificador ou nome do cliente como a primeira coluna na expressão ORDER BY).

Para particionar por mês, use a expressão `toYYYYMM(date_column)`, em que `date_column` é uma coluna com uma data do tipo [Date](/docs/pt-BR/reference/data-types/date). Os nomes das partições aqui têm o formato `"YYYYMM"`.

<div id="primary-key">
  #### CHAVE PRIMÁRIA
</div>

`PRIMARY KEY` — A chave primária, se [for diferente da chave de ordenação](#choosing-a-primary-key-that-differs-from-the-sorting-key). Opcional.

Especificar uma chave de ordenação (usando a cláusula `ORDER BY`) implica especificar também uma chave primária.
Em geral, não é necessário especificar a chave primária além da chave de ordenação.

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

`SAMPLE BY` — Uma expressão de amostragem. Opcional.

Se for especificada, deve estar contida na chave primária.
A expressão de amostragem deve resultar em um inteiro sem sinal.

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

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

`TTL` — Uma lista de regras que especificam a duração do armazenamento das linhas e a lógica de movimentação automática de partes [entre disks e volumes](#table_engine-mergetree-multiple-volumes). Opcional.

A expressão deve resultar em um `Date` ou `DateTime`, por exemplo, `TTL date + INTERVAL 1 DAY`.

O tipo de regra `DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'|GROUP BY` especifica uma ação a ser executada com a parte se a expressão for satisfeita (atingir o momento atual): remoção de linhas expiradas, movimentação de uma parte (se a expressão for satisfeita para todas as linhas de uma parte) para o disk especificado (`TO DISK 'xxx'`) ou para o volume (`TO VOLUME 'xxx'`), ou agregação de valores nas linhas expiradas. O tipo padrão da regra é remoção (`DELETE`). É possível especificar uma lista com várias regras, mas não deve haver mais de uma regra `DELETE`.

Para mais detalhes, consulte [TTL para colunas e tabelas](#table_engine-mergetree-ttl)

<div id="settings">
  #### CONFIGURAÇÕES
</div>

Consulte [Configurações do MergeTree](/docs/pt-BR/reference/settings/merge-tree-settings).

**Exemplo da configuração Sections**

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

No exemplo, definimos o particionamento por mês.

Também definimos uma expressão de amostragem como um hash do ID do usuário. Isso permite pseudorrandomizar os dados na tabela para cada `CounterID` e `EventDate`. Se você definir uma cláusula [SAMPLE](/docs/pt-BR/reference/statements/select/sample) ao selecionar os dados, o ClickHouse retornará uma amostra de dados pseudorrandômica uniforme para um subconjunto de usuários.

A configuração `index_granularity` pode ser omitida, porque 8192 é o valor padrão.

<details markdown="1">
  <summary>Método obsoleto para criar uma tabela</summary>

  <Note>
    Não use este método em novos projetos. Se possível, migre os projetos antigos para o método descrito acima.
  </Note>

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

  **Parâmetros de MergeTree()**

  * `date-column` — O nome de uma coluna do tipo [Date](/docs/pt-BR/reference/data-types/date). O ClickHouse cria automaticamente partições por mês com base nessa coluna. Os nomes das partições ficam no formato `"YYYYMM"`.
  * `sampling_expression` — Uma expressão de amostragem.
  * `(primary, key)` — Chave primária. Tipo: [Tuple()](/docs/pt-BR/reference/data-types/tuple)
  * `index_granularity` — A granularidade de um índice. O número de linhas de dados entre as "marcas" de um índice. O valor 8192 é adequado para a maioria das tarefas.

  **Exemplo**

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

  O motor `MergeTree` é configurado da mesma forma que no exemplo acima, no método principal de configuração do motor.
</details>

<div id="mergetree-data-storage">
  ## Armazenamento de dados
</div>

Uma tabela consiste em partes de dados ordenadas pela chave primária.

Quando dados são inseridos em uma tabela, partes de dados separadas são criadas, e cada uma delas é ordenada lexicograficamente pela chave primária. Por exemplo, se a chave primária for `(CounterID, Date)`, os dados da parte serão ordenados por `CounterID` e, dentro de cada `CounterID`, por `Date`.

Os dados pertencentes a partições diferentes são separados em partes diferentes. Em segundo plano, o ClickHouse mescla as partes de dados para tornar o armazenamento mais eficiente. Partes pertencentes a partições diferentes não são mescladas. O mecanismo de mesclagem não garante que todas as linhas com a mesma chave primária fiquem na mesma parte de dados.

As partes de dados podem ser armazenadas no formato `Wide` ou `Compact`. No formato `Wide`, cada coluna é armazenada em um arquivo separado em um sistema de arquivos; no formato `Compact`, todas as colunas são armazenadas em um único arquivo. O formato `Compact` pode ser usado para aumentar o desempenho de inserções pequenas e frequentes.

O formato de armazenamento dos dados é controlado pelas configurações `min_bytes_for_wide_part` e `min_rows_for_wide_part` do mecanismo da tabela. Se o número de bytes ou de linhas em uma parte de dados for menor que o valor da configuração correspondente, a parte será armazenada no formato `Compact`. Caso contrário, será armazenada no formato `Wide`. Se nenhuma dessas configurações estiver definida, as partes de dados serão armazenadas no formato `Wide`.

Cada parte de dados é dividida logicamente em grânulos. Um grânulo é o menor conjunto de dados indivisível que o ClickHouse lê ao selecionar dados. O ClickHouse não divide linhas nem valores, portanto cada grânulo sempre contém um número inteiro de linhas. A primeira linha de um grânulo é marcada com o valor da chave primária dessa linha. Para cada parte de dados, o ClickHouse cria um arquivo de índice que armazena as marcas. Para cada coluna, esteja ela na chave primária ou não, o ClickHouse também armazena as mesmas marcas. Essas marcas permitem localizar dados diretamente nos arquivos de coluna.

O tamanho do grânulo é limitado pelas configurações `index_granularity` e `index_granularity_bytes` do mecanismo da tabela. O número de linhas em um grânulo fica no intervalo `[1, index_granularity]`, dependendo do tamanho das linhas. O tamanho de um grânulo pode exceder `index_granularity_bytes` se o tamanho de uma única linha for maior que o valor da configuração. Nesse caso, o tamanho do grânulo é igual ao tamanho da linha.

<div id="primary-keys-and-indexes-in-queries">
  ## Chaves primárias e índices nas consultas
</div>

Considere a chave primária `(CounterID, Date)` como exemplo. Nesse caso, a ordenação e o índice podem ser ilustrados da seguinte forma:

```text theme={null}
Dados completos: [---------------------------------------------]
CounterID:      [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll]
Date:           [1111111222222233331233211111222222333211111112122222223111112223311122333]
Marcas:          |      |      |      |      |      |      |      |      |      |      |
                a,1    a,2    a,3    b,3    e,2    e,3    g,1    h,2    i,1    i,3    l,3
Números de marcas: 0      1      2      3      4      5      6      7      8      9      10
```

Se a consulta de dados especificar:

* `CounterID in ('a', 'h')`, o servidor lê os dados nos intervalos de marcas `[0, 3)` e `[6, 8)`.
* `CounterID IN ('a', 'h') AND Date = 3`, o servidor lê os dados nos intervalos de marcas `[1, 3)` e `[7, 8)`.
* `Date = 3`, o servidor lê os dados no intervalo de marcas `[1, 10]`.

Os exemplos acima mostram que usar um índice é sempre mais eficiente do que fazer uma varredura completa.

Um índice esparso permite que dados adicionais sejam lidos. Ao ler um único intervalo da chave primária, até `index_granularity * 2` linhas adicionais em cada bloco de dados podem ser lidas.

Índices esparsos permitem trabalhar com um número muito grande de linhas na tabela porque, na maioria dos casos, esses índices cabem na RAM do computador.

O ClickHouse não exige uma chave primária exclusiva. Você pode inserir várias linhas com a mesma chave primária.

Você pode usar expressões do tipo `Nullable` nas cláusulas `PRIMARY KEY` e `ORDER BY`, mas isso é fortemente desaconselhado. Para permitir esse recurso, ative a configuração [allow\_nullable\_key](/docs/pt-BR/reference/settings/merge-tree-settings#allow_nullable_key). O princípio [NULLS\_LAST](/docs/pt-BR/reference/statements/select/order-by#sorting-of-special-values) se aplica aos valores `NULL` na cláusula `ORDER BY`.

<div id="selecting-a-primary-key">
  ### Selecionando uma chave primária
</div>

O número de colunas na chave primária não é explicitamente limitado. Dependendo da estrutura dos dados, você pode incluir mais ou menos colunas na chave primária. Isso pode:

* Melhorar o desempenho de um índice.

  Se a chave primária for `(a, b)`, adicionar outra coluna `c` melhorará o desempenho se as seguintes condições forem atendidas:

  * Houver consultas com uma condição na coluna `c`.
  * Forem comuns intervalos longos de dados (várias vezes maiores que a `index_granularity`) com valores idênticos para `(a, b)`. Em outras palavras, quando adicionar outra coluna permite pular intervalos de dados bastante longos.

* Melhorar a compressão dos dados.

  O ClickHouse ordena os dados pela chave primária, portanto, quanto maior a consistência, melhor a compressão.

* Fornecer lógica adicional ao mesclar partes de dados nos motores [CollapsingMergeTree](/docs/pt-BR/reference/engines/table-engines/mergetree-family/collapsingmergetree) e [SummingMergeTree](/docs/pt-BR/reference/engines/table-engines/mergetree-family/summingmergetree).

  Nesse caso, faz sentido especificar a *chave de ordenação* diferente da chave primária.

Uma chave primária longa afetará negativamente o desempenho das inserções e o consumo de memória, mas colunas extras na chave primária não afetam o desempenho do ClickHouse durante consultas `SELECT`.

Você pode criar uma tabela sem chave primária usando a sintaxe `ORDER BY tuple()`. Nesse caso, o ClickHouse armazena os dados na ordem de inserção. Se você quiser preservar a ordem dos dados ao inseri-los com consultas `INSERT ... SELECT`, defina [max\_insert\_threads = 1](/docs/pt-BR/reference/settings/session-settings#max_insert_threads).

Para selecionar dados na ordem original, use consultas `SELECT` [single-threaded](/docs/pt-BR/reference/settings/session-settings#max_threads).

<div id="choosing-a-primary-key-that-differs-from-the-sorting-key">
  ### Escolhendo uma chave primária diferente da chave de ordenação
</div>

É possível especificar uma chave primária (uma expressão com valores gravados no arquivo de índice para cada marca) diferente da chave de ordenação (uma expressão usada para ordenar as linhas nas partes de dados). Nesse caso, a tupla de expressão da chave primária deve ser um prefixo da tupla de expressão da chave de ordenação.

Esse recurso é útil ao usar os motores de tabela [SummingMergeTree](/docs/pt-BR/reference/engines/table-engines/mergetree-family/summingmergetree) e
[AggregatingMergeTree](/docs/pt-BR/reference/engines/table-engines/mergetree-family/aggregatingmergetree). Em um caso comum de uso desses motores, a tabela tem dois tipos de colunas: *dimensões* e *medidas*. As consultas típicas agregam os valores das colunas de medida com `GROUP BY` arbitrário e filtragem por dimensões. Como SummingMergeTree e AggregatingMergeTree agregam linhas com o mesmo valor da chave de ordenação, é natural adicionar todas as dimensões a ela. Como resultado, a expressão de chave passa a consistir em uma longa lista de colunas, e essa lista precisa ser atualizada com frequência à medida que novas dimensões são adicionadas.

Nesse caso, faz sentido manter apenas algumas colunas na chave primária para garantir varreduras de intervalo eficientes e adicionar as colunas de dimensão restantes à tupla da chave de ordenação.

O [ALTER](/docs/pt-BR/reference/statements/alter/index) da chave de ordenação é uma operação leve porque, quando uma nova coluna é adicionada simultaneamente à tabela e à chave de ordenação, as partes de dados existentes não precisam ser alteradas. Como a chave de ordenação antiga é um prefixo da nova chave de ordenação e não há dados na coluna recém-adicionada, os dados ficam ordenados tanto pela chave de ordenação antiga quanto pela nova no momento da modificação da tabela.

<div id="use-of-indexes-and-partitions-in-queries">
  ### Uso de índices e partições em consultas
</div>

Para consultas `SELECT`, o ClickHouse analisa se um índice pode ser usado. Um índice pode ser usado se a cláusula `WHERE/PREWHERE` contiver uma expressão (como um dos elementos da conjunção ou por completo) que represente uma operação de comparação de igualdade ou desigualdade, ou se contiver `IN` ou `LIKE` com um prefixo fixo em colunas ou expressões que façam parte da chave primária ou da chave de particionamento, ou em determinadas funções parcialmente repetitivas dessas colunas, ou em relações lógicas entre essas expressões.

Assim, é possível executar rapidamente consultas em um ou vários intervalos da chave primária. Neste exemplo, as consultas serão rápidas quando executadas para uma tag de rastreamento específica, para uma tag específica e um intervalo de datas, para uma tag específica e uma data, para várias tags com um intervalo de datas e assim por diante.

Vamos analisar o motor configurado da seguinte forma:

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

Nesse caso, nas consultas:

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

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

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

O ClickHouse usará o índice da chave primária para descartar dados fora do critério e a chave de particionamento mensal para descartar partições que estejam fora dos intervalos de datas adequados.

As consultas acima mostram que o índice é usado mesmo para expressões complexas. A leitura da tabela é organizada de forma que usar o índice não possa ser mais lento do que uma varredura completa.

No exemplo abaixo, o índice não pode ser usado.

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

Para verificar se o ClickHouse pode usar o índice ao executar uma consulta, use as configurações [force\_index\_by\_date](/docs/pt-BR/reference/settings/session-settings#force_index_by_date) e [force\_primary\_key](/docs/pt-BR/reference/settings/session-settings#force_primary_key).

A chave de partição por mês permite ler apenas os blocos de dados que contêm datas dentro do intervalo correto. Nesse caso, o bloco de dados pode conter dados de várias datas (até um mês inteiro). Dentro de um bloco, os dados são ordenados pela chave primária, que talvez não tenha a data como primeira coluna. Por isso, usar uma consulta com apenas uma condição de data, sem especificar o prefixo da chave primária, fará com que mais dados sejam lidos do que no caso de uma única data.

<div id="use-of-index-for-deterministic-expressions-in-primary-keys">
  ### Uso do índice para expressões determinísticas em chaves primárias
</div>

A chave primária pode conter expressões, não apenas nomes de colunas. Essas expressões não se limitam a cadeias simples de funções: elas podem ser árvores de expressões arbitrárias (por exemplo, funções aninhadas e expressões compostas), desde que sejam determinísticas.

Uma expressão é **determinística** se sempre retorna o mesmo resultado para os mesmos valores de entrada (por exemplo: `length()`, `toDate()`, `lower()`, `left()`, `cityHash64()`, `toUUID()`; ao contrário de `now()` ou `rand()`). Se a chave primária contiver expressões determinísticas, o ClickHouse poderá aplicá-las a valores constantes da consulta e usar o resultado para criar condições no índice da chave primária. Isso permite ignorar dados para predicados como `=`, `IN` e `has`.

Um caso de uso comum é manter a chave primária compacta (por exemplo, armazenar um hash em vez de uma `String` longa), ao mesmo tempo em que se permite que predicados na coluna original usem o índice.

Exemplo de uma chave primária determinística (mas não injetiva):

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

Exemplos de predicados que podem usar o índice:

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

Nesses casos, o ClickHouse calcula `length('alice')` (e outras constantes) uma vez e usa os valores de comprimento para restringir os intervalos no índice da chave primária. Como o comprimento de uma string **não é injetivo**, diferentes strings `user_id` podem ter o mesmo comprimento, então o índice pode ler grânulos extras (falsos positivos). O resultado permanece correto porque o predicado original (`user_id = ...`, `IN` etc.) ainda é aplicado após a leitura.

Se a expressão determinística também for **injetiva** (entradas diferentes não podem produzir a mesma saída para os tipos de argumento usados), o ClickHouse também pode usar o índice de forma eficaz para as formas negadas: `!=`, `NOT IN` e `NOT has(...)`. Por exemplo, `reverse(p)` e `hex(p)` são injetivas para `String`.

Exemplo de uma chave primária injetiva:

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

Expressões injetivas mais complexas também são suportadas, por exemplo:

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

Exemplos de predicados que podem usar o índice:

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

<div id="use-of-index-for-partially-monotonic-primary-keys">
  ### Uso de índice para chaves primárias parcialmente monotônicas
</div>

Considere, por exemplo, os dias do mês. Eles formam uma [sequência monotônica](https://en.wikipedia.org/wiki/Monotonic_function) ao longo de um mês, mas não em períodos mais longos. Essa é uma sequência parcialmente monotônica. Se um usuário criar a tabela com uma chave primária parcialmente monotônica, o ClickHouse criará um índice esparso como de costume. Quando um usuário seleciona dados desse tipo de tabela, o ClickHouse analisa as condições da consulta. Se o usuário quiser obter dados entre duas marcas do índice e ambas estiverem dentro do mesmo mês, o ClickHouse poderá usar o índice nesse caso específico, porque consegue calcular a distância entre os parâmetros de uma consulta e as marcas do índice.

O ClickHouse não pode usar o índice se os valores da chave primária no intervalo de parâmetros da consulta não representarem uma sequência monotônica. Nesse caso, o ClickHouse usa o método de varredura completa.

O ClickHouse usa essa lógica não apenas para sequências de dias do mês, mas para qualquer chave primária que represente uma sequência parcialmente monotônica.

<div id="table_engine-mergetree-data_skipping-indexes">
  ### Data skipping indexes
</div>

A declaração do índice fica na seção de colunas da consulta `CREATE`.

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

Para tabelas da família `*MergeTree`, é possível especificar data skipping indexes.

Esses índices agregam algumas informações sobre a expressão especificada em blocos, que consistem em `granularity_value` grânulos (o tamanho do grânulo é especificado usando a configuração `index_granularity` no engine da tabela). Em seguida, esses agregados são usados em consultas `SELECT` para reduzir a quantidade de dados a ser lida do disco, descartando grandes blocos de dados em que a condição da cláusula `WHERE` não pode ser satisfeita.

A cláusula `GRANULARITY` pode ser omitida; o valor padrão de `granularity_value` é 1.

**Exemplo**

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

Os índices do exemplo podem ser usados pelo ClickHouse para reduzir a quantidade de dados lidos do disco nas consultas a seguir:

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

Os data skipping indexes também podem ser criados em colunas compostas:

```sql theme={null}
-- em colunas do tipo Map:
INDEX map_key_index mapKeys(map_column) TYPE bloom_filter
INDEX map_value_index mapValues(map_column) TYPE bloom_filter

-- em colunas do tipo JSON:
INDEX json_paths_index JSONAllPaths(json_column) TYPE bloom_filter

-- em colunas do tipo Tuple:
INDEX tuple_1_index tuple_column.1 TYPE bloom_filter
INDEX tuple_2_index tuple_column.2 TYPE bloom_filter

-- em colunas do tipo Nested:
INDEX nested_1_index col.nested_col1 TYPE bloom_filter
INDEX nested_2_index col.nested_col2 TYPE bloom_filter
```

<div id="skip-index-types">
  ### Tipos de Skip Index
</div>

O motor de tabela `MergeTree` oferece suporte aos seguintes tipos de skip indexes.
Para mais informações sobre como os skip indexes podem ser usados na otimização de desempenho,
consulte ["Entendendo os data skipping indexes do ClickHouse"](/docs/pt-BR/concepts/features/performance/skip-indexes/skipping-indexes).

* índice [`MinMax`](#minmax)
* índice [`Set`](#set)
* índice [`bloom_filter`](#bloom-filter)
* índice [`ngrambf_v1`](#n-gram-bloom-filter) *(Obsoleto)*
* índice [`tokenbf_v1`](#token-bloom-filter) *(Obsoleto)*
* índice [`text`](#text)
* índice [`vector_similarity`](#vector-similarity)

<div id="minmax">
  #### Índice skip do tipo MinMax
</div>

Para cada grânulo do índice, os valores mínimo e máximo de uma expressão são armazenados.
(Se a expressão for do tipo `tuple`, são armazenados os valores mínimo e máximo de cada elemento da tupla.)

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

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

Para cada grânulo de índice, são armazenados no máximo `max_rows` valores únicos da expressão especificada.
`max_rows = 0` significa "armazenar todos os valores únicos".

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

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

Para cada grânulo de índice, armazena um [filtro de Bloom](https://en.wikipedia.org/wiki/Bloom_filter) para as colunas especificadas.

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

O parâmetro `false_positive_rate` pode assumir um valor entre 0 e 1 (por padrão: `0.025`) e especifica a probabilidade de gerar um resultado positivo (o que aumenta a quantidade de dados a serem lidos).

Os seguintes tipos de dados são suportados:

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

<Info>
  **Tipo de dado Map: especificando a criação do índice com chaves ou valores**

  Para o tipo de dado `Map`, o cliente pode especificar se o índice deve ser criado para chaves ou para valores usando as funções [`mapKeys`](/docs/pt-BR/reference/functions/regular-functions/tuple-map-functions#mapKeys) ou [`mapValues`](/docs/pt-BR/reference/functions/regular-functions/tuple-map-functions#mapValues).
</Info>

<Info>
  **Tipo de dado JSON: indexando caminhos JSON**

  Para o tipo de dado [`JSON`](/docs/pt-BR/reference/data-types/newjson), um índice de filtro de Bloom pode ser criado sobre o conjunto de caminhos usando a função [`JSONAllPaths`](/docs/pt-BR/reference/functions/regular-functions/json-functions#JSONAllPaths). Isso permite ignorar grânulos em que um caminho JSON consultado está ausente. Consulte [Data skipping indexes para JSON](/docs/pt-BR/reference/data-types/newjson#data-skipping-indexes-for-json) para mais detalhes.
</Info>

<div id="n-gram-bloom-filter">
  #### Filtro de Bloom de n-gramas *(Obsoleto)*
</div>

<Note>
  Com a disponibilidade geral (GA) do índice `text` a partir da versão 26.2 do ClickHouse, o índice `ngrambf_v1` não é mais recomendado para busca em texto completo.

  Consulte a página ["Busca em texto completo com índices de texto"](/docs/pt-BR/reference/engines/table-engines/mergetree-family/textindexes) para mais detalhes.
</Note>

Cada grânulo de índice armazena um [filtro de Bloom](https://en.wikipedia.org/wiki/Bloom_filter) para os [n-gramas](https://en.wikipedia.org/wiki/N-gram) das colunas especificadas.

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

| Parâmetro                       | Descrição                                                                                                                                 |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `n`                             | tamanho do ngram                                                                                                                          |
| `size_of_bloom_filter_in_bytes` | Tamanho do filtro de Bloom em bytes. Você pode usar um valor alto aqui, por exemplo, `256` ou `512`, porque ele pode ser bem comprimido). |
| `number_of_hash_functions`      | Número de funções de hash usadas no filtro de Bloom.                                                                                      |
| `random_seed`                   | Semente para as funções de hash do filtro de Bloom.                                                                                       |

Este índice só funciona com os seguintes tipos de dados:

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

Para estimar os parâmetros de `ngrambf_v1`, você pode usar as seguintes [Funções Definidas pelo Usuário (UDFs)](/docs/pt-BR/reference/statements/create/function).

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

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

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

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

Para usar essas funções, é necessário especificar pelo menos dois parâmetros:

* `total_number_of_all_grams`
* `probability_of_false_positives`

Por exemplo, há `4300` ngrams no grânulo, e você espera que os falsos positivos sejam inferiores a `0.0001`.
Os outros parâmetros podem então ser estimados executando as seguintes consultas:

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

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

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

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

É claro que você também pode usar essas funções para estimar parâmetros para outras condições.
As funções acima fazem referência à calculadora de filtro de Bloom [aqui](https://hur.st/bloomfilter).

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

<Note>
  Com a disponibilidade geral (GA) do índice `text` a partir da versão 26.2 do ClickHouse, o índice `tokenbf_v1` não é mais recomendado para busca em texto completo.

  Consulte a página ["Pesquisa de texto completo com índices de texto"](/docs/pt-BR/reference/engines/table-engines/mergetree-family/textindexes) para mais detalhes.
</Note>

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

<div id="sparse-grams-bloom-filter">
  #### Filtro de Bloom de gramas esparsos
</div>

O filtro de Bloom de gramas esparsos é semelhante a `ngrambf_v1`, mas usa [tokens de gramas esparsos](/docs/pt-BR/reference/functions/regular-functions/string-functions#sparseGrams) em vez de ngrams.

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

<div id="text">
  ### Índice de texto
</div>

Cria um índice invertido sobre dados textuais tokenizados, permitindo uma busca de texto completo eficiente e determinística. Veja [aqui](/docs/pt-BR/reference/engines/table-engines/mergetree-family/textindexes) para mais detalhes.

<div id="vector-similarity">
  #### Similaridade vetorial
</div>

Suporta busca aproximada por vizinho mais próximo; veja [aqui](/docs/pt-BR/reference/engines/table-engines/mergetree-family/annindexes) para mais detalhes.

<div id="functions-support">
  ### Suporte a funções
</div>

As condições na cláusula `WHERE` contêm chamadas a funções que operam sobre colunas. Se a coluna fizer parte de um índice, o ClickHouse tentará usar esse índice ao processar essas funções. O ClickHouse oferece suporte a diferentes subconjuntos de funções para o uso de índices.

Índices do tipo `set` podem ser usados por todas as funções. Os demais tipos de índice têm suporte conforme a seguir:

| Função (operador) / Índice                                                                                                                | chave primária | minmax | ngrambf\_v1 | tokenbf\_v1 | bloom\_filter | sparse\_grams | text |
| ----------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------ | ----------- | ----------- | ------------- | ------------- | ---- |
| [igual (=, ==)](/docs/pt-BR/reference/functions/regular-functions/comparison-functions#equals)                                                 | ✔              | ✔      | ✔           | ✔           | ✔             | ✔             | ✔    |
| [notEquals(!=, \<>)](/docs/pt-BR/reference/functions/regular-functions/comparison-functions#notEquals)                                         | ✔              | ✔      | ✔           | ✔           | ✔             | ✔             | ✗    |
| [like](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#like)                                                         | ✔              | ✔      | ✔           | ✔           | ✗             | ✔             | ✔    |
| [notLike](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#notLike)                                                   | ✔              | ✔      | ✔           | ✔           | ✗             | ✔             | ✗    |
| [match](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#match)                                                       | ✗              | ✗      | ✔           | ✔           | ✗             | ✔             | ✔    |
| [startsWith](/docs/pt-BR/reference/functions/regular-functions/string-functions#startsWith)                                                    | ✔              | ✔      | ✔           | ✔           | ✗             | ✔             | ✔    |
| [endsWith](/docs/pt-BR/reference/functions/regular-functions/string-functions#endsWith)                                                        | ✗              | ✗      | ✔           | ✔           | ✗             | ✔             | ✔    |
| [multiSearchAny](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#multiSearchAny)                                     | ✗              | ✗      | ✔           | ✗           | ✗             | ✗             | ✔    |
| [multiSearchAnyUTF8](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#multiSearchAnyUTF8)                             | ✗              | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [multiMatchAny](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#multiMatchAny)                                       | ✗              | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [in](/docs/pt-BR/reference/functions/regular-functions/in-functions)                                                                           | ✔              | ✔      | ✔           | ✔           | ✔             | ✔             | ✔    |
| [notIn](/docs/pt-BR/reference/functions/regular-functions/in-functions)                                                                        | ✔              | ✔      | ✔           | ✔           | ✔             | ✔             | ✗    |
| [menor (`<`)](/docs/pt-BR/reference/functions/regular-functions/comparison-functions#less)                                                     | ✔              | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [greater (`>`)](/docs/pt-BR/reference/functions/regular-functions/comparison-functions#greater)                                                | ✔              | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [lessOrEquals (`<=`)](/docs/pt-BR/reference/functions/regular-functions/comparison-functions#lessOrEquals)                                     | ✔              | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [greaterOrEquals (`>=`)](/docs/pt-BR/reference/functions/regular-functions/comparison-functions#greaterOrEquals)                               | ✔              | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [empty](/docs/pt-BR/reference/functions/regular-functions/array-functions#empty)                                                               | ✔              | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [notEmpty](/docs/pt-BR/reference/functions/regular-functions/array-functions#notEmpty)                                                         | ✗              | ✔      | ✗           | ✗           | ✗             | ✔             | ✗    |
| [has](/docs/pt-BR/reference/functions/regular-functions/array-functions#has)                                                                   | ✔              | ✔      | ✔           | ✔           | ✔             | ✔             | ✔    |
| [hasAny](/docs/pt-BR/reference/functions/regular-functions/array-functions#hasAny)                                                             | ✗              | ✗      | ✔           | ✔           | ✔             | ✔             | ✗    |
| [hasAll](/docs/pt-BR/reference/functions/regular-functions/array-functions#hasAll)                                                             | ✗              | ✗      | ✔           | ✔           | ✔             | ✔             | ✗    |
| [hasToken](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#hasToken)                                                 | ✗              | ✗      | ✗           | ✔           | ✗             | ✗             | ✔    |
| [hasTokenOrNull](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#hasTokenOrNull)                                     | ✗              | ✗      | ✗           | ✔           | ✗             | ✗             | ✔    |
| [hasTokenCaseInsensitive (`*`)](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#hasTokenCaseInsensitive)             | ✗              | ✗      | ✗           | ✔           | ✗             | ✗             | ✗    |
| [hasTokenCaseInsensitiveOrNull (`*`)](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#hasTokenCaseInsensitiveOrNull) | ✗              | ✗      | ✗           | ✔           | ✗             | ✗             | ✗    |
| [hasAnyTokens](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#hasAnyTokens)                                         | ✗              | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [hasAllTokens](/docs/pt-BR/reference/functions/regular-functions/string-search-functions#hasAllTokens)                                         | ✗              | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [pointInPolygon](/docs/pt-BR/reference/functions/regular-functions/geo/coordinates#pointinpolygon)                                             | ✔              | ✔      | ✗           | ✗           | ✗             | ✗             | ✗    |
| [mapContains (mapContainsKey)](/docs/pt-BR/reference/functions/regular-functions/tuple-map-functions#mapContainsKey)                           | ✗              | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [mapContainsKeyLike](/docs/pt-BR/reference/functions/regular-functions/tuple-map-functions#mapContainsKeyLike)                                 | ✗              | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [mapContainsValue](/docs/pt-BR/reference/functions/regular-functions/tuple-map-functions#mapContainsValue)                                     | ✗              | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |
| [mapContainsValueLike](/docs/pt-BR/reference/functions/regular-functions/tuple-map-functions#mapContainsValueLike)                             | ✗              | ✗      | ✗           | ✗           | ✗             | ✗             | ✔    |

Funções com um argumento constante menor que o tamanho do ngram não podem ser usadas por `ngrambf_v1` para otimização de consultas.

(\*) Para que `hasTokenCaseInsensitive` e `hasTokenCaseInsensitiveOrNull` sejam eficazes, o índice `tokenbf_v1` deve ser criado sobre dados convertidos para minúsculas, por exemplo `INDEX idx (lower(str_col)) TYPE tokenbf_v1(512, 3, 0)`.

<Note>
  Filtros de Bloom podem gerar falsos positivos, portanto os índices `ngrambf_v1`, `tokenbf_v1`, `sparse_grams` e `bloom_filter` não podem ser usados para otimizar consultas em que se espera que o resultado de uma função seja falso.

  Por exemplo:

  * Pode ser otimizado:
    * `s LIKE '%test%'`
    * `NOT s NOT LIKE '%test%'`
    * `s = 1`
    * `NOT s != 1`
    * `startsWith(s, 'test')`
  * Não pode ser otimizado:
    * `NOT s LIKE '%test%'`
    * `s NOT LIKE '%test%'`
    * `NOT s = 1`
    * `s != 1`
    * `NOT startsWith(s, 'test')`
</Note>

<div id="projections">
  ## Projeções
</div>

Projeções são como [visões materializadas](/docs/pt-BR/reference/statements/create/view), mas são definidas no nível das partes. Elas oferecem garantias de consistência e são usadas automaticamente nas consultas.

<Note>
  Ao implementar projeções, você também deve considerar a configuração [force\_optimize\_projection](/docs/pt-BR/reference/settings/session-settings#force_optimize_projection).
</Note>

Projeções não são compatíveis com instruções `SELECT` com o modificador [FINAL](/docs/pt-BR/reference/statements/select/from#final-modifier).

<div id="projection-query">
  ### Consulta de projeção
</div>

Uma consulta de projeção define uma projeção. Ela seleciona implicitamente dados da tabela pai.
**Sintaxe**

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

As projeções podem ser modificadas ou removidas com a instrução [ALTER](/docs/pt-BR/reference/statements/alter/projection).

<div id="projection-index">
  ### Índices de projeção
</div>

Os índices de projeção ampliam o subsistema de projeções, oferecendo uma forma leve e explícita de definir índices no nível da projeção.
Externamente, um índice de projeção ainda é uma projeção, mas com sintaxe simplificada e propósito mais claro: ele define uma expressão dedicada à filtragem, em vez de fornecer dados materializados.
Internamente, um índice de projeção não materializa a tabela original em uma ordem de linhas permutada, como faz uma projeção comum.
Em vez disso, a permutação é armazenada na forma de uma coluna numérica de permutação `_part_offset`, ou seja, `SELECT _part_offset ORDER BY <index_expr>`.

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

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

Exemplo:

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

<div id="projection-index-types">
  #### Tipos de índice
</div>

Atualmente, há suporte para:

* **básico**: equivalente a um índice normal do MergeTree sobre a expressão.

O framework permite adicionar mais tipos de índice no futuro.

<div id="projection-storage">
  ### Armazenamento de projeções
</div>

As projeções são armazenadas dentro do diretório da parte. É semelhante a um índice, mas contém um subdiretório que armazena a parte de uma tabela `MergeTree` anônima. A tabela é derivada da consulta de definição da projeção. Se houver uma cláusula `GROUP BY`, o motor de armazenamento subjacente se torna [AggregatingMergeTree](/docs/pt-BR/reference/engines/table-engines/mergetree-family/aggregatingmergetree), e todas as funções de agregação são convertidas em `AggregateFunction`. Se houver uma cláusula `ORDER BY`, a tabela `MergeTree` a usa como expressão de chave primária. Durante o processo de merge, a parte da projeção é mesclada pela rotina de merge do seu motor de armazenamento. O checksum da parte da tabela pai é combinado com a parte da projeção. Outras tarefas de manutenção são semelhantes às dos índices de salto.

<div id="projection-query-analysis">
  ### Análise de consulta
</div>

1. Verifique se a projeção pode ser usada para responder à consulta fornecida, ou seja, se ela gera a mesma resposta que consultar a tabela base.
2. Selecione a melhor opção viável, que contenha o menor número de grânulos a serem lidos.
3. O pipeline de consulta que usa projeções será diferente daquele que usa as partes originais. Se a projeção estiver ausente em algumas partes, podemos adicionar um pipeline para "projetá-la" dinamicamente.

<div id="concurrent-data-access">
  ## Acesso simultâneo aos dados
</div>

Para acesso simultâneo à tabela, usamos multiversionamento. Em outras palavras, quando uma tabela é lida e atualizada ao mesmo tempo, os dados são lidos de um conjunto de partes vigente no momento da consulta. Não há bloqueios prolongados. As inserções não interferem nas operações de leitura.

A leitura de uma tabela é paralelizada automaticamente.

<div id="table_engine-mergetree-ttl">
  ## TTL para colunas e tabelas
</div>

Determina o tempo de vida dos valores.

A cláusula `TTL` pode ser definida para a tabela inteira e para cada coluna individual. O `TTL` no nível da tabela também pode especificar a lógica de movimentação automática de dados entre disks e volumes, ou a recompressão de partes cujos dados já expiraram.

As expressões devem resultar em um tipo de dado [Date](/docs/pt-BR/reference/data-types/date), [Date32](/docs/pt-BR/reference/data-types/date32), [DateTime](/docs/pt-BR/reference/data-types/datetime) ou [DateTime64](/docs/pt-BR/reference/data-types/datetime64).

<Tip>
  **Evite funções não determinísticas em expressões TTL**

  O TTL é avaliado durante a mesclagem em segundo plano, e não no momento da inserção.
  Funções como `rand()`, `now()` ou `now64()` serão reavaliadas a cada merge, levando a um comportamento de exclusão imprevisível.
  O ClickHouse bloqueia expressões sem qualquer dependência de coluna, mas atualmente não rejeita funções não determinísticas misturadas com uma referência de coluna (por exemplo, `ts + rand()`). As expressões TTL devem se basear exclusivamente em valores determinísticos derivados de colunas para garantir resultados previsíveis.
</Tip>

**Sintaxe**

Definir o time-to-live de uma coluna:

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

Para definir `interval`, use os operadores de [intervalo de tempo](/docs/pt-BR/reference/operators/index#operators-for-working-with-dates-and-times), por exemplo:

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

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

Quando os valores de uma coluna expiram, o ClickHouse os substitui pelos valores padrão do tipo de dados da coluna. Se todos os valores da coluna em uma parte de dados expirarem, o ClickHouse exclui essa coluna da parte de dados no sistema de arquivos.

A cláusula `TTL` não pode ser usada para colunas de chave.

**Exemplos**

<div id="creating-a-table-with-ttl">
  #### Criando uma tabela com `TTL`:
</div>

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

<div id="adding-ttl-to-a-column-of-an-existing-table">
  #### Adicionando TTL a uma coluna em uma tabela existente
</div>

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

<div id="altering-ttl-of-the-column">
  #### Alterar o TTL da coluna
</div>

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

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

A tabela pode ter uma expressão para remover linhas expiradas e múltiplas expressões para mover partes automaticamente entre [disks ou volumes](#table_engine-mergetree-multiple-volumes). Quando as linhas da tabela expiram, o ClickHouse exclui todas as linhas correspondentes. Para movimentação de partes ou recompressão, todas as linhas de uma parte devem atender aos critérios da expressão `TTL`.

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

O tipo de regra `TTL` pode acompanhar cada expressão TTL. Ele determina a ação a ser executada quando a expressão for satisfeita (atingir o momento atual):

* `DELETE` - exclui linhas expiradas (ação padrão);
* `RECOMPRESS codec_name` - recomprime a parte de dados com o `codec_name`;
* `TO DISK 'aaa'` - move a part para o disk `aaa`;
* `TO VOLUME 'bbb'` - move a part para o disk `bbb`;
* `GROUP BY` - agrega linhas expiradas.

A ação `DELETE` pode ser usada em conjunto com a cláusula `WHERE` para excluir apenas algumas das linhas expiradas com base em uma condição de filtragem:

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

A expressão `GROUP BY` deve ser um prefixo da chave primária da tabela.

Se uma coluna não fizer parte da expressão `GROUP BY` e não for definida explicitamente na cláusula `SET`, ela conterá, na linha de resultado, um valor aleatório das linhas agrupadas (como se a função de agregação `any` fosse aplicada a ela).

**Exemplos**

<div id="creating-a-table-with-ttl">
  #### Criando uma tabela com `TTL`:
</div>

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

<div id="altering-ttl-of-the-table">
  #### Alterando o `TTL` da tabela:
</div>

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

Criando uma tabela em que as linhas expiram após um mês. As linhas expiradas cujas datas caem em segundas-feiras são excluídas:

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

<div id="creating-a-table-where-expired-rows-are-recompressed">
  #### Criando uma tabela em que as linhas expiradas são recomprimidas:
</div>

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

Criando uma tabela em que as linhas expiradas são agregadas. Nas linhas de resultado, `x` contém o valor máximo entre as linhas agrupadas, `y`, o valor mínimo, e `d`, um valor qualquer das linhas agrupadas.

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

<div id="mergetree-removing-expired-data">
  ### Remoção de dados expirados
</div>

Dados com `TTL` expirado são removidos quando o ClickHouse faz a mesclagem de partes de dados.

Quando o ClickHouse detecta que os dados expiraram, ele realiza uma mesclagem fora do cronograma. Para controlar a frequência dessas mesclagens, você pode definir `merge_with_ttl_timeout`. Se o valor for muito baixo, muitas mesclagens fora do cronograma serão executadas, o que pode consumir muitos recursos.

Se você executar a consulta `SELECT` entre as mesclagens, poderá obter dados expirados. Para evitar isso, use a consulta [OPTIMIZE](/docs/pt-BR/reference/statements/optimize) antes de `SELECT`.

**Veja também**

* configuração [ttl\_only\_drop\_parts](/docs/pt-BR/reference/settings/merge-tree-settings#ttl_only_drop_parts)

<div id="disk-types">
  ## Tipos de disco
</div>

Além de dispositivos de bloco locais, o ClickHouse oferece suporte a estes tipos de armazenamento:

* [`s3` para S3 e MinIO](#table_engine-mergetree-s3)
* [`gcs` para GCS](/docs/pt-BR/integrations/connectors/data-sources/gcs#creating-a-disk)
* [`blob_storage_disk` para Azure Blob Storage](/docs/pt-BR/concepts/features/configuration/server-config/storing-data#azure-blob-storage)
* [`hdfs` para HDFS](/docs/pt-BR/reference/engines/table-engines/integrations/hdfs)
* [`web` para somente leitura via web](/docs/pt-BR/concepts/features/configuration/server-config/storing-data#web-storage)
* [`cache` para cache local](/docs/pt-BR/concepts/features/configuration/server-config/storing-data#using-local-cache)
* [`s3_plain` para backups no S3](/docs/pt-BR/concepts/features/backup-restore/local-disk)
* [`s3_plain_rewritable` para tabelas imutáveis e não replicadas no S3](/docs/pt-BR/concepts/features/configuration/server-config/storing-data#s3-plain-rewritable-storage)

<div id="table_engine-mergetree-multiple-volumes">
  ## Uso de vários dispositivos de bloco para armazenamento de dados
</div>

<div id="introduction">
  ### Introdução
</div>

Os motores de tabela da família `MergeTree` podem armazenar dados em vários dispositivos de bloco. Por exemplo, isso pode ser útil quando os dados de uma determinada tabela são implicitamente divididos em "quentes" e "frios". Os dados mais recentes são consultados com frequência, mas exigem apenas uma pequena quantidade de espaço. Por outro lado, o grande volume de dados históricos é consultado raramente. Se houver vários disks disponíveis, os dados "quentes" podem ficar em disks rápidos (por exemplo, SSDs NVMe ou em memória), enquanto os dados "frios" ficam em disks relativamente lentos (por exemplo, HDDs).

Isso se aplica a todos os tipos de disco, incluindo disks S3 e outros disks de armazenamento de objetos. Por exemplo, você pode distribuir dados entre vários buckets do S3 em um único volume ou criar políticas em camadas que movem dados de disks locais para o S3. Veja [Usando disks S3 com vários volumes](#s3-multiple-volumes) para mais detalhes.

Uma parte de dados é a menor unidade móvel das tabelas com motor `MergeTree`. Os dados pertencentes a uma parte são armazenados em um único disco. As partes de dados podem ser movidas entre disks em segundo plano (de acordo com as configurações do usuário), bem como por meio das consultas [ALTER](/docs/pt-BR/reference/statements/alter/partition).

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

* Disco — Dispositivo de bloco montado no sistema de arquivos.
* Disco padrão — Disco que armazena o caminho especificado na configuração de servidor [path](/docs/pt-BR/reference/settings/server-settings/settings#path).
* Volume — Conjunto ordenado de disks idênticos (semelhante a [JBOD](https://en.wikipedia.org/wiki/Non-RAID_drive_architectures)).
* Política de armazenamento — Conjunto de volumes e das regras para mover dados entre eles.

Os nomes dados às entidades descritas podem ser encontrados nas tabelas de sistema [system.storage\_policies](/docs/pt-BR/reference/system-tables/storage_policies) e [system.disks](/docs/pt-BR/reference/system-tables/disks). Para aplicar uma das políticas de armazenamento configuradas a uma tabela, use a configuração `storage_policy` das tabelas da família de engines `MergeTree`.

<div id="table_engine-mergetree-multiple-volumes_configure">
  ### Configuração
</div>

Discos, volumes e políticas de armazenamento devem ser declarados dentro da tag `<storage_configuration>`, em um arquivo no diretório `config.d`.

<Tip>
  Discos também podem ser declarados na seção `SETTINGS` de uma consulta. Isso é útil
  para análises ad hoc, para anexar temporariamente um disco hospedado, por exemplo, em uma URL.
  Consulte [armazenamento dinâmico](/docs/pt-BR/concepts/features/configuration/server-config/storing-data#dynamic-configuration) para mais detalhes.
</Tip>

Estrutura da configuração:

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

        ...
    </disks>

    ...
</storage_configuration>
```

Tags:

* `<disk_name_N>` — Nome do disco. Os nomes devem ser diferentes para todos os disks.
* `path` — caminho no qual o servidor armazenará os dados (pastas `data` e `shadow`); deve terminar com '/'.
* `keep_free_space_bytes` — quantidade de espaço livre em disco a ser reservada.

A ordem da definição dos disks não é importante.

Markup de configuração das políticas de armazenamento:

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

        <!-- mais políticas -->
    </policies>
    ...
</storage_configuration>
```

Tags:

* `policy_name_N` — Nome da política. Os nomes das políticas devem ser únicos.
* `volume_name_N` — Nome do volume. Os nomes dos volumes devem ser únicos.
* `disk` — um disco dentro de um volume.
* `max_data_part_size_bytes` — o tamanho máximo de uma parte que pode ser armazenada em qualquer um dos disks do volume. Se o tamanho estimado de uma parte de dados mesclada for maior que `max_data_part_size_bytes`, essa parte será gravada no próximo volume. Basicamente, esse recurso permite manter partes novas/pequenas em um volume quente (SSD) e movê-las para um volume frio (HDD) quando atingirem um tamanho grande. Não use essa configuração se sua política tiver apenas um volume.
* `move_factor` — quando a quantidade de espaço disponível fica abaixo desse fator, os dados começam automaticamente a ser movidos para o próximo volume, se houver (por padrão, 0.1). O ClickHouse classifica as partes existentes por tamanho, da maior para a menor (em ordem decrescente), e seleciona partes cujo tamanho total seja suficiente para atender à condição de `move_factor`. Se o tamanho total de todas as partes for insuficiente, todas as partes serão movidas.
* `perform_ttl_move_on_insert` — Desabilita o TTL move no INSERT de partes de dados. Por padrão (se estiver habilitado), se inserirmos uma parte de dados que já expirou pela regra de TTL move, ela irá imediatamente para um volume/disco declarado na regra de movimentação. Isso pode tornar o insert significativamente mais lento caso o volume/disco de destino seja lento (por exemplo, S3). Se estiver desabilitado, a parte de dados já expirada será gravada em um volume padrão e, logo em seguida, movida para o volume de TTL.
* `load_balancing` - Política de balanceamento de disks, `round_robin` ou `least_used`.
* `least_used_ttl_ms` - Configura o timeout (em milissegundos) para a atualização do espaço disponível em todos os disks (`0` - sempre atualizar, `-1` - nunca atualizar, o padrão é `60000`). Observe que, se o disco puder ser usado apenas pelo ClickHouse e não estiver sujeito a redimensionamento/redução online do filesystem, você pode usar `-1`; em todos os outros casos, isso não é recomendado, pois eventualmente levará a uma distribuição incorreta do espaço.
* `prefer_not_to_merge` — Você não deve usar essa configuração. Desabilita a mesclagem de partes de dados neste volume (isso é prejudicial e leva à degradação de desempenho). Quando essa configuração está habilitada (não faça isso), a mesclagem de dados neste volume não é permitida (o que é ruim). Isso permite (mas você não precisa disso) controlar (se você quiser controlar alguma coisa, está cometendo um erro) como o ClickHouse funciona com disks lentos (mas o ClickHouse sabe mais, então, por favor, não use essa configuração).
* `volume_priority` — Define a prioridade (ordem) em que os volumes são preenchidos. Um valor menor significa prioridade maior. Os valores do parâmetro devem ser números naturais e, em conjunto, cobrir o intervalo de 1 a N (sendo N a menor prioridade atribuída) sem pular nenhum número.
  * Se *todos* os volumes estiverem marcados, eles serão priorizados na ordem fornecida.
  * Se apenas *alguns* volumes estiverem marcados, os que não tiverem a marcação terão a menor prioridade e serão priorizados na ordem em que são definidos na configuração.
  * Se *nenhum* volume estiver marcado, sua prioridade será definida de acordo com a ordem em que for declarado na configuração.
  * Dois volumes não podem ter o mesmo valor de prioridade.

Exemplos de configuração:

```xml theme={null}
<storage_configuration>
    ...
    <policies>
        <hdd_in_order> <!-- nome da política -->
            <volumes>
                <single> <!-- nome do volume -->
                    <disk>disk1</disk>
                    <disk>disk2</disk>
                </single>
            </volumes>
        </hdd_in_order>

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

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

No exemplo fornecido, a política `hdd_in_order` implementa a abordagem de [round-robin](https://en.wikipedia.org/wiki/Round-robin_scheduling). Assim, essa política define apenas um volume (`single`), e as partes de dados são armazenadas em todos os seus disks em ordem circular. Essa política pode ser bastante útil se houver vários disks semelhantes montados no sistema, mas o RAID não estiver configurado. Tenha em mente que cada disco individual não é confiável, e talvez você queira compensar isso com um fator de replicação de 3 ou mais.

Se houver diferentes tipos de disks disponíveis no sistema, a política `moving_from_ssd_to_hdd` pode ser usada no lugar. O volume `hot` consiste em um disco SSD (`fast_ssd`), e o tamanho máximo de uma parte que pode ser armazenada nesse volume é 1GB. Todas as partes com tamanho maior que 1GB serão armazenadas diretamente no volume `cold`, que contém um disco HDD `disk1`.
Além disso, quando o disco `fast_ssd` ficar mais de 80% cheio, os dados serão transferidos para `disk1` por um processo em segundo plano.

A ordem de enumeração dos volumes dentro de uma política de armazenamento é importante caso pelo menos um dos volumes listados não tenha um parâmetro `volume_priority` explícito.
Quando um volume fica cheio demais, os dados são movidos para o próximo. A ordem de enumeração dos disks também é importante, porque os dados são armazenados neles em rodízio.

Ao criar uma tabela, é possível aplicar a ela uma das políticas de armazenamento configuradas:

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

A política de armazenamento `default` pressupõe o uso de apenas um volume, que consiste em apenas um disco definido em `<path>`.
Você pode alterar a política de armazenamento após a criação da tabela com a consulta \[ALTER TABLE ... MODIFY SETTING], e a nova política deve incluir todos os disks e volumes antigos com os mesmos nomes.

O número de threads que executam movimentações em segundo plano de partes de dados pode ser alterado pela configuração [background\_move\_pool\_size](/docs/pt-BR/reference/settings/server-settings/settings#background_move_pool_size).

<div id="details">
  ### Detalhes
</div>

No caso das tabelas `MergeTree`, os dados chegam ao disco de diferentes maneiras:

* Como resultado de um insert (consulta `INSERT`).
* Durante mesclagens em segundo plano e [mutações](/docs/pt-BR/reference/statements/alter/index#mutations).
* Ao serem baixados de outra réplica.
* Como resultado do congelamento de uma partição [ALTER TABLE ... FREEZE PARTITION](/docs/pt-BR/reference/statements/alter/partition#freeze-partition).

Em todos esses casos, exceto nas mutações e no congelamento de partição, uma parte é armazenada em um volume e em um disco de acordo com a política de armazenamento definida:

1. É escolhido o primeiro volume (na ordem de definição) que tenha espaço em disco suficiente para armazenar uma parte (`unreserved_space > current_part_size`) e permita armazenar partes de determinado tamanho (`max_data_part_size_bytes > current_part_size`).
2. Dentro desse volume, é escolhido o disco que vem após o usado para armazenar o fragmento anterior de dados e que tenha espaço livre maior que o tamanho da parte (`unreserved_space - keep_free_space_bytes > current_part_size`).

Internamente, mutações e o congelamento de partição usam [hard links](https://en.wikipedia.org/wiki/Hard_link). Hard links entre disks diferentes não são suportados; portanto, nesses casos, as partes resultantes são armazenadas nos mesmos disks que as originais.

Em segundo plano, as partes são movidas entre volumes com base na quantidade de espaço livre (parâmetro `move_factor`), de acordo com a ordem em que os volumes são declarados no arquivo de configuração.
Os dados nunca são transferidos do último para o primeiro. É possível usar as tabelas de sistema [system.part\_log](/docs/pt-BR/reference/system-tables/part_log) (campo `type = MOVE_PART`) e [system.parts](/docs/pt-BR/reference/system-tables/parts) (campos `path` e `disk`) para monitorar movimentações em segundo plano. Além disso, informações detalhadas podem ser encontradas nos logs do servidor.

O usuário pode forçar a movimentação de uma parte ou de uma partição de um volume para outro usando a consulta [ALTER TABLE ... MOVE PART|PARTITION ... TO VOLUME|DISK ...](/docs/pt-BR/reference/statements/alter/partition); todas as restrições das operações em segundo plano são levadas em consideração. A consulta inicia a movimentação por conta própria e não espera a conclusão das operações em segundo plano. O usuário receberá uma mensagem de erro se não houver espaço livre suficiente ou se alguma das condições exigidas não for atendida.

A movimentação de dados não interfere na replicação. Portanto, diferentes políticas de armazenamento podem ser especificadas para a mesma tabela em réplicas diferentes.

Após a conclusão das mesclagens em segundo plano e das mutações, as partes antigas só são removidas depois de um certo tempo (`old_parts_lifetime`).
Durante esse período, elas não são movidas para outros volumes ou disks. Portanto, até que sejam finalmente removidas, ainda são consideradas no cálculo do espaço em disco ocupado.

O usuário pode distribuir novas partes grandes entre diferentes disks de um volume [JBOD](https://en.wikipedia.org/wiki/Non-RAID_drive_architectures) de forma equilibrada usando a configuração [min\_bytes\_to\_rebalance\_partition\_over\_jbod](/docs/pt-BR/reference/settings/merge-tree-settings#min_bytes_to_rebalance_partition_over_jbod).

<div id="table_engine-mergetree-s3">
  ## Usando armazenamento externo para dados
</div>

Os motores de tabela da família [MergeTree](/docs/pt-BR/reference/engines/table-engines/mergetree-family/mergetree) podem armazenar dados em `S3`, `AzureBlobStorage` e `HDFS` usando, respectivamente, discos dos tipos `s3`, `azure_blob_storage` e `hdfs`. Consulte [como configurar opções de armazenamento externo](/docs/pt-BR/concepts/features/configuration/server-config/storing-data#configuring-external-storage) para mais detalhes.

Exemplo de uso do [S3](https://aws.amazon.com/s3/) como armazenamento externo com um disco do tipo `s3`.

Trecho de configuração:

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

Consulte também [como configurar opções de armazenamento externo](/docs/pt-BR/concepts/features/configuration/server-config/storing-data#configuring-external-storage).

<div id="s3-multiple-volumes">
  ### Usando disks do S3 com vários volumes
</div>

Disks do S3 (e de outros tipos de armazenamento de objetos) podem ser usados em políticas de armazenamento com vários disks e vários volumes da mesma forma que disks locais. Isso permite distribuir os dados entre vários buckets do S3 em um único volume (no estilo JBOD) ou configurar políticas de armazenamento em camadas com volumes do S3.

Por exemplo, para distribuir os dados entre dois buckets do S3 em esquema round-robin:

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

Você também pode combinar volumes locais e do S3 em uma política em camadas, por exemplo, movendo dados de um SSD local para o S3 à medida que ficam mais antigos:

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

<Note>
  Ao usar `use_environment_credentials` para autenticação no S3, as credenciais de ambiente (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`) são compartilhadas entre todos os disks S3. Não é possível usar credenciais de ambiente diferentes para disks diferentes. Se você precisar de credenciais diferentes para cada disk S3, use as configurações explícitas `access_key_id` e `secret_access_key` para cada disco.
</Note>

É possível configurar tabelas MergeTree não replicadas em um cenário com um gravador e muitos leitores em armazenamento compartilhado. Isso é viabilizado pela atualização automática da lista de partes, que pode ser configurada nos leitores. Observe que isso requer metadados de sistema de arquivos compartilhados entre as réplicas (ou `table_disk = true` com um disco local da tabela). Consulte [refresh\_parts\_interval and table\_disk](/docs/pt-BR/concepts/features/configuration/server-config/storing-data#refresh-parts-interval-and-table-disk).

<Info>
  **configuração do cache**

  As versões 22.3 a 22.7 do ClickHouse usam uma configuração de cache diferente. Consulte [using local cache](/docs/pt-BR/concepts/features/configuration/server-config/storing-data#using-local-cache) se você estiver usando uma dessas versões.
</Info>

<div id="virtual-columns">
  ## Colunas virtuais
</div>

* `_part` — Nome de uma parte.
* `_part_index` — Índice sequencial da parte no resultado da consulta.
* `_part_starting_offset` — Linha inicial cumulativa da parte no resultado da consulta.
* `_part_offset` — Número da linha na parte.
* `_part_granule_offset` — Número do grânulo na parte.
* `_partition_id` — Nome de uma partição.
* `_part_uuid` — Identificador exclusivo da parte (se a configuração do MergeTree `assign_part_uuids` estiver habilitada).
* `_part_data_version` — Versão dos dados da parte (seja o número mínimo do bloco ou a versão da mutação).
* `_partition_value` — Valores (uma tupla) de uma expressão `partition by`.
* `_sample_factor` — Fator de amostragem (da consulta).
* `_block_number` — Número original do bloco da linha que foi atribuído na inserção, persistido em mesclagens quando a configuração `enable_block_number_column` está habilitada.
* `_block_offset` — Número original da linha no bloco que foi atribuído na inserção, persistido em mesclagens quando a configuração `enable_block_offset_column` está habilitada.
* `_disk_name` — Nome do disco usado para o armazenamento.

<div id="column-statistics">
  ## Estatísticas de coluna
</div>

A declaração das estatísticas fica na seção de colunas da consulta `CREATE` para tabelas da família `*MergeTree*`:

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

Também é possível manipular estatísticas com instruções `ALTER`:

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

Essas estatísticas leves agregam informações sobre a distribuição de valores nas colunas. As estatísticas são armazenadas em cada parte e atualizadas a cada inserção.
Elas só podem ser usadas para a otimização PREWHERE se habilitarmos `set use_statistics = 1`.

<div id="part-pruning-with-statistics">
  #### Poda de partes com estatísticas
</div>

Quando `use_statistics_for_part_pruning` está habilitado, as estatísticas podem ser usadas para a poda de partes.
Atualmente, apenas as estatísticas `basic` (e as estatísticas `minmax` obsoletas) dão suporte à poda de partes. Quando essas estatísticas são definidas para uma coluna, o ClickHouse acompanha os valores mínimo e máximo dessa coluna em cada parte.
A poda de partes permite evitar a leitura de partes de dados inteiras quando a condição de filtro da consulta não pode corresponder a nenhuma linha nessa parte.

**Exemplo:**

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

SYSTEM STOP MERGES test_stats;

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

SET use_statistics_for_part_pruning = 1;

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

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

<div id="available-types-of-column-statistics">
  ### Tipos disponíveis de estatísticas de coluna
</div>

* `basic`

  Um conjunto compacto de resumos de valor único derivados de uma coluna. Dependendo do tipo da coluna, os seguintes elementos são preenchidos:

  * para qualquer coluna cujos valores sejam representados por um número (inteiros, floats, `Decimal*`, `Date*`, `DateTime*`, `Enum*`, `IPv4`, ...): os valores mínimo e máximo, que permitem estimar a seletividade de filtros por intervalo e habilitam a poda de partes;
  * para colunas `String` e `FixedString`: o comprimento total em bytes dos valores não `NULL` (a partir do qual é possível derivar o comprimento médio das strings);
  * para colunas `Nullable` e `LowCardinality(Nullable)`: a contagem de valores `NULL`, que o otimizador usa para descontar linhas `NULL` das estimativas de seletividade.

    Uma única estatística `basic` pode preencher vários desses elementos ao mesmo tempo — por exemplo, em uma coluna `Nullable(UInt32)`, ela rastreia tanto o mínimo/máximo numérico quanto a contagem de nulos. Em comparação com `minmax`, `basic` também funciona em colunas `String` / `FixedString` e pode ser declarada em wrappers `Nullable` de tipos como `UUID` ou `IPv6` apenas para rastrear a contagem de nulos.

* `minmax` (obsoleto)

<Note>
  As estatísticas `minmax` estão obsoletas e não podem mais ser criadas (`CREATE TABLE ... STATISTICS(minmax)` e `ALTER TABLE ... ADD/MODIFY STATISTICS ... TYPE minmax` retornam um erro). Tabelas e partes existentes com estatísticas `minmax` continuam funcionando. Use estatísticas `basic` no lugar.
</Note>

* `tdigest`

<Warning>
  Estatísticas do tipo `tdigest` têm alto custo de criação e podem desacelerar a ingestão de dados.
</Warning>

Sketches [TDigest](https://github.com/tdunning/t-digest) que permitem calcular percentis aproximados (por exemplo, o percentil 90) para colunas numéricas.

* `uniq`

  Sketches [BJKST](https://people.iith.ac.in/aravind/Files-CS5120/pc-lec14-BJKST.pdf) que fornecem uma estimativa de quantos valores distintos uma coluna contém. Internamente, usa [`uniq`](/docs/pt-BR/reference/functions/aggregate-functions/uniq).

* `uniq_v2`

  Semelhante a `uniq`, mas internamente usa [`uniqCombined`](/docs/pt-BR/reference/functions/aggregate-functions/uniqCombined)`(12)` (uma variante de [HyperLogLog](https://en.wikipedia.org/wiki/HyperLogLog)). Consome menos memória que `uniq` e pode ser compilado mais rapidamente.

* `countmin`

<Warning>
  Estatísticas do tipo `countmin` têm alto custo de criação e podem desacelerar a ingestão de dados.
</Warning>

Sketches [CountMin](https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch) que fornecem uma contagem aproximada da frequência de cada valor em uma coluna.

<div id="supported-data-types">
  ### Tipos de dados suportados
</div>

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

Todos os itens acima também aceitam wrappers `Nullable` e `LowCardinality(Nullable)` dos tipos listados. `Basic` também pode ser declarado em wrappers `Nullable` de tipos como `UUID` ou `IPv6` exclusivamente para rastrear a contagem de valores nulos.

<div id="supported-operations">
  ### Operações suportadas
</div>

|          | Filtros de igualdade (==) | Filtros por intervalo (`>, >=, <, <=`) |
| -------- | ------------------------- | -------------------------------------- |
| basic    | ✗                         | ✔ (somente colunas numéricas)          |
| countmin | ✔                         | ✗                                      |
| minmax   | ✗                         | ✔ (somente colunas numéricas)          |
| tdigest  | ✗                         | ✔ (somente colunas numéricas)          |
| uniq     | ✔                         | ✗                                      |
| uniq\_v2 | ✔                         | ✗                                      |

Para `basic` em colunas `String` / `FixedString`, a estatística registra apenas o comprimento total, em bytes, dos valores não NULL
(usado para estimar o comprimento médio da string) e a contagem de nulos;
filtros por intervalo e poda de partes não usam essa estatística.

<div id="column-level-settings">
  ## Configurações em nível de coluna
</div>

Determinadas configurações do MergeTree podem ser sobrescritas em nível de coluna:

* `max_compress_block_size` — Tamanho máximo dos blocos de dados não comprimidos antes de serem comprimidos para gravação em uma tabela.
* `min_compress_block_size` — Tamanho mínimo dos blocos de dados não comprimidos necessário para que a compressão ocorra ao gravar a próxima marca.

Exemplo:

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

As configurações de nível de coluna podem ser alteradas ou removidas usando [ALTER MODIFY COLUMN](/docs/pt-BR/reference/statements/alter/column), por exemplo:

* Remova `SETTINGS` da declaração da coluna:

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

* Altere uma configuração:

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

* Restaura uma ou mais configurações e também remove a declaração da configuração na expressão de coluna da consulta CREATE da tabela.

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