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

> EXPLAIN に関するドキュメント

# EXPLAIN

ステートメントの実行計画を表示します。

<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 | ANALYZE | 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
```

## EXPLAIN の種類

* `AST` — 抽象構文木。
* `SYNTAX` — AST レベルでの最適化後のクエリテキスト。
* `QUERY TREE` — クエリツリー レベルでの最適化後のクエリツリー。
* `PLAN` — クエリ実行プラン。
* `PIPELINE` — クエリ実行パイプライン。
* `ANALYZE` — クエリを実行し、計測されたランタイムメトリクスを実行計画に注釈として付加します。
* `ESTIMATE` — クエリの処理中にテーブルから読み取ると見積もられる行数、マーク数、パーツ数。
* `TABLE OVERRIDE` — テーブル関数のスキーマに対するテーブルオーバーライドの検証済み結果。

### EXPLAIN AST

クエリ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
```

### EXPLAIN SYNTAX

構文解析後のクエリの抽象構文木 (AST) を表示します。

これは、クエリをパースしてクエリASTとクエリツリーを構築し、必要に応じてクエリアナライザと最適化パスを実行したうえで、クエリツリーをクエリASTに再変換することで行われます。

設定:

* `oneline` – クエリを1行で表示します。デフォルト: `0`。
* `run_query_tree_passes` – クエリツリーをダンプする前にクエリツリーパスを実行します。デフォルト: `0`。
* `query_tree_passes` – `run_query_tree_passes` が設定されている場合、実行するパス数を指定します。`query_tree_passes` を指定しない場合は、すべてのパスが実行されます。
* `single_record` – 整形されたクエリを、行ごとに1レコードではなく単一の複数行レコードとして返します。デフォルト: `1` (`explain_syntax_single_record` 設定で制御) 。従来の1行1レコード出力に戻すには、`0` を設定するか、`explain_syntax_single_record = 0` を設定します (グローバルまたはクエリごとの `SETTINGS` 内) 。または、`compatibility` を `26.8` より前の任意のバージョンに設定します。

例:

```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
```

### EXPLAIN QUERY TREE

設定:

* `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
```

### EXPLAIN PLAN

クエリプランのステップを出力します。

設定:

* `optimize` — プランを表示する前に、クエリプランの最適化を適用するかどうかを制御します。デフォルト: 1。
* `header` — ステップの出力ヘッダーを表示します。デフォルト: 0。
* `description` — ステップの説明を表示します。デフォルト: 1。
* `indexes` — 使用された索引、フィルタリングされたパーツ数、および適用された各索引についてフィルタリングされたグラニュール数を表示します。デフォルト: 0。[MergeTree](/ja/reference/engines/table-engines/mergetree-family/mergetree) テーブルでサポートされています。ClickHouse >= v25.9 以降、このステートメントが適切な出力を示すのは、`SETTINGS use_query_condition_cache = 0, use_skip_indexes_on_data_read = 0` とともに使用した場合のみです。
* `projections` — 解析されたすべてのプロジェクションと、プロジェクションの主キー条件に基づくパーツレベルのフィルタリングへの影響を表示します。各プロジェクションについて、このセクションには、プロジェクションの主キーを使って評価されたパーツ数、行数、マーク数、範囲数などの統計が含まれます。また、このフィルタリングにより、プロジェクション自体を読み取ることなくスキップされた data parts の数も表示します。プロジェクションが実際に読み取りに使用されたのか、それともフィルタリングのために解析されただけなのかは、`description` フィールドで判別できます。デフォルト: 0。[MergeTree](/ja/reference/engines/table-engines/mergetree-family/mergetree) テーブルでサポートされています。
* `actions` — ステップの actions に関する詳細情報を表示します。デフォルト: 1。
* `sorting` — ソート済みの出力を生成する各プランステップについて、ソートの説明を表示します。デフォルト: 0。
* `keep_logical_steps` — joins について、物理的な join 実装に変換せずに、論理プランステップを保持します。デフォルト: 0。
* `json` — クエリプランのステップを [JSON](/ja/reference/formats/JSON/JSON) フォーマットの 1 行として出力します。デフォルト: 0。不要なエスケープを避けるため、[TabSeparatedRaw (TSVRaw)](/ja/reference/formats/TabSeparated/TabSeparatedRaw) フォーマットの使用を推奨します。
* `input_headers` — ステップの入力ヘッダーを表示します。デフォルト: 0。主に、入力ヘッダーと出力ヘッダーの不一致に関する問題をデバッグする開発者にのみ有用です。
* `column_structure` — ヘッダー内のカラム構造を、名前と型に加えて表示します。デフォルト: 0。主に、入力ヘッダーと出力ヘッダーの不一致に関する問題をデバッグする開発者にのみ有用です。
* `distributed` — 分散テーブルまたは並列レプリカについて、リモートノードで実行されるクエリプランを表示します。`json` と同時にはサポートされません。デフォルト: 0。
* `compact` — 有効にすると、プランから expression ステップと詳細な action 情報 (入力、関数、別名、出力位置) を非表示にします。`actions = 1` の場合にのみ効果があります。デフォルト: 1。
* `pretty` — インデントの代わりに罫線文字 (├──、└──、│) を使ってプランツリーを表示し、階層構造を視覚化します。さらに、join ステップのプロパティもインラインで整形して表示します。デフォルト: 1。

