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

> отличается от MergeTree тем, что удаляет повторяющиеся записи с одинаковым значением ключа сортировки (раздел таблицы `ORDER BY`, а не `PRIMARY KEY`).

# Движок таблицы ReplacingMergeTree

Этот движок отличается от [MergeTree](/docs/ru/reference/engines/table-engines/mergetree-family/mergetree) тем, что удаляет повторяющиеся записи с одинаковым значением [ключа сортировки](/docs/ru/reference/engines/table-engines/mergetree-family/mergetree) (раздел таблицы `ORDER BY`, а не `PRIMARY KEY`).

Дедупликация данных происходит только во время слияния. Слияние выполняется в фоновом режиме в непредсказуемый момент, поэтому заранее рассчитывать на него нельзя. Часть данных может остаться необработанной. Хотя вы можете запустить внеплановое слияние с помощью запроса `OPTIMIZE`, полагаться на это не стоит, поскольку запрос `OPTIMIZE` считывает и записывает большой объем данных.

Таким образом, `ReplacingMergeTree` подходит для фонового удаления дубликатов ради экономии места, но не гарантирует полного отсутствия дубликатов.

<Note>
  Подробное руководство по ReplacingMergeTree, включая рекомендации и способы оптимизации производительности, доступно [здесь](/docs/ru/concepts/features/operations/update/replacing-merge-tree).
</Note>

<div id="creating-a-table">
  ## Создание таблицы
</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 = ReplacingMergeTree([ver [, is_deleted]])
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
```

Описание параметров запроса см. в разделе [описание оператора](/docs/ru/reference/statements/create/table).

<Note>
  Уникальность строк определяется разделом таблицы `ORDER BY`, а не `PRIMARY KEY`.
</Note>

<div id="replacingmergetree-parameters">
  ## Параметры ReplacingMergeTree
</div>

<div id="ver">
  ### `ver`
</div>

`ver` — столбец с номером версии. Тип: `UInt*`, `Date`, `DateTime` или `DateTime64`. Необязательный параметр.

При слиянии `ReplacingMergeTree` из всех строк с одинаковым ключом сортировки оставляет только одну:

* Последнюю в выборке, если `ver` не задан. Выборка — это набор строк из набора частей, участвующих в слиянии. Последней в выборке будет строка из самой недавно созданной части (то есть из последней вставки). Таким образом, после дедупликации для каждого уникального ключа сортировки останется самая последняя строка из самой недавней вставки.
* С максимальной версией, если указан `ver`. Если `ver` одинаков для нескольких строк, то для них будет применяться правило «если `ver` не задан», то есть останется строка, вставленная последней.

Пример:

```sql theme={null}
-- без ver - побеждает последняя вставленная строка
CREATE TABLE myFirstReplacingMT
(
    `key` Int64,
    `someCol` String,
    `eventTime` DateTime
)
ENGINE = ReplacingMergeTree
ORDER BY key;

INSERT INTO myFirstReplacingMT Values (1, 'first', '2020-01-01 01:01:01');
INSERT INTO myFirstReplacingMT Values (1, 'second', '2020-01-01 00:00:00');

SELECT * FROM myFirstReplacingMT FINAL;

┌─key─┬─someCol─┬───────────eventTime─┐
│   1 │ second  │ 2020-01-01 00:00:00 │
└─────┴─────────┴─────────────────────┘

-- с ver - побеждает строка с наибольшим ver
CREATE TABLE mySecondReplacingMT
(
    `key` Int64,
    `someCol` String,
    `eventTime` DateTime
)
ENGINE = ReplacingMergeTree(eventTime)
ORDER BY key;

INSERT INTO mySecondReplacingMT Values (1, 'first', '2020-01-01 01:01:01');
INSERT INTO mySecondReplacingMT Values (1, 'second', '2020-01-01 00:00:00');

SELECT * FROM mySecondReplacingMT FINAL;

