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

> EXPLAIN 문서

# EXPLAIN 문

SQL 문의 실행 계획을 보여줍니다.

<div class="vimeo-container">
  <Frame>
    <iframe
      src="//www.youtube.com/embed/hP6G2Nlz_cA"
      frameborder="0"
      allow="autoplay;
fullscreen;
picture-in-picture"
      allowfullscreen
    />
  </Frame>
</div>

구문:

```sql theme={null}
EXPLAIN [AST | SYNTAX | QUERY TREE | PLAN | PIPELINE | ESTIMATE | TABLE OVERRIDE | WHATIF] [setting = value, ...]
    [
      SELECT ... |
      tableFunction(...) [COLUMNS (...)] [ORDER BY ...] [PARTITION BY ...] [PRIMARY KEY] [SAMPLE BY ...] [TTL ...]
    ]
    [FORMAT ...]
```

예시:

```sql theme={null}
EXPLAIN SELECT sum(number) FROM numbers(10) UNION ALL SELECT sum(number) FROM numbers(10) ORDER BY sum(number) ASC FORMAT TSV;
```

```sql theme={null}
Output: sum(number)

Union
├──Aggregating
│  │  Keys:
│  │  Aggregates: sum(number)
│  │  Skip merging: 0
│  └──ReadFromSystemNumbers
│        Output: number
└──Sorting (Sorting for ORDER BY)
   │  Sort description: sum(number) ASC
   └──Aggregating
      │  Keys:
      │  Aggregates: sum(number)
      │  Skip merging: 0
      └──ReadFromSystemNumbers
            Output: number
```

<div id="explain-types">
  ## EXPLAIN 타입
</div>

* `AST` — 추상 구문 트리입니다.
* `SYNTAX` — AST 수준 최적화가 적용된 후의 쿼리 텍스트입니다.
* `QUERY TREE` — 쿼리 트리 수준 최적화가 적용된 후의 쿼리 트리입니다.
* `PLAN` — 쿼리 실행 계획입니다.
* `PIPELINE` — 쿼리 실행 파이프라인입니다.

<div id="explain-ast">
  ### EXPLAIN AST
</div>

쿼리 AST를 출력합니다. `SELECT`뿐 아니라 모든 쿼리를 지원합니다.

설정:

