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

> 동일한 정렬 키 값(`ORDER BY` 테이블 절, `PRIMARY KEY` 아님)을 가진 중복 레코드를 제거한다는 점에서 MergeTree와 다릅니다.

# ReplacingMergeTree 테이블 엔진

이 엔진은 동일한 [정렬 키](/docs/ko/reference/engines/table-engines/mergetree-family/mergetree) 값(`ORDER BY` 테이블 절, `PRIMARY KEY` 아님)을 가진 중복 레코드를 제거한다는 점에서 [MergeTree](/docs/ko/reference/engines/table-engines/mergetree-family/mergetree)와 다릅니다.

데이터 중복 제거는 머지 중에만 발생합니다. 머지는 시점을 알 수 없는 상태로 백그라운드에서 수행되므로 이를 계획할 수 없습니다. 일부 데이터는 처리되지 않은 채 남아 있을 수 있습니다. `OPTIMIZE` 쿼리를 사용해 예정되지 않은 머지를 실행할 수는 있지만, `OPTIMIZE` 쿼리는 대량의 데이터를 읽고 쓰므로 이에 의존해서는 안 됩니다.

따라서 `ReplacingMergeTree`는 공간 절약을 위해 백그라운드에서 중복 데이터를 정리하는 데는 적합하지만, 중복이 전혀 없다고 보장하지는 않습니다.

<Note>
  모범 사례와 성능 최적화 방법을 포함한 ReplacingMergeTree의 자세한 가이드는 [여기](/docs/ko/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, ...]
```

요청 매개변수에 대한 설명은 [SQL 문 설명](/docs/ko/reference/statements/create/table)을 참조하십시오.

<Note>
  행의 고유성은 `PRIMARY KEY`가 아니라 테이블의 `ORDER BY` 테이블 절에 따라 결정됩니다.
</Note>

<div id="replacingmergetree-parameters">
  ## ReplacingMergeTree 매개변수
</div>

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

`ver` — 버전 번호를 담는 컬럼입니다. 유형은 `UInt*`, `Date`, `DateTime` 또는 `DateTime64`입니다. 선택적 매개변수입니다.

머지 시 `ReplacingMergeTree`는 동일한 정렬 키를 가진 모든 행 중 하나만 남깁니다.

* `ver`가 설정되지 않은 경우 선택(selection)에서 마지막 행을 남깁니다. selection은 머지에 참여하는 파트 집합 내 행들의 집합입니다. 가장 최근에 생성된 파트(마지막 삽입)가 selection에서 마지막이 됩니다. 따라서 중복 제거 후에는 각 고유한 정렬 키별로 가장 최근 삽입에서 들어온 마지막 행이 남습니다.
* `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` — 머지 중 이 행의 데이터가 현재 state를 나타내는지, 또는 삭제 대상인지를 판단하는 데 사용하는 컬럼 이름입니다. `1`은 "deleted" 행이고, `0`은 "state" 행입니다.

컬럼의 데이터 타입 — `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` 테이블을 생성할 때는 `MergeTree` 테이블을 생성할 때와 동일한 [절](/docs/ko/reference/engines/table-engines/mergetree-family/mergetree)이 필요합니다.

<details markdown="1">
  <summary>Deprecated 테이블 생성 방법</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` 성능 최적화 방법을 포함한 `FINAL`의 자세한 내용은 [ReplacingMergeTree 자세히 알아보기](/docs/ko/concepts/features/operations/update/replacing-merge-tree)를 참조하십시오.