┌─key─┬─someCol─┬───────────eventTime─┐
│   1 │ first   │ 2020-01-01 01:01:01 │
└─────┴─────────┴─────────────────────┘
```

<div id="is_deleted">
  ### `is_deleted`
</div>

`is_deleted` — имя столбца, используемого при слиянии для определения того, представляет ли данные в этой строке состояние или строка должна быть удалена; `1` — это строка "удалена", `0` — строка "состояние".

Тип данных столбца — `UInt8`.

<Note>
  `is_deleted` можно включить только при использовании `ver`.

  Независимо от операции с данными, версию следует увеличивать. Если две вставленные строки имеют одинаковый номер версии, сохраняется последняя вставленная строка.

  По умолчанию ClickHouse сохраняет последнюю строку для ключа, даже если это строка удаления. Это сделано для того, чтобы любые будущие строки с меньшими версиями можно было
  безопасно вставлять, и строка удаления всё равно применялась.

  Чтобы навсегда удалить такие строки удаления, включите настройку таблицы `allow_experimental_replacing_merge_with_cleanup` и выполните одно из следующих действий:

  1. Установите настройки таблицы `enable_replacing_merge_with_cleanup_for_min_age_to_force_merge`, `min_age_to_force_merge_on_partition_only` и `min_age_to_force_merge_seconds`. Если все части в партиции старше `min_age_to_force_merge_seconds`, ClickHouse объединит их
     в одну часть и удалит все строки удаления.

  2. Вручную выполните `OPTIMIZE TABLE table [PARTITION partition | PARTITION ID 'partition_id'] FINAL CLEANUP`.
</Note>

Пример:

```sql theme={null}
-- с ver и is_deleted
CREATE OR REPLACE TABLE myThirdReplacingMT
(
    `key` Int64,
    `someCol` String,
    `eventTime` DateTime,
    `is_deleted` UInt8
)
ENGINE = ReplacingMergeTree(eventTime, is_deleted)
ORDER BY key
SETTINGS allow_experimental_replacing_merge_with_cleanup = 1;

INSERT INTO myThirdReplacingMT Values (1, 'first', '2020-01-01 01:01:01', 0);
INSERT INTO myThirdReplacingMT Values (1, 'first', '2020-01-01 01:01:01', 1);

select * from myThirdReplacingMT final;

0 rows in set. Elapsed: 0.003 sec.

-- удалить строки с is_deleted
OPTIMIZE TABLE myThirdReplacingMT FINAL CLEANUP;

INSERT INTO myThirdReplacingMT Values (1, 'first', '2020-01-01 00:00:00', 0);

select * from myThirdReplacingMT final;

┌─key─┬─someCol─┬───────────eventTime─┬─is_deleted─┐
│   1 │ first   │ 2020-01-01 00:00:00 │          0 │
└─────┴─────────┴─────────────────────┴────────────┘
```

<div id="query-clauses">
  ## Секции запроса
</div>

При создании таблицы `ReplacingMergeTree` требуются те же [секции](/docs/ru/reference/engines/table-engines/mergetree-family/mergetree), что и при создании таблицы `MergeTree`.

<details markdown="1">
  <summary>Устаревший метод создания таблицы</summary>

  <Note>
    Не используйте этот метод в новых проектах и, по возможности, переведите существующие проекты на метод, описанный выше.
  </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 [=] ReplacingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [ver])
  ```

  Все параметры, кроме `ver`, имеют тот же смысл, что и в `MergeTree`.

  * `ver` — столбец с версией. Необязательный параметр. Описание см. в тексте выше.
</details>

<div id="query-time-de-duplication--final">
  ## Дедупликация во время выполнения запроса & FINAL
</div>

Во время слияния ReplacingMergeTree выявляет повторяющиеся строки, используя значения столбцов `ORDER BY` (применяемых при создании таблицы) как уникальный идентификатор, и сохраняет только строку с наибольшей версией. Однако это обеспечивает лишь отложенную корректность: нет гарантии, что строки будут дедуплицированы, поэтому полагаться на это не следует. В результате запросы могут возвращать неверные результаты, поскольку при их выполнении могут учитываться строки обновления и удаления.

Чтобы получать корректные результаты, пользователям нужно дополнять фоновые слияния дедупликацией во время выполнения запроса и исключением удалённых строк. Это можно сделать с помощью оператора `FINAL`. Например, рассмотрим следующий пример:

```sql theme={null}
CREATE TABLE rmt_example
(
    `number` UInt16
)
ENGINE = ReplacingMergeTree
ORDER BY number

INSERT INTO rmt_example SELECT floor(randUniform(0, 100)) AS number
FROM numbers(1000000000)

0 rows in set. Elapsed: 19.958 sec. Processed 1.00 billion rows, 8.00 GB (50.11 million rows/s., 400.84 MB/s.)
```

Запрос без `FINAL` возвращает неверное число (точный результат зависит от слияний):

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

┌─count()─┐
│     200 │
└─────────┘

1 row in set. Elapsed: 0.002 sec.
```

При добавлении FINAL получается правильный результат:

```sql theme={null}
SELECT count()
FROM rmt_example
FINAL

┌─count()─┐
│     100 │
└─────────┘

1 row in set. Elapsed: 0.002 sec.
```

Более подробную информацию о `FINAL`, в том числе о том, как оптимизировать его производительность, см. в нашем [подробном руководстве по ReplacingMergeTree](/docs/ru/concepts/features/operations/update/replacing-merge-tree).
