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

> Permite gravar rapidamente estados de objetos que mudam continuamente, e excluir estados antigos de objetos em segundo plano.

# motor de tabela VersionedCollapsingMergeTree

Este motor:

* Permite gravar rapidamente estados de objetos que mudam continuamente.
* Exclui estados antigos de objetos em segundo plano. Isso reduz significativamente o volume de armazenamento.

Consulte a seção [colapsamento](#table_engines_versionedcollapsingmergetree) para mais detalhes.

O motor herda de [MergeTree](/docs/pt-BR/reference/engines/table-engines/mergetree-family/mergetree) e adiciona a lógica de colapsamento de linhas ao algoritmo de mesclagem de partes de dados. `VersionedCollapsingMergeTree` tem a mesma finalidade que [CollapsingMergeTree](/docs/pt-BR/reference/engines/table-engines/mergetree-family/collapsingmergetree), mas usa um algoritmo de colapsamento diferente que permite inserir dados em qualquer ordem com várias threads. Em particular, a coluna `Version` ajuda a colapsar as linhas corretamente, mesmo que sejam inseridas fora de ordem. Em contraste, `CollapsingMergeTree` permite apenas inserção estritamente consecutiva.

<div id="creating-a-table">
  ## Criando uma tabela
</div>

```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 = VersionedCollapsingMergeTree(sign, version)
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
```

Para obter uma descrição dos parâmetros de consulta, consulte a [descrição da consulta](/docs/pt-BR/reference/statements/create/table).

<div id="engine-parameters">
  ### Parâmetros do motor
</div>

```sql theme={null}
VersionedCollapsingMergeTree(sign, version)
```

| Parâmetro | Descrição                                                                                            | Tipo                                                                                                                                                                                                                                                                                                      |
| --------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sign`    | Nome da coluna com o tipo de linha: `1` é uma linha de "estado", `-1` é uma linha de "cancelamento". | [`Int8`](/docs/pt-BR/reference/data-types/int-uint)                                                                                                                                                                                                                                                            |
| `version` | Nome da coluna com a versão do estado do objeto.                                                     | [`Int*`](/docs/pt-BR/reference/data-types/int-uint), [`UInt*`](/docs/pt-BR/reference/data-types/int-uint), [`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) |

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

Ao criar uma tabela `VersionedCollapsingMergeTree`, são necessárias as mesmas [cláusulas](/docs/pt-BR/reference/engines/table-engines/mergetree-family/mergetree) exigidas na criação de uma tabela `MergeTree`.