<Note>
  デフォルトでは、`explain_query_plan_default = 'pretty'` であるため、`actions`、`compact`、`pretty` は `1` に初期化され、プランはコンパクトで見やすく、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>
  Step およびクエリのコスト見積もりはサポートされていません。
</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` キーが追加されます。これには、使用された索引の配列が含まれます。各索引は JSON で記述され、`Type` キー (文字列 `Partition Min-Max`、`Partition`、`Statistics`、`PrimaryKey` または `Skip`) と、必要に応じて以下のキーを持ちます。

`Statistics` 索引は、パーツごとのカラム STATISTICS (最小値/最大値、および `Nullable` カラムにおける `NULL` 値の数) を使用して、クエリのフィルタに一致し得ないパーツをスキップします。

* `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` 出力ではリモート分片のプランがプランツリーに統合されないため、`legacy` (非`pretty`) 形式でのみ表示されます。このため、`distributed` を有効にすると、`explain_query_plan_default` の値に関係なく、`pretty` のデフォルト設定 (`actions`、`compact`、`pretty`) は自動的に無効になります。なお、`actions=1` は手動で設定できます。また、`distributed` オプションは `json` と併用できません。
</Note>

分散テーブルを使用した例:

```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 表記のフィルタ条件が表示されます。ランタイム join フィルタが存在する場合は、それらは別個に表示されます。
* **集約ステップ** には、キーと、引数付きの集約関数 (例: `sum(c)`、`count()`) が表示されます。
* タプルリテラルの **IN set** にはその値が表示され (大きな set の場合は切り詰められます) 、サブクエリベースの set には `subquery1`、`subquery2` などのラベルが付き、`Set` engine tables 由来の set にはテーブル名が表示されます。
* **join ステップ** には、数学的記法を用いた join 関係、そのステップについて
  join 順序最適化器が生成した推定値 (コスト、選択性、出力行数、各側の行数)、
  および各側の入力カラムが表示されます。異なる join タイプを表すために、次の記号が使用されます：

| 記号 | Join タイプ |
| - | - |
| `⋈` | Inner Join |
| `⟕` | Left Join |
| `⟖` | Right Join |
| `⟗` | Full Join |
| `⋉` | Left Semi Join |
| `⋊` | Right Semi Join |
| 取り消し線付きの `⋉` | Left Anti Join |
| 取り消し線付きの `⋊` | Right Anti Join |
| `×` | Cross Join |

たとえば、`t1 ⟕ t2` はテーブル `t1` と `t2` の間の left join を意味します。
テーブル名の後の角括弧内の数値 (例: `t1[100]`) は、テーブル統計情報が利用可能な場合の推定行数を
示します。

join 関係の下には、各 join ステップについて join 順序最適化器が生成した推定値が表示されます：

```txt theme={null}
Cost: estimated <cost>
Selectivity: estimated (NDV) <selectivity>
Output rows: estimated <rows>
Left: rows estimated <left_rows>
Right: rows estimated <right_rows>
```

* `Cost` — このステップ配下の join サブツリー全体のコストであり、オプティマイザが候補となる JOIN 順序を
  比較する際に最小化する値です。join のコストは、結合した行ペアの推定数
  `<selectivity> * <left_rows> * <right_rows>` に入力のコストを加えたものです。
* `Selectivity` — 2 つの側のデカルト積のうち、join 条件を満たして残ると推定される割合です。
  これは結合キーの distinct values (NDV) 数から導出されます。
  キーの等価条件ではペアの約 `1 / max(NDV_left, NDV_right)` が残り、join 条件全体では
  最小の割合が使用されます。
* `Output rows` — join が生成する推定行数です。
  inner join では `<selectivity> * <left_rows> * <right_rows>`、`LEFT` では `<left_rows>`、
  `RIGHT` では `<right_rows>`、`FULL` では `<left_rows> + <right_rows>` を下限とします。これは
  outer join が保持側のすべての行を残すためです。`SEMI` または `ANTI` join が並べ替えに
  関与する場合は、保持側の割合として推定されます。`SEMI` では
  `<preserved_rows> * min(1, <selectivity> * <other_rows>)`、`ANTI` では残りの行数です。
  それ以外の場合は、その join 種別の式を使用します。
* `Left` / `Right` — 各側から join に入力される推定行数です。

