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

# ClickHouse의 Kusto Query Language (KQL)

> 실험적 KQL 방언에서 지원하는 기능과 의도적으로 지원하지 않는 기능

ClickHouse는 SQL 대신 [Kusto Query Language](https://learn.microsoft.com/en-us/kusto/query/)의 부분 집합을
구문 분석할 수 있습니다. 이 방언은 **실험적** 기능이며 기본적으로 비활성화되어 있습니다:

```sql theme={null}
SET allow_experimental_kusto_dialect = 1;
SET dialect = 'kusto';

StormEvents
| where State == 'FLORIDA' and DamageProperty > 0
| summarize Total = sum(DamageProperty) by EventType
| top 5 by Total
```

`SET dialect = 'clickhouse'`를 실행하면 다시 전환됩니다. KQL 방언이 활성화된 상태에서도 인식되는 유일한 SQL 문는 `SET`이므로
session은 언제든지 KQL 방언을 종료할 수 있습니다.

<h2 id="what-is-supported">
  지원 범위
</h2>

지원 범위는 의도적으로 제한되어 있습니다. KQL 구문은 Kusto 문서에 명시된 의미 체계에 따라
변환되거나, 이름을 명시한 구문 분석 오류와 함께 거부됩니다. 어떠한 구문도 조용히 근사 변환되지 않습니다.
쿼리 구문 분석에 성공하면 결과는 Kusto's 결과와 일치하도록 설계되었습니다.

**소스**: 테이블 이름, `print`, `datatable`, `range`, 괄호로 묶인 파이프라인, `union`입니다.
`range`는 숫자에서 숫자 단위로, datetime에서 timespan 단위로, 또는 timespan에서
timespan 단위로 증가합니다.

**연산자**: `where` / `filter`, `extend`, `project`, `project-away`, `project-keep`,
`project-rename`, `summarize`, `sort by` / `order by`, `take` / `limit`, `top`, `distinct`,
`count`, `mv-expand`, `join`, `union`, `as`, `render`.

**스칼라 연산자**: `==`, `!=`, `<`, `<=`, `>`, `>=`, `=~`, `!~`, `in`, `in~`, `between`,
`contains`, `startswith`, `endswith`, `has`, `hasprefix`, `hassuffix`, 이들의 `_cs`
(대소문자 구분) 및 `!`(부정) 형식, `has_any`, `has_all`, `matches regex`입니다.
`in` 및 `!in`은 첫 번째 컬럼에서 값을 제공하는 테이블 표현식도 사용할 수 있습니다
(`x in (T | project key)`). `in~`은 목록만 사용할 수 있습니다. `in (...)` 내부의 단일 이름은
이를 바인딩하는 `let`이 없으면 컬럼으로 해석됩니다. 파서에는 컬럼과 테이블을 구분할 스키마가 없기 때문입니다.
테이블을 `let`으로 바인딩하거나, 한정 이름(`db.table`)을 사용하거나, 파이프를 추가해 테이블 형식으로 지정하십시오.

**SQL 문**: 스칼라, 전체 테이블 표현식 또는 **함수**를 바인딩하는 `let`:

```sql theme={null}
let MultiplyByN = (val: long, n: long = 2) { val * n };
let RecentErrors = (since: timespan) { Logs | where Level == 'Error' and Timestamp > ago(since) };

RecentErrors(1h) | summarize Count = count() by Component
```

함수는 선택적 리터럴 기본값을 갖는 스칼라 매개변수와, 반드시 앞에 와야 하는 `T: (*)` 또는 `T: (col: type, ...)`로
선언된 테이블 형식 매개변수를 받습니다. 컬럼 이름을 지정한 테이블 형식 매개변수는 본문에서 인수의 해당 컬럼만
볼 수 있도록 하므로, 실제 인수에 해당 컬럼이 있더라도 선언되지 않은 컬럼을 참조하는 본문은 거부됩니다. `T: (*)`는
인수를 있는 그대로 전달합니다. 선언된 타입은 호출 시점에 적용됩니다.
타입이 선언된 KQL 타입에 속하지 않는 인수(또는 테이블 형식 인수의 선언된 컬럼)는 거부되며, `long`에서 `real`로의 변환과 같은
무손실 변환은 적용됩니다. 또한 선언된 타입에 맞지 않는 값(예: `int` 오버플로우)은
자동으로 잘리는 대신 오류가 발생합니다. 인수는
이름을 지정해 임의의 순서로 전달할 수 있습니다(`f(c = 7, a = 12)`). 본문은 임의 개수의 `let` 문 다음에
표현식 하나가 오는 형태이며, 자신을 둘러싼 바인딩을 참조할 수 있습니다. 본문이
파이프라인인 함수는 값이 아닌 테이블이므로 표현식이 필요한 위치에서는 거부됩니다 -
`extend`, `where`, `print`에서입니다. 매개변수가 없는 함수는
괄호를 사용하거나 생략하여 호출할 수 있습니다. `view ()`도 허용되며, 여기서는 `union *`
와일드카드를 해석하지 않으므로 `()`와 같은 의미입니다. Kusto와 마찬가지로 재귀는 허용되지 않습니다.

`let`은 바로 뒤에 오는 문에 대해서만 바인딩합니다. KQL 문 하나가 ClickHouse 쿼리 하나이기 때문입니다. 이는 바인딩이 동시 쿼리로
누출되는 것도 방지합니다. 두 문 모두에서 필요한 이름은 두 번 바인딩해야 합니다.

**리터럴**: 문자열(축자 문자열 `@'...'` 포함), 숫자, `datetime(...)`, `guid(...)`,
`1d` / `2.5h` / `500ms`와 같은 기간, 그리고 `dynamic([...])` 배열입니다.

약 130개의 스칼라 및 집계 함수가 변환됩니다. Kusto와 마찬가지로 집계
함수는 `summarize`의 집계 목록에서만 호출할 수 있습니다. `print count()`는
같은 이름의 ClickHouse 집계 함수로 전달되지 않고 거부됩니다.

**ClickHouse 함수**도 사용할 수 있습니다. KQL 레지스트리가 알지 못하는 이름은
작성한 표기 그대로 ClickHouse에 전달되므로 쿼리에서 서버가 제공하는 모든 기능을 사용할 수 있습니다:

```sql theme={null}
SET dialect = 'kusto';

StormEvents
| extend Bucket = toStartOfHour(StartTime), Fingerprint = cityHash64(EventType)
| summarize Events = count() by Bucket
```

예외는 Kusto에서 정의하지만 이 방언에서 구현하지 않는 이름입니다. 이러한 이름은
그대로 전달하는 대신 거부되므로, Kusto 이름이 모르는 사이에 다른 의미로 해석될 수 없습니다.
`range`가 가장 명확한 사례입니다. Kusto에서 `range(1, 3, 1)`은 `[1, 2, 3]`이고
ClickHouse에서는 `[1, 2]`이므로, KQL에서 이를 작성하면 잘못된 결과가 반환되는 대신 오류가 발생합니다.

<h2 id="coverage-against-the-kusto-reference">
  Kusto 참고 자료 대비 지원 범위
</h2>

Microsoft 자체 색인([테이블 형식 연산자](https://learn.microsoft.com/en-us/kusto/query/queries),
[스칼라 함수](https://learn.microsoft.com/en-us/kusto/query/scalar-functions),
[집계 함수](https://learn.microsoft.com/en-us/kusto/query/aggregation-functions))을 기준으로 측정했습니다.

| | Kusto 문서 | 여기서 지원 |
| - | - | - |
| 테이블 형식 연산자 | 52 | 20 |
| 스칼라 및 집계 함수 | 307 | 162 |
| 사용자 정의 함수 | 예 | 예 |

지원되는 연산자는 Microsoft의 *일반적인 연산자 알아보기* Tutorial에서 다루는 연산자에
`datatable`, `range`, `print`, `union`, `join`을 더한 것입니다. 이는 해당 Tutorial과 KQL Quick Reference에서 단계적으로 구성하는 쿼리 형태를 지원하기에 충분합니다.

<h2 id="what-is-not-supported">
  지원되지 않는 항목
</h2>

잘못 번역하는 대신 구문 분석 오류로 거부됩니다.

* 연산자: 연산자로 사용되는 `search`, `parse`, `mv-apply`, `lookup`, `evaluate`, `invoke`, `facet`,
  `top-nested`, `make-series`, `sample`, `serialize`, `partition`, `range`.
* 함수: `series_*` 계열, `bag_*` / `pack_*`, `parse_url`, `parse_csv`,
  `parse_json`, `todynamic`, `toscalar`, `format_timespan`, `format_datetime`, `extract_all`,
  `range`, `percentiles*` 계열 및 `row_*` 윈도 함수.
  (`format_datetime` 및 `extract_all`은 근사 변환하지 않고 거부됩니다. Kusto의
  `yyyy-MM-dd` 포맷 지정자는 ClickHouse의 것과 다르며, Kusto의 `extract_all`은 캡처 그룹마다
  배열 하나를 반환합니다.)
* 의미가 다른 ClickHouse 함수와 이름이 충돌하는 Kusto 함수: 위에 언급한 `range`,
  `repeat`, `replace`, `translate`, `materialize`. 이를 그대로 통과시키면
  아무런 경고 없이 다른 결과가 계산됩니다. Kusto의 `repeat(1, 3)`은 배열 `[1, 1, 1]`인 반면,
  ClickHouse의 `repeat`는 문자열을 반복하므로 모두 이름을 기준으로 거부됩니다.
* GeoJSON을 인수로 받거나 반환하는 지리 공간 함수, 즉 모든 `geo_*_to_central_point` 및
  다각형과 선을 처리하는 모든 함수. 일반 경도/위도 값에서 작동하는 Point, geohash 및 H3 함수는
  지원됩니다. `geo_point_to_s2cell`은 지원되지 않습니다. ClickHouse에는
  S2 토큰 형식이 없습니다.
* `dynamic` **객체** (`dynamic({"a": 1})`), 멤버 액세스 (`x.y`) 및 키 기반 조회
  (`x['k']`). `dynamic` 배열만 ClickHouse `Array`에 매핑됩니다. `datatable`
  스키마, `typeof(...)` 또는 함수 매개변수에서 **선언된 타입**으로 사용된 `dynamic`도
  거부됩니다. 이 주석에는 원소 타입 정보가 없으므로 충실하게 매핑할 대상이 없습니다.
* `cluster(...)`, `database(...)`와 같은 클러스터 간 및 데이터베이스 간 참조.
* 쿼리 및 `join` 힌트(`hint.strategy`, `hint.shufflekey`, ...).
* 연산자 옵션: `mv-expand ... to typeof(T)` / `limit N` / `bagexpansion`, `summarize`
  힌트, `union kind=` / `withsource=` / `isfuzzy=`, `join hint.*`.
* `project-away` 및 `project-keep`의 와일드카드 컬럼 패턴(`project-away Tmp*`):
  이를 확장하려면 스키마가 필요하지만 구문 분석 중에는 스키마를 알 수 없습니다. 컬럼을 명시적으로 나열하십시오.
* `evaluate` 플러그인 메커니즘 전체와 이에 따른 `bag_unpack`, `pivot`, `narrow`,
  `python`, `R` 및 기타 항목.
* 애플리케이션 SQL 문: `alias database`, `declare pattern`,
  `declare query_parameters`, `restrict access to`.
* 난독화된 문자열 리터럴(`h"..."`) 및 여러 줄 리터럴(삼중 백틱).

<h2 id="behaviour-worth-knowing">
  알아두면 좋은 동작
</h2>

* **기간은 `Interval` 값입니다.** `1d`는 `toIntervalNanosecond(86400000000000)`로 변환됩니다.
  숫자가 아닌 Kusto 방식(`1.00:00:00`)으로 표시하려면 `interval_output_format = 'kusto'`를 설정하십시오.
* **나눗셈은 Kusto 방식을 따릅니다**: `7 / 2`는 두 피연산자가 모두 정수이므로 `3`이고,
  기간을 기간으로 나눈 결과는 실수 비율입니다(`15ms / 10ms`는 `1.5`). 이는 인수 타입을 기준으로 결정하는
  `kqlDivide`로 구현됩니다.
* **두 datetime을 빼면 초 단위 숫자가 반환됩니다.** Kusto에서는 기간이 반환됩니다.
  기간을 더하거나 빼는 연산은 예상대로 동작합니다.
* **`sort`는 기본적으로 내림차순이며**, SQL과 달리 null을 작은 값 쪽 끝에 배치합니다.
* **`project-rename`은 이름을 변경한 컬럼을 행의 끝으로 이동합니다.** Kusto는 원래 위치를 유지하지만,
  이를 재현하려면 파싱 중에 스키마를 알아야 합니다.
* **`union`은 피연산자의 스키마가 호환되어야 합니다.** Kusto는 모든 컬럼의 합집합으로 확장하고
  null로 채우지만, ClickHouse의 `UNION ALL`은 그렇지 않습니다.
* **문자열 연산자는 `LIKE` 패턴이 아니라 일치 함수입니다.** `contains '50%'`는
  리터럴 퍼센트 기호를 찾습니다.
* **`geo_*`는 Kusto처럼 위도보다 경도를 먼저 받습니다.** `geo_distance_2points`는
  ClickHouse의 `greatCircleDistance`를 사용합니다. 이는 Kusto와 유효숫자 4번째 자리에서 차이가 나는
  빠른 근삿값으로, 1500km에서 약 600m의 차이가 납니다. `use_spheroid = true`를 설정하면 Kusto처럼
  타원체 공식인 `geoDistance`가 선택됩니다. 정확한 일치는 목표가 아닙니다.
  이러한 함수는 일반적으로 보고용이 아니라 필터링용으로 사용됩니다. \[-180, 180] 또는 \[-90, 90] 범위를 벗어난 좌표는
  Kusto가 반환하는 null 대신 의미 없는 숫자를 반환합니다. ClickHouse 함수는 어느 것도 인수 범위를
  검사하지 않으며, 검사하려면 행당 8번의 비교가 필요합니다.
* **`dayofweek()`는 숫자가 아닌 기간을 반환합니다**: 월요일은 `1.00:00:00`입니다.
* **`tohex()`는 음수 값을 64비트 너비로 표시합니다.** Kusto는 인수 자체 타입의 너비로 표시하지만,
  이는 파싱 중에는 알 수 없습니다.
* **datetime은 실제 `DateTime64` 값입니다.** 따라서 Kusto의 `2017-01-01T00:00:00.0000000` 대신
  ClickHouse 방식(`2017-01-01 00:00:00`)으로 출력됩니다. 이전 구현은 Kusto처럼 보이지만 datetime으로
  비교하거나 정렬할 수 없는 포맷된 *문자열*을 생성했습니다.
* **ClickHouse의 매개변수 집계 함수**(`quantileExact(0.5)(x)`)에는 KQL 표기법이 없습니다.
  `medianExact(x)`와 같은 이름이 지정된 대안을 사용하십시오.

<h2 id="reporting-a-problem">
  문제 신고
</h2>

구문 분석은 되지만 Kusto에서 반환하지 않는 결과를 반환하는 쿼리는 버그입니다. 두 결과를 함께 첨부하여 신고하십시오. 거부되었지만 필요한 쿼리는 기능 요청에 해당합니다. 위 목록은 거부되는 모든 이름을 열거한 것이 아니라 대략적인 경계를 보여 주는 것이며, 특정 쿼리에 대한 최종 답변은 구문 분석 오류 자체입니다. 또한 이 내용은 영구적으로 유지되지 않습니다.
