> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-parallel-read-in-order-multi-part.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> DISTINCT 절 문서

# DISTINCT

`SELECT DISTINCT`를 지정하면 쿼리 결과에는 고유한 행만 남습니다. 즉, 결과에서 완전히 일치하는 행들의 각 집합마다 행 1개만 남습니다.

고유한 값을 가져야 하는 컬럼 목록을 지정할 수 있습니다: `SELECT DISTINCT ON (column1, column2,...)`. 컬럼을 지정하지 않으면 모든 컬럼을 기준으로 판단합니다.

다음 테이블을 예로 살펴보겠습니다:

```text theme={null}
┌─a─┬─b─┬─c─┐
│ 1 │ 1 │ 1 │
│ 1 │ 1 │ 1 │
│ 2 │ 2 │ 2 │
│ 2 │ 2 │ 2 │
│ 1 │ 1 │ 2 │
│ 1 │ 2 │ 2 │
└───┴───┴───┘
```

컬럼을 지정하지 않고 `DISTINCT`를 사용하는 경우:

```sql theme={null}
SELECT DISTINCT * FROM t1;
```

```text theme={null}
┌─a─┬─b─┬─c─┐
│ 1 │ 1 │ 1 │
│ 2 │ 2 │ 2 │
│ 1 │ 1 │ 2 │
│ 1 │ 2 │ 2 │
└───┴───┴───┘
```

지정한 컬럼과 함께 `DISTINCT` 사용:

```sql theme={null}
SELECT DISTINCT ON (a,b) * FROM t1;
```

```text theme={null}
┌─a─┬─b─┬─c─┐
│ 1 │ 1 │ 1 │
│ 2 │ 2 │ 2 │
│ 1 │ 2 │ 2 │
└───┴───┴───┘
```

## DISTINCT와 ORDER BY

ClickHouse는 하나의 쿼리에서 서로 다른 컬럼에 `DISTINCT` 절과 `ORDER BY` 절을 함께 사용할 수 있습니다. `DISTINCT` 절은 `ORDER BY` 절보다 먼저 실행됩니다.

다음 테이블을 예로 살펴보겠습니다:

```text theme={null}
┌─a─┬─b─┐
│ 2 │ 1 │
│ 1 │ 2 │
│ 3 │ 3 │
│ 2 │ 4 │
└───┴───┘
```

데이터 조회:

```sql theme={null}
SELECT DISTINCT a FROM t1 ORDER BY b ASC;
```

```text theme={null}
┌─a─┐
│ 2 │
│ 1 │
│ 3 │
└───┘
```

서로 다른 정렬 방향으로 데이터 선택하기:

```sql theme={null}
SELECT DISTINCT a FROM t1 ORDER BY b DESC;
```

```text theme={null}
┌─a─┐
│ 3 │
│ 1 │
│ 2 │
└───┘
```

행 `2, 4`는 정렬 전에 잘려 나갔습니다.

쿼리를 작성할 때는 이러한 구현상의 특이사항을 고려하십시오.

## NULL 처리

`DISTINCT`는 [NULL](/ko/reference/syntax#null)을 `NULL`이 특정 값이며 `NULL==NULL`인 것처럼 취급합니다. 즉, `DISTINCT` 결과에서는 `NULL`을 포함한 서로 다른 조합도 한 번만 나타납니다. 이는 대부분의 다른 문맥에서의 `NULL` 처리 방식과 다릅니다.

## 대안

집계 함수를 사용하지 않고 `SELECT` 절에 지정된 것과 동일한 값 집합에 [GROUP BY](/ko/reference/statements/select/group-by)를 적용해도 동일한 결과를 얻을 수 있습니다. 다만 `GROUP BY` 방식과 비교하면 몇 가지 차이점이 있습니다.

* `DISTINCT`는 `GROUP BY`와 함께 사용할 수 있습니다.
* 외부 실행이 시작되기 전이라면, [ORDER BY](/ko/reference/statements/select/order-by)가 없는 쿼리는 [LIMIT](/ko/reference/statements/select/limit)를 충족할 만큼 서로 다른 행을 읽는 즉시 중지될 수 있습니다.
* 외부 실행이 시작되기 전이고 `ORDER BY`가 생략된 경우, `ALL` 없이 지정한 [`LIMIT ... AFTER ... UNTIL`](/ko/reference/statements/select/limit#limit-after-until) 범위 역시 범위가 끝나면 쿼리를 중지시킬 수 있습니다.
* 데이터 블록은 외부 실행이 시작되기 전까지 처리되는 대로 출력됩니다.

## 외부 메모리를 사용하는 DISTINCT

`DISTINCT`는 메모리에 모두 담기에는 너무 큰 고유 값 집합을 처리하기 위해 임시 데이터를 디스크에 쓸 수 있습니다. 이 경우 추가적인 디스크 I/O가 발생하여 쿼리 속도가 느려질 수 있습니다.

스필이 시작되는 시점은 두 가지 설정으로 제어합니다:

* `max_bytes_before_external_distinct`는 전체 쿼리 메모리에 대한 임계값을 바이트 단위로 지정합니다. 기본값은 `0`(비활성화)입니다.
* `max_bytes_ratio_before_external_distinct`는 서버 또는 사용자 제한 내에서 사용 가능한 메모리의 비율을 지정하며, 이 값은 실행 시작 시점에 측정됩니다. 기본값은 `0.5`이며, 두 제한 중 어느 것도 적용되지 않으면 아무런 효과가 없습니다.

두 임계값이 모두 적용되는 경우 더 작은 값이 사용됩니다. 스필을 비활성화하려면 두 설정을 모두 `0`으로 지정하십시오.

`max_memory_usage`는 이 비율에 영향을 주지 않습니다. 쿼리 메모리 제한을 기준으로 스필을 구성하려면 해당 제한보다 낮은 절대 임계값을 지정하십시오. 예를 들어 다음 쿼리는 256 MiB의 쿼리 메모리 제한과 16 MiB의 스필 임계값을 사용합니다:

```sql theme={null}
SELECT DISTINCT number % 1000000 AS id
FROM numbers(2000000)
SETTINGS
    max_bytes_before_external_distinct = 16777216,
    max_bytes_ratio_before_external_distinct = 0,
    max_memory_usage = 268435456;
```

이러한 임계값이 메모리 사용량의 상한을 정하지는 않습니다. 다른 쿼리 처리와 스필 자체를 위한 여유를 남겨 두십시오. 메모리 압박 상황에서는 스필이 더 일찍 시작될 수도 있습니다.

스필이 시작되기 전에 행이 반환될 수 있으며, 이 단계에서 `LIMIT`이 충족되면 쿼리가 조기에 완료될 수 있습니다.
일단 스필이 시작되면 나머지 결과를 반환하기 전에 남은 입력을 모두 읽어야 합니다.
쿼리에 `ORDER BY`가 포함된 경우 해당 결과는 요청된 순서대로 반환됩니다.

`DISTINCT`가 키의 접두사로 정렬된 입력을 사용하는 경우에는 스필이 발생하지 않습니다. 다만 동일한 접두사를 가진 행 그룹이 크면 여전히 상당한 메모리를 사용할 수 있습니다.

`optimize_distinct_in_order`와 마찬가지로, 스필 과정에서 이진 표현은 다르지만 비교 시 같은 값으로 판단되는 부동소수점 값이 중복 제거될 수 있습니다. 여기에는 `0.0`과 `-0.0`, 또는 페이로드가 다른 `NaN` 값이 포함됩니다.