* `graph` – [DOT](https://en.wikipedia.org/wiki/DOT_\(graph_description_language\)) 그래프 기술 언어로 표현된 그래프 형태로 AST를 출력합니다. 기본값: 0.

예시:

```sql theme={null}
EXPLAIN AST SELECT 1;
```

```sql theme={null}
SelectWithUnionQuery (children 1)
 ExpressionList (children 1)
  SelectQuery (children 1)
   ExpressionList (children 1)
    Literal UInt64_1
```

```sql theme={null}
EXPLAIN AST ALTER TABLE t1 DELETE WHERE date = today();
```

```sql theme={null}
  explain
  AlterQuery  t1 (children 1)
   ExpressionList (children 1)
    AlterCommand 27 (children 1)
     Function equals (children 1)
      ExpressionList (children 2)
       Identifier date
       Function today (children 1)
        ExpressionList
```

<div id="explain-syntax">
  ### EXPLAIN 구문
</div>

구문 분석이 끝난 후 쿼리의 추상 구문 트리(AST)를 표시합니다.

쿼리를 파싱해 쿼리 AST와 쿼리 트리를 구성하고, 필요에 따라 쿼리 분석기와 최적화 패스를 실행한 뒤, 쿼리 트리를 다시 쿼리 AST로 변환하는 방식으로 수행됩니다.

설정:

* `oneline` – 쿼리를 한 줄로 출력합니다. 기본값: `0`.
* `run_query_tree_passes` – 쿼리 트리를 덤프하기 전에 쿼리 트리 패스를 실행합니다. 기본값: `0`.
* `query_tree_passes` – `run_query_tree_passes`가 설정된 경우 실행할 패스 수를 지정합니다. `query_tree_passes`를 지정하지 않으면 모든 패스를 실행합니다.

예시:

```sql title="Query" theme={null}
EXPLAIN SYNTAX SELECT * FROM system.numbers AS a, system.numbers AS b, system.numbers AS c WHERE a.number = b.number AND b.number = c.number;
```

```sql title="Response" theme={null}
SELECT *
FROM system.numbers AS a, system.numbers AS b, system.numbers AS c
WHERE (a.number = b.number) AND (b.number = c.number)
```

`run_query_tree_passes`를 사용하는 경우:

```sql title="Query" theme={null}
EXPLAIN SYNTAX run_query_tree_passes = 1 SELECT * FROM system.numbers AS a, system.numbers AS b, system.numbers AS c WHERE a.number = b.number AND b.number = c.number;
```

```sql title="Response" theme={null}
SELECT
    __table1.number AS `a.number`,
    __table2.number AS `b.number`,
    __table3.number AS `c.number`
FROM system.numbers AS __table1
ALL INNER JOIN system.numbers AS __table2 ON __table1.number = __table2.number
ALL INNER JOIN system.numbers AS __table3 ON __table2.number = __table3.number
```

<div id="explain-query-tree">
  ### EXPLAIN QUERY TREE
</div>

설정:

* `run_passes` — 쿼리 트리를 덤프하기 전에 모든 쿼리 트리 패스를 실행합니다. 기본값: `1`.
* `dump_passes` — 쿼리 트리를 덤프하기 전에 사용된 패스 정보를 덤프합니다. 기본값: `0`.
* `passes` — 실행할 패스 수를 지정합니다. `-1`로 설정하면 모든 패스를 실행합니다. 기본값: `-1`.
* `dump_tree` — 쿼리 트리를 표시합니다. 기본값: `1`.
* `dump_ast` — 쿼리 트리에서 생성된 쿼리 AST를 표시합니다. 기본값: `0`.

예시:

```sql theme={null}
EXPLAIN QUERY TREE SELECT id, value FROM test_table;
```

```sql theme={null}
QUERY id: 0
  PROJECTION COLUMNS
    id UInt64
    value String
  PROJECTION
    LIST id: 1, nodes: 2
      COLUMN id: 2, column_name: id, result_type: UInt64, source_id: 3
      COLUMN id: 4, column_name: value, result_type: String, source_id: 3
  JOIN TREE
    TABLE id: 3, table_name: default.test_table
```

<div id="explain-plan">
  ### EXPLAIN PLAN
</div>

쿼리 계획 단계를 덤프합니다.

설정:

* `optimize` — 계획을 표시하기 전에 쿼리 계획 최적화를 적용할지 제어합니다. 기본값: 1.
* `header` — 단계의 출력 헤더를 출력합니다. 기본값: 0.
* `description` — 단계 설명을 출력합니다. 기본값: 1.
* `indexes` — 사용된 인덱스와, 적용된 각 인덱스별로 필터링된 파트 수 및 필터링된 그래뉼 수를 표시합니다. 기본값: 0. [MergeTree](/ko/reference/engines/table-engines/mergetree-family/mergetree) 테이블에서 지원됩니다. ClickHouse >= v25.9부터는 이 문을 `SETTINGS use_query_condition_cache = 0, use_skip_indexes_on_data_read = 0`와 함께 사용할 때만 출력이 적절하게 표시됩니다.
* `projections` — 분석된 모든 프로젝션과, 프로젝션 프라이머리 키 조건에 기반한 파트 수준 필터링에 미치는 영향을 표시합니다. 각 프로젝션에 대해 이 섹션에는 프로젝션의 프라이머리 키를 사용해 평가된 파트 수, 행 수, 마크 수, 범위 수 등의 통계가 포함됩니다. 또한 프로젝션 자체를 읽지 않고도 이 필터링으로 인해 건너뛴 데이터 파트 수를 보여줍니다. 프로젝션이 실제 읽기에 사용되었는지, 아니면 필터링용으로만 분석되었는지는 `description` 필드로 확인할 수 있습니다. 기본값: 0. [MergeTree](/ko/reference/engines/table-engines/mergetree-family/mergetree) 테이블에서 지원됩니다.
* `actions` — 단계 작업에 대한 자세한 정보를 출력합니다. 기본값: 1.
* `sorting` — 정렬된 출력을 생성하는 각 계획 단계의 정렬 설명을 출력합니다. 기본값: 0.
* `keep_logical_steps` — 조인을 물리적 조인 구현으로 변환하지 않고 논리적 계획 단계를 유지합니다. 기본값: 0.
* `json` — 쿼리 계획 단계를 [JSON](/ko/reference/formats/JSON/JSON) 포맷의 한 행으로 출력합니다. 기본값: 0. 불필요한 이스케이프를 피하려면 [TabSeparatedRaw (TSVRaw)](/ko/reference/formats/TabSeparated/TabSeparatedRaw) 포맷을 사용하는 것이 좋습니다.
* `input_headers` — 단계의 입력 헤더를 출력합니다. 기본값: 0. 주로 입력-출력 헤더 불일치와 관련된 문제를 디버깅하는 개발자에게만 유용합니다.
* `column_structure` — 헤더에서 컬럼의 이름과 유형뿐 아니라 구조도 함께 출력합니다. 기본값: 0. 주로 입력-출력 헤더 불일치와 관련된 문제를 디버깅하는 개발자에게만 유용합니다.
* `distributed` — 분산 테이블 또는 병렬 레플리카의 원격 노드에서 실행되는 쿼리 계획을 표시합니다. `json`과 함께는 지원되지 않습니다. 기본값: 0.
* `compact` — 활성화하면 계획에서 표현식 단계와 자세한 작업 정보(입력, 함수, aliases, 출력 위치)를 숨깁니다. `actions = 1`일 때만 효과가 있습니다. 기본값: 1.
* `pretty` — 들여쓰기 대신 선 그리기 문자(├──, └──, │)를 사용해 계층 구조를 시각화한 계획 트리를 출력합니다. 또한 조인 단계 속성을 인라인으로 포맷합니다. 기본값: 1.

<Note>
  기본적으로 `explain_query_plan_default = 'pretty'`이므로 `actions`, `compact`, `pretty`는 `1`로 초기화되며 계획은 compact, pretty, action 주석이 포함된 형태로 렌더링됩니다. `EXPLAIN` 문에서 이러한 옵션 중 하나를 명시적으로 지정하면(예: `EXPLAIN actions = 0, compact = 0, pretty = 0 SELECT ...`) 항상 기본값을 재정의합니다.

  ClickHouse 26.7 이전에는 `actions`, `compact`, `pretty`의 기본값이 `0`이었습니다. `explain_query_plan_default = 'legacy'`로 설정하거나(전역 또는 쿼리별 `SETTINGS`에서), `compatibility`를 `26.7`보다 이전 버전으로 설정하면 여전히 해당 출력을 얻을 수 있습니다.

  `json` 및 `distributed` 옵션은 `explain_query_plan_default = 'pretty'`인 경우에도 `pretty` 기본값(`actions`, `compact`, `pretty`)을 활성화하지 않습니다. 해당 출력에 action 세부 정보를 포함하려면 `actions = 1`을 수동으로 설정하십시오.
</Note>

예시:

```sql theme={null}
EXPLAIN SELECT sum(number) FROM numbers(10) GROUP BY number % 4  LIMIT 1;
```

```sql theme={null}
Output: sum(number)

Limit (preliminary LIMIT)
│  Limit 1
│  Offset 0
└──Aggregating
   │  Keys: number MOD 4
   │  Aggregates: sum(number)
   │  Skip merging: 0
   └──ReadFromSystemNumbers
         Output: number
```

<Note>
  단계별 비용 및 쿼리 비용 추정은 지원되지 않습니다.
</Note>

`json = 1`이면 쿼리 계획이 JSON 포맷으로 표시됩니다. 모든 노드는 항상 `Node Type`, `Node Id`, `Plans` 키를 가지는 딕셔너리입니다. `Node Type`은 단계 이름을 나타내는 문자열이고, `Node Id`는 고유한 단계 식별자입니다(숫자 접미사가 붙은 단계 이름으로, 예: `Union_10`). `Plans`는 하위 단계 설명을 담은 배열입니다. 그 밖의 선택적 키는 노드 유형과 설정에 따라 추가될 수 있습니다.

예시:

```sql theme={null}
EXPLAIN json = 1, description = 0 SELECT 1 UNION ALL SELECT 2 FORMAT TSVRaw;
```

```json theme={null}
[
  {
    "Plan": {
      "Node Type": "Union",
      "Node Id": "Union_10",
      "Plans": [
        {
          "Node Type": "Expression",
          "Node Id": "Expression_13",
          "Plans": [
            {
              "Node Type": "ReadFromStorage",
              "Node Id": "ReadFromStorage_0"
            }
          ]
        },
        {
          "Node Type": "Expression",
          "Node Id": "Expression_16",
          "Plans": [
            {
              "Node Type": "ReadFromStorage",
              "Node Id": "ReadFromStorage_4"
            }
          ]
        }
      ]
    }
  }
]
```

`description` = 1이면 `Description` 키가 해당 단계에 추가됩니다:

```json theme={null}
{
  "Node Type": "ReadFromStorage",
  "Description": "SystemOne"
}
```

`header` = 1이면 `Header` 키가 컬럼 배열 형태로 해당 단계에 추가됩니다.

예시:

```sql theme={null}
EXPLAIN json = 1, description = 0, header = 1 SELECT 1, 2 + dummy;
```

```json theme={null}
[
  {
    "Plan": {
      "Node Type": "Expression",
      "Node Id": "Expression_5",
      "Header": [
        {
          "Name": "1",
          "Type": "UInt8"
        },
        {
          "Name": "plus(2, dummy)",
          "Type": "UInt16"
        }
      ],
      "Plans": [
        {
          "Node Type": "ReadFromStorage",
          "Node Id": "ReadFromStorage_0",
          "Header": [
            {
              "Name": "dummy",
              "Type": "UInt8"
            }
          ]
        }
      ]
    }
  }
]
```

`indexes` = 1이면 `Indexes` 키가 추가됩니다. 이 키에는 사용된 인덱스의 배열이 포함됩니다. 각 인덱스는 `Type` 키(문자열 `Partition Min-Max`, `Partition`, `Statistics`, `PrimaryKey` 또는 `Skip`)와 선택적 키를 포함하는 JSON으로 설명됩니다.

* `Name` — 인덱스 이름입니다(현재는 `Skip` 인덱스에만 사용됩니다).
* `Keys` — 인덱스에 사용되는 컬럼 배열입니다.
* `Condition` — 사용된 조건입니다.
* `Description` — 인덱스 설명입니다(현재는 `Skip` 인덱스에만 사용됩니다).
* `Parts` — 인덱스 적용 전/후의 파트 수입니다.
* `Granules` — 인덱스 적용 전/후의 그래뉼 수입니다.
* `Ranges` — 인덱스 적용 후의 그래뉼 범위 수입니다.

예시:

```json theme={null}
"Node Type": "ReadFromMergeTree",
"Indexes": [
  {
    "Type": "Partition Min-Max",
    "Keys": ["y"],
    "Condition": "(y in [1, +inf))",
    "Parts": 4/5,
    "Granules": 11/12
  },
  {
    "Type": "Partition",
    "Keys": ["y", "bitAnd(z, 3)"],
    "Condition": "and((bitAnd(z, 3) not in [1, 1]), and((y in [1, +inf)), (bitAnd(z, 3) not in [1, 1])))",
    "Parts": 3/4,
    "Granules": 10/11
  },
  {
    "Type": "PrimaryKey",
    "Keys": ["x", "y"],
    "Condition": "and((x in [11, +inf)), (y in [1, +inf)))",
    "Parts": 2/3,
    "Granules": 6/10,
    "Search Algorithm": "generic exclusion search"
  },
  {
    "Type": "Skip",
    "Name": "t_minmax",
    "Description": "minmax GRANULARITY 2",
    "Parts": 1/2,
    "Granules": 2/6
  },
  {
    "Type": "Skip",
    "Name": "t_set",
    "Description": "set GRANULARITY 2",
    "": 1/1,
    "Granules": 1/2
  }
]
```

`projections` = 1로 설정하면 `Projections` 키가 추가됩니다. 이 키에는 분석된 프로젝션의 배열이 포함됩니다. 각 프로젝션은 다음 키를 포함하는 JSON으로 설명됩니다:

* `Name` — 프로젝션 이름입니다.
* `Condition` — 사용된 프로젝션 프라이머리 키 조건입니다.
* `Description` — 프로젝션이 어떻게 사용되는지에 대한 설명입니다(예: 파트 수준 필터링).
* `Selected Parts` — 프로젝션이 선택한 파트 수입니다.
* `Selected Marks` — 선택된 마크 수입니다.
* `Selected Ranges` — 선택된 범위 수입니다.
* `Selected Rows` — 선택된 행 수입니다.
* `Filtered Parts` — 파트 수준 필터링으로 건너뛴 파트 수입니다.

예시:

```json theme={null}
"Node Type": "ReadFromMergeTree",
"Projections": [
  {
    "Name": "region_proj",
    "Description": "Projection has been analyzed and is used for part-level filtering",
    "Condition": "(region in ['us_west', 'us_west'])",
    "Search Algorithm": "binary search",
    "Selected Parts": 3,
    "Selected Marks": 3,
    "Selected Ranges": 3,
    "Selected Rows": 3,
    "Filtered Parts": 2
  },
  {
    "Name": "user_id_proj",
    "Description": "Projection has been analyzed and is used for part-level filtering",
    "Condition": "(user_id in [107, 107])",
    "Search Algorithm": "binary search",
    "Selected Parts": 1,
    "Selected Marks": 1,
    "Selected Ranges": 1,
    "Selected Rows": 1,
    "Filtered Parts": 2
  }
]
```

`actions` = 1로 설정하면, 추가되는 키는 단계 유형에 따라 달라집니다.

예시:

```sql theme={null}
EXPLAIN json = 1, actions = 1, description = 0 SELECT 1 FORMAT TSVRaw;
```

```json theme={null}
[
  {
    "Plan": {
      "Node Type": "Expression",
      "Node Id": "Expression_5",
      "Expression": {
        "Inputs": [
          {
            "Name": "dummy",
            "Type": "UInt8"
          }
        ],
        "Actions": [
          {
            "Node Type": "INPUT",
            "Result Type": "UInt8",
            "Result Name": "dummy",
            "Arguments": [0],
            "Removed Arguments": [0],
            "Result": 0
          },
          {
            "Node Type": "COLUMN",
            "Result Type": "UInt8",
            "Result Name": "1",
            "Column": "Const(UInt8)",
            "Arguments": [],
            "Removed Arguments": [],
            "Result": 1
          }
        ],
        "Outputs": [
          {
            "Name": "1",
            "Type": "UInt8"
          }
        ],
        "Positions": [1]
      },
      "Plans": [
        {
          "Node Type": "ReadFromStorage",
          "Node Id": "ReadFromStorage_0"
        }
      ]
    }
  }
]
```

`compact = 0`과 `actions = 1`을 설정하면 `Expression` 단계와 함께 표현식에 대한 상세 정보를 확인할 수 있습니다:

```sql theme={null}
EXPLAIN actions = 1, compact = 0 SELECT sum(number) FROM numbers(10) GROUP BY number % 4;
```

```text theme={null}
Output: sum(number)

Expression ((Project names + Projection))
│  Actions: INPUT : 0 -> sum(__table1.number) UInt64 : 0
│           INPUT :: 1 -> modulo(__table1.number, 4_UInt8) UInt8 : 1
│           ALIAS sum(__table1.number) :: 0 -> sum(number) UInt64 : 2
│  Positions: 2
└──Aggregating
   │  Keys: number MOD 4
   │  Aggregates: sum(number)
   │  Skip merging: 0
   └──Expression ((Before GROUP BY + Change column names to column identifiers))
      │  Actions: INPUT : 0 -> number UInt64 : 0
      │           COLUMN Const(UInt8) -> 4_UInt8 UInt8 : 1
      │           ALIAS number :: 0 -> __table1.number UInt64 : 2
      │           FUNCTION modulo(__table1.number : 2, 4_UInt8 :: 1) -> modulo(__table1.number, 4_UInt8) UInt8 : 0
      │  Positions: 0 2
      └──ReadFromSystemNumbers
            Output: number
```

`distributed` = 1로 설정하면 로컬 쿼리 플랜뿐만 아니라 원격 노드에서 실행될 쿼리 플랜도 출력에 포함됩니다. 분산 쿼리를 분석하고 디버깅할 때 유용합니다.

<Note>
  `distributed`는 `pretty` 출력이 원격 세그먼트의 계획을 계획 트리에 통합하지 않기 때문에 레거시(`pretty`가 아닌) 형식으로만 표시됩니다. 이러한 이유로 `distributed`를 활성화하면 `explain_query_plan_default`와 관계없이 `pretty` 기본값(`actions`, `compact`, `pretty`)이 자동으로 비활성화됩니다. 그래도 `actions=1`은 수동으로 설정할 수 있습니다. 또한 `distributed` 옵션은 `json`과 함께 사용할 수 없습니다.
</Note>

분산 테이블(Distributed Table)을 사용한 예시:

```sql theme={null}
EXPLAIN distributed=1 SELECT * FROM remote('127.0.0.{1,2}', numbers(2)) WHERE number = 1;
```

```sql theme={null}
Union
  Expression ((Project names + (Projection + (Change column names to column identifiers + (Project names + Projection)))))
    Filter ((WHERE + Change column names to column identifiers))
      ReadFromSystemNumbers
  Expression ((Project names + (Projection + Change column names to column identifiers)))
    ReadFromRemote (Read from remote replica)
      Expression ((Project names + Projection))
        Filter ((WHERE + Change column names to column identifiers))
          ReadFromSystemNumbers
```

병렬 레플리카를 사용한 예시:

```sql theme={null}
SET enable_parallel_replicas = 2, max_parallel_replicas = 2, cluster_for_parallel_replicas = 'default';

EXPLAIN distributed=1 SELECT sum(number) FROM test_table GROUP BY number % 4;
```

```sql theme={null}
Expression ((Project names + Projection))
  MergingAggregated
    Union
      Aggregating
        Expression ((Before GROUP BY + Change column names to column identifiers))
          ReadFromMergeTree (default.test_table)
      ReadFromRemoteParallelReplicas
        BlocksMarshalling
          Aggregating
            Expression ((Before GROUP BY + Change column names to column identifiers))
              ReadFromMergeTree (default.test_table)
```

두 예시 모두에서 쿼리 플랜은 로컬 및 원격 단계를 포함한 전체 실행 흐름을 표시합니다.

`pretty` = 1로 설정하면 들여쓰기 대신 선 그리기 문자를 사용하여 플랜 트리가 표시되며, 주요 단계에 대한 추가 정보도 함께 표시됩니다:

* **쿼리 출력 컬럼**은 플랜 상단에 표시됩니다.
* 필터, 집계 키, 정렬 설명, 윈도우 함수의 **표현식**은 사람이 읽기 쉬운 SQL 유사 표기법으로 표시됩니다(예: `greater(plus(a, 1), 5)` 대신 `a + 1 > 5`). 명확성을 위해 내부 컬럼 식별자 프리픽스(예: `__table1.`)는 제거됩니다.
* **소스 단계**(`ReadFromMergeTree` 등)는 출력 컬럼을 표시합니다.
* **필터 단계**는 필터 조건을 SQL 표기법으로 표시합니다. 런타임 조인 필터가 있는 경우 별도로 표시됩니다.
* **집계 단계**는 키와 해당 인수가 포함된 집계 함수를 표시합니다(예: `sum(c)`, `count()`).
* 튜플 리터럴의 **IN Set**은 값을 표시하며(큰 Set의 경우 잘림), 서브쿼리 기반 Set에는 `subquery1`, `subquery2` 등의 레이블이 지정되고, `Set` 엔진 테이블의 Set은 테이블 이름을 표시합니다.
* **조인 단계**는 수학적 표기법을 사용한 조인 릴레이션, 예상 결과 행 수,
  그리고 어떤 출력 컬럼이 왼쪽과 오른쪽에서 오는지를 표시합니다. 다음 기호는
  서로 다른 JOIN 유형을 나타내는 데 사용됩니다:

| 기호                     | 조인 유형     |
| ---------------------- | --------- |
| `⋈`                    | 내부 조인     |
| `⟕`                    | 왼쪽 조인     |
| `⟖`                    | 오른쪽 조인    |
| `⟗`                    | 전체 조인     |
| `⋉`                    | 왼쪽 세미 조인  |
| `⋊`                    | 오른쪽 세미 조인 |
| `⋉` with strikethrough | 왼쪽 안티 조인  |
| `⋊` with strikethrough | 오른쪽 안티 조인 |
| `×`                    | 크로스 조인    |

예를 들어, `t1 ⟕ t2`는 테이블 `t1`과 `t2` 사이의 왼쪽 조인을 의미합니다.
테이블 이름 뒤 대괄호 안의 숫자(예: `t1[100]`)는 예상 행 수를 나타내며,
테이블 통계를 사용할 수 있을 때 표시됩니다.

`pretty` 옵션은 `compact = 1`과 함께 사용하면 효과적이며, 이 경우 `Expression` 단계와 자세한 작업 정보가 숨겨져 계획을 더 읽기 쉽게 만듭니다.

조인을 사용하는 더 자세한 예시:

```sql theme={null}
CREATE TABLE t1 (id UInt64, value String) ENGINE = MergeTree ORDER BY id;
CREATE TABLE t2 (id UInt64, value String) ENGINE = MergeTree ORDER BY id;
INSERT INTO t1 SELECT number, toString(number) FROM numbers(100);
INSERT INTO t2 SELECT number, toString(number) FROM numbers(100);

EXPLAIN actions = 1, compact = 1, pretty = 1
SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.id FORMAT Raw;
```

```text theme={null}
Output: id, value, id, value

Join (JOIN FillRightFirst)
│  t1[100] ⋈ t2[100]
│  Type: inner | Strictness: all | Algorithm: SpillingHashJoin(HashJoin)
│  Result rows: 100
│  Join conditions: id = id
│  Output:
│    Left:  id, value
│    Right: id, value
├──ReadFromMergeTree (default.t1)
│     Read type: Default
│     Parts: 1 | Granules: 1
│     Output: id, value
│     Runtime filters: RF1(id, id from default.t2)
└──BuildRuntimeFilter (Build runtime join filter on id)
   │  Filter id: RF1
   │  Source table: default.t2
   └──ReadFromMergeTree (default.t2)
         Read type: Default
         Parts: 1 | Granules: 1
         Output: id, value
```

<div id="explain-pipeline">
  ### EXPLAIN PIPELINE
</div>

설정:

* `header` — 각 출력 포트의 헤더를 출력합니다. 기본값: 0입니다.
* `graph` — [DOT](https://en.wikipedia.org/wiki/DOT_\(graph_description_language\)) 그래프 기술 언어로 작성된 그래프를 출력합니다. 기본값: 0입니다.
* `compact` — `graph` 설정이 활성화되면 그래프를 compact 모드로 출력합니다. 기본값: 1입니다.
* `compact_repeated_processor_chains` — 텍스트 출력에서 인접하게 반복되는 프로세서 체인을, 반복 횟수와 함께 체인 하나만 표시하는 방식으로 압축합니다. 예를 들어 조인에서 동일한 체인이 여러 번 나타나는 경우 병렬 파이프라인을 더 쉽게 읽을 수 있습니다. 그래프 출력에는 영향을 주지 않습니다. 기본값: 0입니다.

```text theme={null}
Resize 16 → 1
  FillingRightJoinSide          │
    SimpleSquashingTransform    │ × 16
      Resize 1 → 16
```

`compact=0`이고 `graph=1`이면 프로세서 이름에 고유한 프로세서 식별자를 나타내는 추가 접미사가 포함됩니다.

예시:

```sql theme={null}
EXPLAIN PIPELINE SELECT sum(number) FROM numbers_mt(100000) GROUP BY number % 4;
```

```sql theme={null}
(Union)
(Expression)
ExpressionTransform
  (Expression)
  ExpressionTransform
    (Aggregating)
    Resize 2 → 1
      AggregatingTransform × 2
        (Expression)
        ExpressionTransform × 2
          (SettingQuotaAndLimits)
            (ReadFromStorage)
            NumbersRange × 2 0 → 1
```

<div id="explain-estimate">
  ### EXPLAIN ESTIMATE
</div>

쿼리를 처리하는 동안 테이블에서 읽게 될 것으로 예상되는 행 수, 마크 수, 파트 수를 표시합니다. [MergeTree](/ko/reference/engines/table-engines/mergetree-family/mergetree) 계열 테이블에서 동작합니다.

**예시**

테이블 생성:

```sql title="Query" theme={null}
CREATE TABLE ttt (i Int64) ENGINE = MergeTree() ORDER BY i SETTINGS index_granularity = 16, write_final_mark = 0;
INSERT INTO ttt SELECT number FROM numbers(128);
OPTIMIZE TABLE ttt;
```

```sql title="Query" theme={null}
EXPLAIN ESTIMATE SELECT * FROM ttt;
```

```text title="Response" theme={null}
┌─database─┬─table─┬─parts─┬─rows─┬─marks─┐
│ default  │ ttt   │     1 │  128 │     8 │
└──────────┴───────┴───────┴──────┴───────┘
```

<div id="explain-whatif">
  ### EXPLAIN WHATIF
</div>

가상의 스킵 인덱스를 디스크에 구체화하지 *않고도* `SELECT` 쿼리에서 얼마나 효과가 있을지 추정합니다. [`CREATE HYPOTHETICAL INDEX`](/ko/reference/statements/hypothetical-index#create-hypothetical-index)로 하나 이상의 후보를 정의한 다음 `EXPLAIN WHATIF SELECT ...`를 실행하면 각 후보별로 적용 가능 여부, 예상 읽기 마크 수, 예상 바이트 수, 스킵 비율을 확인할 수 있습니다.

**구문**

```sql theme={null}
EXPLAIN WHATIF [empirical = 0] SELECT ...
```

**설정**

* `empirical` — `1`(기본값)은 메모리에서 baseline으로 걸러진 그래뉼에 인덱스를 적용해 스킵 비율(상한값)을 측정합니다. `0`은 해당 경로를 건너뜁니다. 어느 쪽이든 `empirical`이 결과를 생성하지 못하면(비활성화되었거나 인덱스를 메모리에서 평가할 수 없는 경우) 추정기는 컬럼 [통계](/ko/reference/engines/table-engines/mergetree-family/mergetree#column-statistics)로 대체하고, 이마저도 사용할 수 없으면 마지막으로 적용 가능성만 요약한 결과로 대체합니다.

**출력**

```text theme={null}
Baseline (after PK + partition + existing indexes):
  table:       db.t
  parts:       1
  marks:       100
  est_bytes:   1.50 MiB             (only when the query reads rows)

With idx_b (minmax, hypothetical):
  status:       applicable
  marks:        1
  est_bytes:    15.00 KiB           (only when baseline bytes are known)
  skip_ratio:   99.0%

Estimation:
  source:           empirical | statistical | applicability_only
  empirical_status: ok | unsupported | disabled
  sampled_parts:    50 / 100        (only when source = empirical)
  sampled_marks:    50 / 100        (only when source = empirical)
  elapsed_us:       631             (only when source = empirical)
```

* `source` — 추정치가 산출된 방식입니다.
  * `empirical`: 기준선 프루닝이 적용된 그래뉼을 기준으로 메모리에서 인덱스를 구축한 뒤, 해당 인덱스가 건너뛸 그래뉼 수를 계산했습니다. 이는 상한값입니다. 자세한 내용은 [`CREATE HYPOTHETICAL INDEX`](/ko/reference/statements/hypothetical-index#limitations)의 제한 사항을 참조하십시오.
  * `statistical`: 컬럼 통계를 바탕으로 도출됩니다. empirical이 비활성화된 경우(`empirical = 0`) 또는 empirical이 결과를 산출하지 못했고 관련 컬럼에 컬럼 통계가 정의되어 있을 때 사용됩니다.
  * `applicability_only`: 인덱스는 프레디케이트에 적용할 수 있지만 empirical 추정과 statistical 추정 모두 결과를 산출하지 못한 경우입니다(예: `empirical = 0`이고 컬럼 통계가 정의되지 않은 경우). 보수적인 상한값으로 `skip_ratio: 0.0%`를 보고합니다.
* `sampled_parts` / `sampled_marks` — `<baseline-pruned> / <total in the table>`. 테이블에서 PK, 파티션, 기존 인덱스 프루닝을 거친 뒤 남은 비율, 즉 hypothetical index의 입력이 되는 범위를 보여줍니다.
* `est_bytes` — 읽기 바이트 수의 추정치입니다. 테이블의 평균 행 크기를 바탕으로 계산하므로 근사치이며, 스토리지와 Compression에 따라 달라집니다. 기준선 행은 쿼리가 행을 읽을 때만 표시되며, 각 후보 행은 기준선 바이트 추정치를 알 수 있을 때만 표시됩니다.

이 설정은 `WHATIF`와 `SELECT` 사이에 인라인으로 작성하며, `SETTINGS` keyword는 없습니다(다른 `EXPLAIN` 변형이 옵션을 받는 방식과 동일합니다).

테이블에 hypothetical index가 정의되어 있지 않으면 `EXPLAIN WHATIF`는 `status: not_applicable`와 함께 생성하라는 힌트를 보고합니다.

**Empirical 예시**

```sql theme={null}
CREATE TABLE t (a UInt64, b UInt64) ENGINE = MergeTree ORDER BY a
SETTINGS index_granularity = 100;

INSERT INTO t SELECT number, number FROM numbers(10000);

CREATE HYPOTHETICAL INDEX idx_b ON t (b) TYPE minmax GRANULARITY 1;

EXPLAIN WHATIF SELECT * FROM t WHERE b = 42;
```

```text theme={null}
Baseline (after PK + partition + existing indexes):
  table:       default.t
  parts:       1
  marks:       100
  est_bytes:   85.52 KiB

With idx_b (minmax, hypothetical):
  status:       applicable
  marks:        1
  est_bytes:    875.00 B
  skip_ratio:   99.0%

Estimation:
  source:           empirical
  empirical_status: ok
  sampled_parts:    1 / 1
  sampled_marks:    100 / 100
```

가상의 `minmax`는 100개의 마크를 1개로 걸러낼 수 있습니다 — `skip_ratio: 99.0%`. (`est_bytes`는 평균 행 크기를 기준으로 한 추정치이므로 정확한 값은 달라질 수 있습니다.)

**통계 예시**

컬럼 [통계](/ko/reference/engines/table-engines/mergetree-family/mergetree#column-statistics)는 기본적으로 비활성화되어 있습니다. `statistical` 경로를 테스트하려면 먼저 관련 컬럼에 이를 정의하고 구체화 mutation이 완료될 때까지 기다리십시오:

```sql theme={null}
ALTER TABLE t ADD STATISTICS b TYPE TDigest;
ALTER TABLE t MATERIALIZE STATISTICS b SETTINGS mutations_sync = 1;
```

그런 다음 추정기가 컬럼 통계를 대신 사용하도록 경험적 경로를 비활성화합니다:

```sql theme={null}
EXPLAIN WHATIF empirical = 0 SELECT * FROM t WHERE b < 10;
```

```text theme={null}
With idx_b (minmax, hypothetical):
  status:       applicable
  marks:        1
  est_bytes:    1.66 KiB
  skip_ratio:   99.9%

Estimation:
  source:           statistical
  empirical_status: disabled
```

이 값은 `b < 10`의 컬럼 통계 선택도(selectivity)에서 나오며(10000개 행 중 약 10개 행), `skip_ratio`의 상한값으로 보고됩니다. `sampled_parts` / `sampled_marks`는 없으며, 데이터를 읽지 않았습니다.

두 경로 모두 사용할 수 없으면(예: `empirical = 0`이고 컬럼 통계가 정의되지 않은 경우), 추정기는 `source: applicability_only`와 보수적인 `skip_ratio: 0.0%`를 보고합니다.

<div id="explain-table-override">
  ### EXPLAIN TABLE OVERRIDE
</div>

테이블 함수를 통해 접근한 테이블 스키마(schema)에 테이블 재정의를 적용한 결과를 보여줍니다.
또한 일부 유효성 검사도 수행하며, 이 재정의로 인해 어떤 형태로든 실패가 발생할 경우 예외를 발생시킵니다.

**예시**

원격 MySQL 테이블이 다음과 같다고 가정합니다:

```sql title="Query" theme={null}
CREATE TABLE db.tbl (
    id INT PRIMARY KEY,
    created DATETIME DEFAULT now()
)
```

```sql title="Query" theme={null}
EXPLAIN TABLE OVERRIDE mysql('127.0.0.1:3306', 'db', 'tbl', 'root', 'clickhouse')
PARTITION BY toYYYYMM(assumeNotNull(created))
```

```text title="Response" theme={null}
┌─explain─────────────────────────────────────────────────┐
│ PARTITION BY uses columns: `created` Nullable(DateTime) │
└─────────────────────────────────────────────────────────┘
```

<Note>
  검증이 완전하지 않으므로, 쿼리가 성공하더라도 재정의로 인해 문제가 발생하지 않는다고 보장할 수는 없습니다.
</Note>