オプティマイザが推定できなかった値は `no stats` として報告されます。これは、
join 順序の最適化が実行されなかった場合、たとえば
[`query_plan_optimize_join_order_limit`](/ja/reference/settings/session-settings/query-plan#query_plan_optimize_join_order_limit)
が `0` の場合、または推定の根拠がない場合に発生します。

`Input (left):` および `Input (right):` の行には、各側が join に入力するカラムが一覧表示されます。

`pretty` オプションは `compact = 1` と組み合わせて使用すると効果的です。これにより、`Expression` ステップと詳細な action 情報が非表示になり、プランが読みやすくなります。

join を含む詳細な例です。
[`join_runtime_filter_min_probe_rows`](/ja/reference/settings/session-settings/join-runtime#join_runtime_filter_min_probe_rows)
設定は、この程度に小さいテーブルでもランタイム join フィルタが構築されるようにするためだけに引き下げています。

```sql theme={null}
SET join_runtime_filter_min_probe_rows = 10;

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)
│  Cost: estimated 100.00
│  Selectivity: estimated (NDV) 0.01
│  Output rows: estimated 100.00
│  Left: rows estimated 100.00
│  Right: rows estimated 100.00
│  Join conditions: id = id
│  Input (left): id, value
│  Input (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
```

### EXPLAIN PIPELINE

設定:

* `header` — 各出力ポートのヘッダーを表示します。デフォルト: 0。
* `graph` — [DOT](https://en.wikipedia.org/wiki/DOT_\(graph_description_language\)) グラフ記述言語で記述されたグラフを表示します。デフォルト: 0。
* `compact` — `graph` 設定が有効な場合、compact モードでグラフを表示します。デフォルト: 1。
* `compact_repeated_processor_chains` — テキスト出力で、隣接して繰り返されるプロセッサチェーンを、チェーンを 1 つだけ表示して繰り返し回数を付けることでコンパクトにします。これにより、たとえば JOIN で同じチェーンが何度も現れる場合に、並列パイプラインが読みやすくなります。グラフ出力には影響しません。デフォルト: 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
```

### EXPLAIN ANALYZE

`EXPLAIN ANALYZE` は実際にクエリを実行し、結果の行を破棄したうえで、各ステップに実行時に実際に何が起きたかを注記として付けた、`EXPLAIN PLAN` と同じプランツリーを出力します。

設定:

`EXPLAIN ANALYZE` では、`EXPLAIN PLAN` と同じ表示オプションを使用できます ([EXPLAIN PLAN](#explain-plan) セクションを参照) 。

* `header` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。
* `description` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。
* `projections` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。
* `sorting` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。
* `input_headers` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。
* `column_structure` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。
* `actions` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。デフォルト: 1。
* `indexes` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。デフォルト: 1。
* `compact` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。デフォルト: 1。
* `pretty` — [EXPLAIN PLAN](#explain-plan) セクションを参照してください。デフォルト: 1。
* `processors` — `EXPLAIN ANALYZE` では、各ステージについて、プロセッサごとの経過時間分布 (`min`、`median`、`max`、`sum`) を示す追加の行を出力します。並列プロセッサ間の負荷の偏りを見つけるのに役立ちます。デフォルト: 0。
* `matches` — `EXPLAIN ANALYZE` では、結合による出力だけではこれらの数値を導出できない場合に、`matched`、`match rate`、`fanout` メトリクスに必要な追加の記録処理を join ステップで行います。導出できる場合は、このオプションなしで報告されます。[join ステップ](#explain-analyze-join-steps)を参照してください。デフォルト: 0。

<Note>
  `EXPLAIN ANALYZE` はラップされたクエリを実際に実行するため、いくつかの点で、そのクエリと同じように動作し、実行しない `EXPLAIN` 形式とは異なります。

  * **クォータと制限。** クエリを直接実行した場合と同じ [quotas](/ja/concepts/features/configuration/server-config/quotas)
    に対してカウントされ、同じ [limits](/ja/concepts/features/configuration/settings/query-complexity)
    (たとえば `query_selects`、`read_rows`) の対象にもなります。プランニング中はクォータの対象外となるテーブル
    (`system.one` など) についてはカウントされません。
  * **失敗したトランザクション。** すでに失敗している [transaction](/ja/concepts/features/operations/insert/transactions)
    (`ROLLED_BACK`) 内では、通常の `SELECT` と同様に `INVALID_TRANSACTION` で拒否されます。
    先に `ROLLBACK` を実行してください。
  * **ストリーミング読み取り。** ストリーミング (`FROM ... STREAM`) 読み取りに対しては、
    そのような読み取りは完了しないため、`NOT_IMPLEMENTED` で拒否されます。
  * **分散クエリ。** [distributed](/ja/reference/engines/table-engines/special/distributed) モードで実行される
    クエリではサポートされていません。
</Note>

例:

```sql theme={null}
EXPLAIN ANALYZE SELECT number % 10 AS k, count() FROM numbers_mt(1000000) GROUP BY k;
```

```text theme={null}
Query summary:
  Time:        10.72 ms (planning 6.45 ms · execution 4.26 ms)
  Read:        1.00 million rows, 8.00 MB (234.49 million rows/s., 1.88 GB/s.)
  Peak memory: 28.98 KiB

Output: number MOD 10, count()

Expression ((Project names + Projection))
│  I/O: rows 10 → 10 · 90 B → 90 B
│    time 21.82 us (0.5%) · parallelism 0.98/1
└──Aggregating
   │  Keys: number MOD 10
   │  Aggregates: count()
   │  Skip merging: 0
   │  I/O: rows 1.00 million → 10 (0.00%) · 1.00 MB → 90 B
   │    Stage (partial aggregation): time 868.45 us (20.4%) · parallelism 3.80/15
   │    Stage (final aggregation): time 445.27 us (10.4%) · parallelism 1.11/16
   └──Expression ((Before GROUP BY + Change column names to column identifiers))
      │  I/O: rows 1.00 million → 1.00 million · 8.00 MB → 1.00 MB
      │    time 677.07 us (15.9%) · parallelism 4.31/15
      └──ReadFromSystemNumbers
            Output: number
            I/O: rows 0 → 1.00 million · 0 B → 8.00 MB
              time 993.94 us (23.3%) · parallelism 7.52/15
```

出力を見てみましょう。まずはヘッダーを見てみましょう。

```txt theme={null}
   Query summary:
     Time:        <total> (planning <planning> · execution <execution>)
     Read:        <rows> rows, <bytes> (<rows/s>, <bytes/s>)
     Peak memory: <peak>
```

* `Time` — 合計時間です。planning (つまり、plan の作成 + plan の最適化 + パイプラインの構築) フェーズと execution (パイプラインの実行) フェーズに分けて表示されます。
* `Read` — テーブルから読み取られた行数と非圧縮バイト数、および throughput です。これは通常のクエリのフッターで "Processed" として報告される数値と同じです。
* `Peak memory` — クエリが使用したピークメモリです。

次に、クエリプランに表示される新しい行を見ていきましょう。

```txt theme={null}
I/O: rows <in> → <out> (<selectivity>%) · <bytes_in> → <bytes_out>
  [Stage (<stage>): ]time <t> (<share>%) · parallelism <avg>/<max>
```

行数とバイト数は、ステップ全体について一度だけ報告されます (`I/O` 行) 。時間と並列度は、ステップ内の各ステージごとに、その下のインデントされた行に報告されます。

* `rows <in> → <out>` — ステップに入力された行数と、ステップから出力された行数です。`(<selectivity>`%) は、そのステップがデータをどの程度絞り込んだか (`out/in`) 、または増やしたかを示します。入力行数と出力行数が同じ場合、および入力行数が `0` の場合は表示されません。
* `<bytes_in> → <bytes_out>` — ステップ内を流れる非圧縮のインメモリバイト数です (両方ともゼロの場合は省略されます) 。
* `time <t> (<share>%)` — そのステージがアクティブだった実時間と、クエリ実行時間に占める割合です (つまり、build time は含みません) 。ステージやステップは同時実行されるため、この割合の合計が 100% を超えることがあります。
* `parallelism <avg>/<max>` — このステージ内で同時に動作していた CPU スレッド数の平均値と、そのステージで使用可能な最大値です。値が最大値に近いほど、そのステージは十分に並列化されていたことを示します。1 に近い場合は、ほぼ直列に実行されていたことを示します。
* `Stage (<stage>)` — ステージ名です。ステージが 1 つだけのステップでは、`Stage (...)` ラベルは付かず、時間の行が直接出力されます。複数のステージを持つステップでは、各ステージごとにラベル付きの行が 1 行ずつ出力されます。たとえば `Aggregating` では `Stage (partial aggregation)` と `Stage (final aggregation)` が表示され、ハッシュ結合では `Stage (build)` と `Stage (probe)` が表示されます。

<Note>
  ClickHouse は、プランステップ内のタスク実行だけでなく、プランステップ自体の実行も並列化します。`parallelism` メトリクスが反映するのは、このステップの処理だけです。他のステップも同時に実行されることがあるため、この数値から、このステップの並列度をクエリ全体と比較することはできません。
</Note>

<Note>
  `parallelism` の最大値は、次の 2 つのうち小さい方として計算されます。

  1. プランステップ内のタスク総数
  2. `max_threads` で設定されたクエリ処理スレッドの最大数
</Note>

#### Join ステップ

join ステップでは、`EXPLAIN ANALYZE` は結合順序オプティマイザの推定値と実際に起きたことを比較する行 ([推定値と実測値の join メトリクス](#explain-analyze-join-estimation) を参照) と、各側の*参加*行 (`Left` と `Right`) を出力し、その後に join の実装固有の行を出力します。[`join_algorithm`](/ja/reference/settings/session-settings/join#join_algorithm) のすべての値 (`hash`、`parallel_hash`、`grace_hash`、`partial_merge`、`full_sorting_merge`、`parallel_full_sorting_merge`、`direct`) に対応しています。また、この設定では選択できない 2 つの実装、すなわち `CROSS` または `COMMA` join、キーの等価条件を含まない `ON` 句、および [`Join`](/ja/reference/engines/table-engines/special/join) テーブルエンジンも対象です。ほとんどの実装では両側が報告されますが、マテリアライズする側のみを報告するものもあります (たとえば、`direct` は `Left:` のみを出力します) 。

各側の行は同じ形式です。

```txt theme={null}
Left:  rows estimated <estimated_left_rows>  · rows <left_rows>  · matched <matched_left_rows>  · match rate <match_rate>% · fanout <fanout>
Right: rows estimated <estimated_right_rows> · rows <right_rows> · matched <matched_right_rows> · match rate <match_rate>% · fanout <fanout>
```

各側について、`EXPLAIN ANALYZE` は以下を報告します。

* `rows estimated <estimated_rows>` — 結合順序オプティマイザによるその側の行数の推定値。隣に表示される実際の `rows` との比較用に出力されます。オプティマイザが推定値を生成しなかった場合は `no stats` となります (「[推定 join メトリクスと実測値](#explain-analyze-join-estimation)」を参照) 。
* `rows <rows>` — 結合処理を通過したその側の行の総数。
* `matched <matched_rows>` — もう一方の側で少なくとも1つの結合相手が見つかった、その側の行数。これはキーではなく*行*を数えます。キーが右側に3回出現して一致した場合、右側の3行すべてが一致した行としてカウントされます。
* `match rate <match_rate>%` — 一致したその側の行の割合。`100 * <matched_rows> / <rows>` として算出されます。
* `fanout <fanout>` — その側で一致した行1行あたりが平均して生成する出力行数。

正確に導出できない数値は、`0` ではなく `not collected` として報告されます。`match rate` と `fanout` は `matched` から導出されるため、`matched` がない側では、3つすべてが `not collected` として報告されます。

#### 推定値と実測値の join メトリクス

join ステップは、結合順序オプティマイザによる推定値 (`EXPLAIN PLAN` が表示するものと同じ値。[EXPLAIN PLAN](#explain-plan) セクションを参照) を保持しており、`EXPLAIN ANALYZE` はそれぞれを実測値と並べて出力します。

```txt theme={null}
Cost: estimated <cost> · actual <cost>
Selectivity: estimated (NDV) <selectivity> · actual (cartesian) <selectivity>
Output rows: estimated <rows> · actual <rows> · q-error <ratio>
```

* `Cost` — どちらの値も、マッチした出力行数を表します。推定値は、オプティマイザによる join 部分木のコストで、`<selectivity> * <left_rows> * <right_rows>` にその入力のコストを加えたものです。実測値も同様に算出され、この join のマッチした出力行数に、同じ並べ替えクラスターに属する下位のすべての join の実コストを加えたものです。
* `Selectivity` — 推定値は結合キーの個別値数から導出されます。実測値は、出力に含まれたデカルト積の割合を計測したもので、`<matched output rows> / (<left rows> * <right rows>)` です。これはオプティマイザが推定しようとする値です。
* `Output rows` — join が生成した推定行数と実測行数です。両方がゼロ以外の場合、`q-error` はカーディナリティ推定の品質を測る標準的な指標である `max(estimated / actual, actual / estimated)` を報告します。`1.00` は完全な推定を意味し、大きな値は、オプティマイザが大きく誤ったカーディナリティに基づいて JOIN 順序を選択したことを意味します。

一度も推定が行われなかった場合は、`no stats` として報告されます。たとえば、[`query_plan_optimize_join_order_limit`](/ja/reference/settings/session-settings/query-plan#query_plan_optimize_join_order_limit) が `0` であるため、JOIN 順序の最適化が実行されなかった場合です。これは、実行時に計測できなかった実測値を示す `not collected` とは異なります。右側が事前に入力済みの join ([`Join`](/ja/reference/engines/table-engines/special/join) テーブルエンジン、`direct` join) は 結合順序オプティマイザを経由せず、参加行のみを出力します。

join ステップでは、各辺が join に渡すカラムを示す `Input (left):` 行と `Input (right):` 行も出力されます。

[EXPLAIN PLAN](#explain-plan) の join 例で使用するテーブルでは、`EXPLAIN ANALYZE SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.id` の join ステップは次のようになります:

```text theme={null}
Join (JOIN FillRightFirst)
│  t1[100] ⋈ t2[100]
│  Type: inner | Strictness: all | Algorithm: SpillingHashJoin(HashJoin)
│  Join conditions: id = id
│  Cost: estimated 100.00 · actual 100.00
│  Selectivity: estimated (NDV) 0.01 · actual (cartesian) 0.01
│  Output rows: estimated 100.00 · actual 100.00 · q-error 1.00
│  Left: rows estimated 100.00 · rows 100.00 · matched 100.00 · match rate 100.00% · fanout 1.00
│  Right: rows estimated 100.00 · rows 100.00 · matched not collected · match rate not collected · fanout not collected
│  Hash table: unique keys 100.00 · memory 6.27 KB
│  Input (left): id, value
│  Input (right): id, value
│  I/O: rows 200 → 100 (50.00%) · 3.58 KB → 3.58 KB
│    Stage (build): time 127.45 us (1.2%) · parallelism 0.99/1
│    Stage (probe): time 124.99 us (1.2%) · parallelism 0.99/1
```

#### ファンアウト

`fanout` は行数の増加倍率を示します。

```txt theme={null}
matched output rows = <output_rows> - <NULL-padded rows of both sides>
fanout              = <matched output rows> / <matched_rows of that side>
```

外部結合では、相手が見つからなかった保持側の各行に対して、`NULL` で埋められた出力行が 1 行生成されます。これらの行は比率を薄めないよう差し引かれます。このような行が存在するのは保持側のみです。`RIGHT` と `FULL` では右側、`LEFT` と `FULL` では左側になります。

* `fanout = 0` — 一致した行からは出力行がまったく生成されません。これは `ANTI` join の動作であり、相手が見つからなかった行のみを出力します。
* `fanout = 1` — 明確な 1:1 join です。一致した各行から、ちょうど 1 行の出力行が生成されます。
* `fanout > 1` — 1:N join です。もう一方の側の重複キーによって行数が増加しました。両側で同時に大きな値になる場合は、意図しない Cartesian 的な行数の急増を示します。

#### 数値に `matches = 1` が必要な場合

これらの数値の大半は、結合がいずれにせよ構築するデータから得られるため、通常の `EXPLAIN ANALYZE` で報告されます。残りは、結合が通常は行わない追跡処理を必要とするため、`EXPLAIN ANALYZE matches = 1` でのみ報告されます。どれが該当するかはアルゴリズムによって異なり、hash ファミリーでは次の 2 つです。

* 一致したすべての右行に印を付ける必要がある、`ALL INNER` と `ALL LEFT` の**右**側。
* `ALL LEFT` と `ALL FULL` の**左**側。ただし、クエリが右テーブルから何も選択せず、
  `ON` セクションが単純なキーの等価条件である場合に限ります。それ以外の場合、プローブはすでに
  一致した左行を記録します。これは右カラムをマテリアライズするため、または残余条件を評価するためです。
  そのため、このオプションがなくてもカウントは正確です。

`partial_merge` では、同じ理由で 4 種類の `ALL` の**右**側に必要です。
`full_sorting_merge` と `parallel_full_sorting_merge` では、`ANY` 種類の**両方**の側に必要です。
`ALL` 種類では何も必要ありません。

<Note>
  追加の追跡処理にはコストがかかり、計測精度に影響します。そのため、このオプションはデフォルトで無効になっています。処理はプローブループ内で実行され、出力行数に応じて増加します。左行と右行が相手側で正確に何件一致したかを知る必要がある場合は、`matches = 1` を使用してください。
</Note>

`matches = 1` を指定しても、すべての組み合わせで収集できるようになるわけではありません。結合がどちらの側を報告できるかは、その結合がいずれにせよ実行する必要がある処理によるため、種類や厳密性だけでなくアルゴリズムにも依存します。

**Hash ファミリー。** `hash`、`parallel_hash`、`grace_hash` は常に同じ結果になります。

| Join | `matched` 左 | `matched` 右 |
| - | - | - |
| `ALL INNER`, `ALL LEFT`, `ALL RIGHT`, `ALL FULL` | はい | はい |
| `SEMI LEFT`, `ANTI LEFT` | はい | いいえ |
| `ANY RIGHT`, `ANTI RIGHT` | いいえ | はい |
| `ASOF` (inner) | はい | いいえ |
| `SEMI RIGHT` | いいえ | いいえ |
| `ANY INNER`, `ANY LEFT`, `ASOF LEFT` | いいえ | いいえ |

結合がハッシュテーブル内でキーごとに 1 行だけを保持する場合、右側は収集できません。これは `ANY`、`SEMI`、`ANTI` 結合で行われます。重複する右行は保存されないため、カウントできません。別の左行によってすでに相手が確保されている左行の出力を結合が抑制する場合、左側は収集できません。この場合、出力される行数は一致した行数より少なくなります。

[`any_join_distinct_right_table_keys`](/ja/reference/settings/session-settings/other#any_join_distinct_right_table_keys) を有効にすると、`ANY` は以前の `RightAny` セマンティクスに切り替わります。このセマンティクスでは左行ごとに 1 行が出力されるため、両方のカウントが保持されます。その場合、`ANY RIGHT` と `ANY FULL` は**両方**の側を報告し、`ANY INNER` は `SEMI LEFT` に書き換えられます。

[`Join`](/ja/reference/engines/table-engines/special/join) テーブルエンジンも同じ表に従い、エンジンで宣言された種類と厳密性を使用します。`Join(ALL, INNER, …)` は両方の側を報告し、`Join(ANY, LEFT, …)` はどちらも報告しません。

**マージアルゴリズム。** `full_sorting_merge` と `parallel_full_sorting_merge` は、4 種類の `ALL`、`ANY INNER`、`ANY LEFT`、`ANY RIGHT`、`ASOF`、`ASOF LEFT` を受け入れます。`ASOF` と `ASOF LEFT` を除くすべての種類で**両方**の側を報告します。これらでは右側が `not collected` になります。また、`matches = 1` も不要です。2 つのソート済み入力を走査し、処理中に等しい範囲にあるすべての行を確認するため、後から再構築する必要がないからです。

`partial_merge` は `ALL INNER`、`ALL LEFT`、`ALL RIGHT`、`ALL FULL`、`ANY INNER`、`ANY LEFT`、`SEMI LEFT` を受け入れます。4 種類の `ALL` では両方の側を報告しますが、右側には `matches = 1` が必要です。`ANY INNER`、`ANY LEFT`、`SEMI LEFT` では右側が `not collected` になります。

**`direct`。** 左側のみです。右側は行としてマテリアライズされないキー・バリューストアであるため、`Right:` 行自体がありません。

**`CROSS`、`COMMA`、定数の `ON`。** [前述](#explain-analyze-join-algorithm-lines)のとおり、どちらの側も報告されません。

両方のアルゴリズムが数値を報告する場合、その値は一致します。マージアルゴリズムは単により多くの情報を持っているだけで、何を一致とみなすかについて見解が異なるわけではありません。

#### アルゴリズム固有の行

各JOIN実装がこれに加えて出力する行を見てみましょう。

`hash` および `parallel_hash` JOIN、ならびに `Join` テーブルエンジンでは、`Hash table:` 行に右テーブルから構築されたハッシュテーブルの情報が表示されます。

```txt theme={null}
Hash table: unique keys <unique_keys> · memory <peak_memory>
```

* `unique keys <unique_keys>` — ビルドフェーズ中にハッシュテーブルに格納された一意のキーの数。
* `memory <peak_memory>` — ビルドフェーズ中にハッシュテーブルが使用したピークメモリ。

`grace_hash` join では、`Hash table:` 行に、join がメモリ制限にどのように適応したかも表示されます。また、`Spill:` 行には、データがディスクにスピルされたかどうかが表示されます。

```txt theme={null}
Hash table: unique keys <unique_keys> · memory <peak_memory> · buckets <buckets> · rehashes <rehashes>
Spill: yes · left spilled <left_spilled_bytes> · right spilled <right_spilled_bytes>
```

* `buckets <buckets>` — 実行終了時点で Grace Hash Join に含まれるバケット数です。常に `2` のべき乗になります。
* `rehashes <rehashes>` — メモリ制限内に収めるために、バケット数を 2 倍にする必要があった回数です。
* `Spill:` — ディスクへのスピリングが発生したかどうかを示す `yes`/`no` フラグです。発生した場合、`left spilled <left_spilled_bytes>` と `right spilled <right_spilled_bytes>` は、それぞれ左側 (probe) と右側 (build) でスピリングされた圧縮バイト数を示します。スピリングが発生しなかった場合、この行は単に `Spill: no` となります。

`partial_merge` 結合では、`Right:` 行に右テーブルのバッファリング方法とソート方法に関する追加情報が含まれ、ソート時間は `Stage (build)` 行と `Stage (probe)` 行に表示されます。

```txt theme={null}
Right: rows estimated <estimated_right_rows> · rows <right_rows> · matched <matched_right_rows> · size <right_size> · blocks <right_blocks> · storage <in-memory|external> · match rate <match_rate>% · fanout <fanout>
  Stage (build): time <t> (<share>%) · parallelism <avg>/<max> · sort time <build_sort_time> · sort share <build_sort_share>%
  Stage (probe): time <t> (<share>%) · parallelism <avg>/<max> · sort time <probe_sort_time> · sort share <probe_sort_share>%
```

* `size <right_size>` — 右テーブルのブロックのメモリ使用量。
* `blocks <right_blocks>` — 右テーブルがバッファリングされたブロック数。
* `storage <in-memory|external>` — 右テーブルがメモリに収まったか (`in-memory`) 、またはディスクにスピルする必要があったか (`external`) 。`external` の場合、追加の `spilled <spilled_bytes>` にディスクに書き込まれた圧縮バイト数が表示されます。
* `sort time <sort_time>` — 右テーブルのソート (build ステージ) と、受信した各左ブロックのソート (probe ステージ) に費やされた時間。
* `sort share <sort_share>%` — ステージの `time` の割合が*クエリ全体の実行時間*に占める割合であるのに対し、`sort time` が*そのステージ自体のビジー時間* (各プロセッサの経過時間の合計) に占める割合。

`full_sorting_merge` join では、共通の `Left:` 行と `Right:` 行のみが出力されます。

`direct` join では、右側は行としてマテリアライズされず直接ルックアップされるキー・バリューストアであるため、`Left:` 行のみが出力されます。

`CROSS` または `COMMA` join、およびキーの等価条件を含まない任意の `ON` セクションでは、`Buffer:` 行に右テーブルがメモリ内でどのように保持されたかが示され、`Spill:` 行にディスクにスピルされたかどうかが表示されます。

```txt theme={null}
Buffer: memory <peak_memory> · compressed <yes|no>
Spill: yes · right spilled <right_spilled_bytes>
```

* `memory <peak_memory>` — バッファリングされた右テーブルが使用したピークメモリ。
* `compressed <yes|no>` — バッファリングされたブロックが少なくとも1つ圧縮されているかどうか。圧縮されている場合、reader は保存されているすべてのブロックを展開します。
* `Spill:` — `grace_hash` と同じ `yes`/`no` フラグで、`right spilled <right_spilled_bytes>` はディスクに書き込まれた圧縮バイト数を示します。

ここでは両側で `matched not collected` が報告されます。定数の predicate では、すべての左行がすべての右行と組み合わされるか、まったく組み合わされないかのいずれかとなるため、個々のどの行が一致したかを特定することはできません。

[`Join`](/ja/reference/engines/table-engines/special/join) テーブルエンジンとの join では、事前構築済みテーブルを説明する `Hash table:` 行とともに、両側が報告されます。右側では、クエリごとの build の行数ではなく、エンジンに格納されている行数がカウントされます。

#### プロセッサごとの時間

`processors = 1` の場合、各ステージの下に追加の行が出力され、そのステージのプロセッサごとの経過時間の分布が表示されます。

```txt theme={null}
Time per processor (<n>): min <t> · median <t> · max <t> · sum <t>
```

`<n>` はそのステージのプロセッサ数です。`median` と `max` の間に大きなギャップがある場合は、並列プロセッサ間で負荷に偏りがあることを示します。

### EXPLAIN ESTIMATE

クエリの実行時に、テーブルから読み取られると推定される行数、マーク数、パーツ数を表示します。[MergeTree](/ja/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 │
└──────────┴───────┴───────┴──────┴───────┘
```

### EXPLAIN WHATIF

仮想的なスキップ索引をディスク上に*マテリアライズ*することなく、それが `SELECT` クエリにもたらす効果を見積もります。[`CREATE HYPOTHETICAL INDEX`](/ja/reference/statements/hypothetical-index#create-hypothetical-index) で 1 つ以上の候補を定義し、`EXPLAIN WHATIF SELECT ...` を実行すると、各候補について、適用可否、推定読み取りマーク数、推定バイト数、スキップ率を確認できます。

[`CREATE HYPOTHETICAL PROJECTION`](/ja/reference/statements/hypothetical-projection#create-hypothetical-projection) で定義された仮想的な PROJECTION も候補として一覧表示されますが、その効果はまだ推定されず、いずれも `status: not_applicable` として報告されます。定義がテーブルに対して有効でなくなった PROJECTION (カラムが削除された、あるいは必要な機能を無効化する設定変更が行われた場合など) については、代わりにその理由が報告されます。

**構文**

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

**設定**

* `empirical` — `1` (デフォルト) では、スキップ率 (上限値) を測定するため、ベースラインで絞り込まれたグラニュールに対してメモリ内で索引を適用します。`0` ではその処理をスキップします。いずれの場合も、`empirical` で結果が得られない場合 (無効になっている、または索引をメモリ内で評価できない場合) 、推定器はカラム [STATISTICS](/ja/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
  empirical_reason: <reason>        (only when empirical_status = unsupported)
  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`: ベースラインで pruned されたグラニュールを対象に、メモリ内で索引を構築し、その索引によってスキップされるグラニュール数を数えます。これは上限値です。制限事項については [`CREATE HYPOTHETICAL INDEX`](/ja/reference/statements/hypothetical-index#limitations) を参照してください。
  * `statistical`: カラム STATISTICS から導出されます。empirical が無効化されている場合 (`empirical = 0`) 、または empirical で結果を生成できず、かつ関連するカラムにカラム STATISTICS が定義されている場合に使用されます。
  * `applicability_only`: 索引は predicate に適用可能ですが、empirical と statistical のいずれでも結果を生成できなかったことを示します (たとえば `empirical = 0` でカラム STATISTICS が定義されていない場合) 。保守的な上限として `skip_ratio: 0.0%` を返します。
* `empirical_reason` — 経験的推定を実行できなかった理由です。`empirical_status: unsupported` の場合にのみ表示されます。たとえば、`merge_tree_min_rows_for_seek` または `merge_tree_min_bytes_for_seek` がゼロ以外の場合、実際の読み取りではマーク範囲が統合されますが、グラニュールごとのカウントではこれをモデル化しないため、推定は `statistical` または `applicability_only` にフォールバックします。
* `sampled_parts` / `sampled_marks` — `<baseline-pruned> / <total in the table>`。テーブル全体のうち、PK、partition、既存の索引による pruning を通過した割合、つまり仮想索引への入力となる部分を示します。
* `est_bytes` — 読み取られるバイト数の推定値です。テーブルの平均行サイズから導出されるため概算であり、ストレージや圧縮によって変動します。ベースラインの行はクエリが行を読み取る場合にのみ表示され、候補ごとの行はベースラインのバイト推定値がわかっている場合にのみ表示されます。

この設定は `WHATIF` と `SELECT` の間にインラインで記述します。`SETTINGS` キーワードはありません (これは、他の `EXPLAIN` バリアントでオプションを受け付ける方法と一致しています) 。

テーブルに仮想索引も仮想 PROJECTION も定義されていない場合、`EXPLAIN WHATIF` は `status: not_applicable` を返し、作成を促すヒントを表示します。

**結合行 (複数候補)**

2 つ以上の候補が経験的に評価される場合、`EXPLAIN WHATIF` は候補ごとの行の後に `(combined: idx_a, idx_b, ...)` という名前のブロックを 1 つ追加します。これは、それら *すべて* の索引を同時に持った場合の複合的な効果を報告します。実際の読み取りでは、グラニュールは *すべて* のスキップ索引を通過した場合にのみ残るため、結合推定は各候補で生き残ったグラニュールの積集合になります。したがって、その `skip_ratio` は少なくとも最良の単一候補と同等以上になります — 相補的な索引は組み合わせることでより多くを削減し、冗長な索引では値が変わりません。

寄与するのは `source: empirical` の候補だけです。これは、結合行が各 グラニュール ごとの生存集合の積集合を取って構築されるためです。`statistical` または `applicability_only` と推定された候補には グラニュール ごとのデータがないため除外されます。その結果、結合ブロックが表示されるのは少なくとも 2 つの候補が経験的推定を生成した場合だけで、それ以外の場合 (たとえば `empirical = 0` の場合) には省略されます。その推定フィールドは、`elapsed_us` が `0` である点を除き、候補ごとの経験的なブロックと同じです — 結合推定は候補ごとのスキャンから導出されるものであり、新たなスキャンではありません。合成された `(combined: ...)` という名前はレポート用ラベルにすぎず、`force_data_skipping_indices` では使用できません。

**経験的な例**

```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` は平均行サイズに基づく推定値のため、正確な値は変動します。)

**統計の例**

[カラム STATISTICS](/ja/reference/engines/table-engines/mergetree-family/mergetree#column-statistics)はデフォルトで無効になっています。`statistical` パスを試すには、まず対象のカラムでこれらを定義し、materialize mutation が完了するまで待ちます:

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

次に、推定器がカラム STATISTICS にフォールバックするよう、経験的なパスを無効にします：

```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` のカラム STATISTICS における選択性 (10000 行中およそ 10 行) に基づくもので、`skip_ratio` の上限として報告されます。`sampled_parts` / `sampled_marks` はなく、データは読み取られていません。

どちらの方法も利用できない場合 (たとえば `empirical = 0` で、かつカラム STATISTICS が定義されていない場合) 、推定器は `source: applicability_only` と保守的な `skip_ratio: 0.0%` を報告します。

### EXPLAIN TABLE OVERRIDE

テーブル関数を介してアクセスするテーブルのスキーマに対して、テーブルオーバーライドを適用した結果を表示します。
また、いくつかの検証も行い、オーバーライドによって何らかの問題が発生する場合は例外をスローします。

**例**

次のようなリモート 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>