<details markdown="1">
  <summary>Método obsoleto de criação de tabela</summary>

  <Note>
    Não use esse método em projetos novos. 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 [=] VersionedCollapsingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, sign, version)
  ```

  Todos os parâmetros, exceto `sign` e `version`, têm o mesmo significado que em `MergeTree`.

  * `sign` — Nome da coluna que indica o tipo da linha: `1` é uma linha de "estado", `-1` é uma linha de "cancelamento".

    Tipo de dados da coluna — `Int8`.

  * `version` — Nome da coluna com a versão do estado do objeto.

    O tipo de dados da coluna deve ser `UInt*`.
</details>

<div id="table_engines_versionedcollapsingmergetree">
  ## colapsamento
</div>

<div id="data">
  ### Dados
</div>

Considere uma situação em que você precisa salvar dados que mudam continuamente de um objeto. É natural ter uma linha para um objeto e atualizar essa linha sempre que houver alterações. No entanto, a operação de atualização é cara e lenta para um SGBD, porque exige reescrever os dados no armazenamento. A atualização não é aceitável se você precisa gravar dados rapidamente, mas pode gravar as alterações de um objeto de forma sequencial, como a seguir.

Use a coluna `Sign` ao gravar a linha. Se `Sign = 1`, isso significa que a linha representa um estado de um objeto (vamos chamá-la de linha de "estado"). Se `Sign = -1`, isso indica o cancelamento do estado de um objeto com os mesmos atributos (vamos chamá-la de linha de "cancelamento"). Use também a coluna `Version`, que deve identificar cada estado de um objeto com um número distinto.

Por exemplo, queremos calcular quantas páginas os usuários visitaram em um site e por quanto tempo permaneceram nele. Em determinado momento, gravamos a seguinte linha com o estado da atividade do usuário:

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │    1 │       1 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

Em algum momento depois, registramos a alteração na atividade do usuário nas duas linhas a seguir.

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │   -1 │       1 |
│ 4324182021466249494 │         6 │      185 │    1 │       2 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

A primeira linha cancela o estado anterior do objeto (usuário). Ela deve copiar todos os campos do estado cancelado, exceto `Sign`.

A segunda linha contém o estado atual.

Como precisamos apenas do último estado da atividade do usuário, as linhas

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │    1 │       1 |
│ 4324182021466249494 │         5 │      146 │   -1 │       1 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

pode ser excluída, colapsando o estado inválido (antigo) do objeto. `VersionedCollapsingMergeTree` faz isso durante a mesclagem das partes de dados.

Para descobrir por que precisamos de duas linhas para cada alteração, consulte [Algorithm](#table_engines-versionedcollapsingmergetree-algorithm).

**Observações sobre o uso**

1. O programa que grava os dados deve manter o estado de um objeto na memória para poder cancelá-lo. A string "Cancel" deve conter cópias dos campos da chave primária, da versão da string "state" e o `Sign` oposto. Isso aumenta o tamanho inicial do armazenamento, mas permite gravar os dados rapidamente.
2. Arrays longos e crescentes em colunas reduzem a eficiência do motor devido à carga de gravação. Quanto mais simples forem os dados, maior será a eficiência.
3. Os resultados de `SELECT` dependem fortemente da consistência do histórico de alterações do objeto. Tenha cuidado ao preparar os dados para inserção. Dados inconsistentes podem produzir resultados imprevisíveis, como valores negativos para métricas não negativas, como a profundidade da sessão.

<div id="table_engines-versionedcollapsingmergetree-algorithm">
  ### Algoritmo
</div>

Quando o ClickHouse faz o merge das partes de dados, ele exclui cada par de linhas que tenha a mesma chave primária e a mesma versão, mas `Sign` diferente. A ordem das linhas não importa.

Quando o ClickHouse insere dados, ele ordena as linhas pela chave primária. Se a coluna `Version` não estiver na chave primária, o ClickHouse a adiciona implicitamente a ela como o último campo e a usa para ordenação.

<div id="selecting-data">
  ## Seleção de dados
</div>

O ClickHouse não garante que todas as linhas com a mesma chave primária ficarão na mesma parte de dados resultante, nem sequer no mesmo servidor físico. Isso vale tanto para a gravação dos dados quanto para a mesclagem posterior das partes de dados. Além disso, o ClickHouse processa consultas `SELECT` com várias threads e não consegue prever a ordem das linhas no resultado. Isso significa que a agregação é necessária quando for preciso obter dados totalmente "colapsados" de uma tabela `VersionedCollapsingMergeTree`.

Para concluir o colapsamento, escreva uma consulta com uma cláusula `GROUP BY` e funções de agregação que levem em conta o sinal. Por exemplo, para calcular a quantidade, use `sum(Sign)` em vez de `count()`. Para calcular a soma de algum valor, use `sum(Sign * x)` em vez de `sum(x)` e adicione `HAVING sum(Sign) > 0`.

Os agregados `count`, `sum` e `avg` podem ser calculados dessa forma. O agregado `uniq` pode ser calculado se um objeto tiver pelo menos um estado não colapsado. Os agregados `min` e `max` não podem ser calculados porque o `VersionedCollapsingMergeTree` não salva o histórico dos valores dos estados colapsados.

Se você precisar extrair os dados com "colapsamento", mas sem agregação (por exemplo, para verificar se há linhas cujos valores mais recentes correspondem a determinadas condições), poderá usar o modificador `FINAL` na cláusula `FROM`. Essa abordagem é ineficiente e não deve ser usada com tabelas grandes.

<div id="example-of-use">
  ## Exemplo de uso
</div>

Dados de exemplo:

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │    1 │       1 |
│ 4324182021466249494 │         5 │      146 │   -1 │       1 |
│ 4324182021466249494 │         6 │      185 │    1 │       2 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

Criando a tabela:

```sql theme={null}
CREATE TABLE UAct
(
    UserID UInt64,
    PageViews UInt8,
    Duration UInt8,
    Sign Int8,
    Version UInt8
)
ENGINE = VersionedCollapsingMergeTree(Sign, Version)
ORDER BY UserID
```

Inserção dos dados:

```sql theme={null}
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, 1, 1)
```

```sql theme={null}
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, -1, 1),(4324182021466249494, 6, 185, 1, 2)
```

Usamos duas consultas `INSERT` para criar duas partes de dados diferentes. Se inserirmos os dados em uma única consulta, o ClickHouse criará apenas uma parte de dados e nunca fará nenhum merge.

Obtendo os dados:

```sql theme={null}
SELECT * FROM UAct
```

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │    1 │       1 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         5 │      146 │   -1 │       1 │
│ 4324182021466249494 │         6 │      185 │    1 │       2 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

O que vemos aqui e onde estão as partes colapsadas?
Criamos duas partes de dados usando duas consultas `INSERT`. A consulta `SELECT` foi executada em duas threads, e o resultado é uma ordem aleatória das linhas.
O colapsamento não ocorreu porque as partes de dados ainda não foram mescladas. O ClickHouse mescla partes de dados em algum momento imprevisível, que não podemos prever.

É por isso que precisamos de agregação:

```sql theme={null}
SELECT
    UserID,
    sum(PageViews * Sign) AS PageViews,
    sum(Duration * Sign) AS Duration,
    Version
FROM UAct
GROUP BY UserID, Version
HAVING sum(Sign) > 0
```

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Version─┐
│ 4324182021466249494 │         6 │      185 │       2 │
└─────────────────────┴───────────┴──────────┴─────────┘
```

Se não precisarmos de agregação e quisermos forçar o colapsamento, podemos usar o modificador `FINAL` na cláusula `FROM`.

```sql theme={null}
SELECT * FROM UAct FINAL
```

```text theme={null}
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │         6 │      185 │    1 │       2 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
```

Esta é uma forma muito ineficiente de selecionar dados. Não a use em tabelas grandes.
