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

> Documentação do EXPLAIN

# Instrução EXPLAIN

Mostra o plano de execução de uma instrução.

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

Sintaxe:

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

Exemplo:

```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">
  ## Tipos de EXPLAIN
</div>

* `AST` — Árvore de sintaxe abstrata.
* `SYNTAX` — Texto da consulta após otimizações no nível da AST.
* `QUERY TREE` — Árvore de consulta após otimizações no nível da árvore de consulta.
* `PLAN` — Plano de execução da consulta.
* `PIPELINE` — Pipeline de execução da consulta.
* `ANALYZE` — Executa a consulta e anota o plano de execução com métricas de runtime medidas.
* `ESTIMATE` — Número estimado de linhas, marcas e partes a serem lidas das tabelas durante o processamento da consulta.
* `TABLE OVERRIDE` — Resultado validado de um override de tabela em um esquema de função de tabela.

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

Exibe a AST da consulta. Compatível com todos os tipos de consulta, não apenas `SELECT`.

Configurações:

* `graph` – Exibe a AST como um grafo descrito na linguagem de descrição de grafos [DOT](https://en.wikipedia.org/wiki/DOT_\(graph_description_language\)). Padrão: 0.

Exemplos:

```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 SYNTAX
</div>

Mostra a Árvore de Sintaxe Abstrata (AST) de uma consulta após a análise de sintaxe.

Isso é feito fazendo o parsing da consulta, construindo a AST e a árvore de consulta da consulta, opcionalmente executando o analisador de consultas e os passes de otimização, e então convertendo a árvore de consulta de volta para a AST da consulta.

Configurações:

* `oneline` – Imprime a consulta em uma única linha. Padrão: `0`.
* `run_query_tree_passes` – Executa os passes da árvore de consulta antes de exibi-la. Padrão: `0`.
* `query_tree_passes` – Se `run_query_tree_passes` estiver definido, especifica quantos passes executar. Sem especificar `query_tree_passes`, ele executa todos os passes.
* `single_record` – Retorna a consulta reformatada como um único registro multilinha em vez de um registro por linha. Padrão: `1` (controlado pela configuração `explain_syntax_single_record`). Defina como `0` para restaurar a saída histórica de um registro por linha, ou defina `explain_syntax_single_record = 0` (globalmente ou em `SETTINGS` por consulta), ou defina `compatibility` como qualquer versão anterior à `26.8`.

Exemplos:

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

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

Configurações:

* `run_passes` — Executa todos os passes da árvore de consulta antes de exibir a árvore de consulta. Padrão: `1`.
* `dump_passes` — Exibe informações sobre os passes usados antes de exibir a árvore de consulta. Padrão: `0`.
* `passes` — Especifica quantos passes devem ser executados. Se definido como `-1`, executa todos os passes. Padrão: `-1`.
* `dump_tree` — Exibe a árvore de consulta. Padrão: `1`.
* `dump_ast` — Exibe a AST da consulta gerada a partir da árvore de consulta. Padrão: `0`.

Exemplo:

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

Exibe os passos do plano de consulta.

Configurações:

* `optimize` — Controla se as otimizações do plano de consulta são aplicadas antes da exibição do plano. Padrão: 1.
* `header` — Exibe o cabeçalho de saída do passo. Padrão: 0.
* `description` — Exibe a descrição do passo. Padrão: 1.
* `indexes` — Mostra os índices usados, o número de partes filtradas e o número de grânulos filtrados para cada índice aplicado. Padrão: 0. Compatível com tabelas [MergeTree](/pt-BR/reference/engines/table-engines/mergetree-family/mergetree). A partir do ClickHouse >= v25.9, esta instrução só exibe uma saída adequada quando usada com `SETTINGS use_query_condition_cache = 0, use_skip_indexes_on_data_read = 0`.
* `projections` — Mostra todas as projeções analisadas e seu efeito na filtragem no nível de partes com base nas condições da chave primária da projeção. Para cada projeção, esta seção inclui estatísticas como o número de partes, linhas, marcas e intervalos avaliados usando a chave primária da projeção. Também mostra quantas partes de dados foram ignoradas devido a essa filtragem, sem ler a própria projeção. Se uma projeção foi realmente usada para leitura ou apenas analisada para filtragem pode ser determinado pelo campo `description`. Padrão: 0. Compatível com tabelas [MergeTree](/pt-BR/reference/engines/table-engines/mergetree-family/mergetree).
* `actions` — Exibe informações detalhadas sobre as ações do passo. Padrão: 1.
* `sorting` — Exibe a descrição da ordenação para cada passo do plano que produz saída ordenada. Padrão: 0.
* `keep_logical_steps` — Mantém os passos lógicos do plano para junções em vez de convertê-los em implementações físicas de junção. Padrão: 0.
* `json` — Exibe os passos do plano de consulta como uma linha no formato [JSON](/pt-BR/reference/formats/JSON/JSON). Padrão: 0. Recomenda-se usar o formato [TabSeparatedRaw (TSVRaw)](/pt-BR/reference/formats/TabSeparated/TabSeparatedRaw) para evitar escapes desnecessários.
* `input_headers` — Exibe os cabeçalhos de entrada do passo. Padrão: 0. Em geral, isso só é útil para desenvolvedores depurarem problemas relacionados à incompatibilidade entre cabeçalhos de entrada e saída.
* `column_structure` — Exibe também a estrutura das colunas nos cabeçalhos, além do nome e do tipo. Padrão: 0. Em geral, isso só é útil para desenvolvedores depurarem problemas relacionados à incompatibilidade entre cabeçalhos de entrada e saída.
* `distributed` — Mostra os planos de consulta executados em nós remotos para tabelas distribuídas ou réplicas paralelas. Não é compatível com `json`. Padrão: 0.
* `compact` — Quando ativado, oculta do plano os passos de expressão e as informações detalhadas das ações (entradas, funções, aliases e posições de saída). Só tem efeito quando `actions = 1`. Padrão: 1.
* `pretty` — Exibe a árvore do plano usando caracteres de desenho de linha (├──, └──, │) em vez de indentação para visualizar a hierarquia. Também formata as propriedades do passo de junção em linha. Padrão: 1.

<Note>
  Por padrão, `explain_query_plan_default = 'pretty'`, portanto `actions`, `compact` e `pretty` são inicializados com `1`, e o plano é renderizado na forma compacta, com formatação pretty e com anotações de ações. Especificar explicitamente qualquer uma dessas opções na instrução `EXPLAIN` (por exemplo, `EXPLAIN actions = 0, compact = 0, pretty = 0 SELECT ...`) sempre substitui o padrão.

  Antes do ClickHouse 26.7, os valores padrão de `actions`, `compact` e `pretty` eram `0`. Você ainda pode obter essa saída definindo `explain_query_plan_default = 'legacy'` (globalmente ou em `SETTINGS` por consulta) ou definindo `compatibility` para qualquer versão anterior à `26.7`.

  As opções `json` e `distributed` não habilitam os padrões de `pretty` (`actions`, `compact` e `pretty`), mesmo quando `explain_query_plan_default = 'pretty'`. Para incluir detalhes das ações na saída delas, defina `actions = 1` manualmente.
</Note>

Exemplo:

```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>
  Não há suporte à estimativa de custo do passo e da consulta.
</Note>

Quando `json = 1`, o plano da consulta é representado em formato JSON. Cada nó é um dicionário que sempre tem as chaves `Node Type`, `Node Id` e `Plans`. `Node Type` é uma string com o nome do passo, e `Node Id` é um identificador exclusivo do passo (o nome do passo com um sufixo numérico, por exemplo, `Union_10`). `Plans` é um array com descrições dos passos filhos. Outras chaves opcionais podem ser adicionadas dependendo do tipo de nó e das configurações.

Exemplo:

```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"
            }
          ]
        }
      ]
    }
  }
]
```

Com `description` = 1, a chave `Description` é adicionada ao passo:

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

Com `header` = 1, a chave `Header` é adicionada ao passo na forma de um array de colunas.

Exemplo:

```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"
            }
          ]
        }
      ]
    }
  }
]
```

Com `indexes` = 1, a chave `Indexes` é adicionada. Ela contém um array dos índices usados. Cada índice é descrito como JSON com a chave `Type` (uma string `Partition Min-Max`, `Partition`, `Statistics`, `PrimaryKey` ou `Skip`) e chaves opcionais:

* `Name` — O nome do índice (atualmente usado apenas para índices `Skip`).
* `Keys` — O array de colunas usado pelo índice.
* `Condition` — A condição usada.
* `Description` — A descrição do índice (atualmente usada apenas para índices `Skip`).
* `Parts` — O número de partes após/antes da aplicação do índice.
* `Granules` — O número de grânulos após/antes da aplicação do índice.
* `Ranges` — O número de intervalos de grânulos após a aplicação do índice.

Exemplo:

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

Com `projections` = 1, a chave `Projections` é adicionada. Ela contém um array de projeções analisadas. Cada projeção é descrita como JSON com as seguintes chaves:

* `Name` — O nome da projeção.
* `Condition` — A condição da chave primária usada pela projeção.
* `Description` — A descrição de como a projeção é usada (por exemplo, filtragem em nível de partes).
* `Selected Parts` — Número de partes selecionadas pela projeção.
* `Selected Marks` — Número de marcas selecionadas.
* `Selected Ranges` — Número de intervalos selecionados.
* `Selected Rows` — Número de linhas selecionadas.
* `Filtered Parts` — Número de partes ignoradas devido à filtragem em nível de partes.

Exemplo:

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

Com `actions` = 1, as chaves adicionadas dependem do tipo de passo.

Exemplo:

```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"
        }
      ]
    }
  }
]
```

Com `compact = 0` e `actions = 1`, os passos de `Expression` podem ser vistos juntamente com informações detalhadas sobre as expressões:

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

Com `distributed` = 1, a saída inclui não apenas o plano de consulta local, mas também os planos de consulta que serão executados nos nós remotos. Isso é útil para analisar e depurar consultas distribuídas.

<Note>
  `distributed` é exibido apenas na forma `legacy` (sem `pretty`), porque a saída `pretty` não integra os planos de shard remotos à árvore do plano. Por esse motivo, habilitar `distributed` desabilita automaticamente os padrões de `pretty` (`actions`, `compact` e `pretty`), independentemente de `explain_query_plan_default`. Você ainda pode definir `actions=1` manualmente. A opção `distributed` também não é compatível com `json`.
</Note>

Exemplo com tabela distribuída:

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

Exemplo com réplicas paralelas:

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

Em ambos os exemplos, o plano de consulta exibe o fluxo de execução completo, incluindo etapas locais e remotas.

Com `pretty` = 1, a árvore do plano é exibida usando caracteres de desenho de linhas em vez de recuo, e informações adicionais são mostradas para os passos principais:

* As **colunas de saída da consulta** são exibidas no topo do plano.
* **Expressões** em filtros, chaves de agregação, descrições de ordenação e funções de janela são exibidas em uma notação semelhante a SQL legível por humanos (por exemplo, `a + 1 > 5` em vez de `greater(plus(a, 1), 5)`). Os prefixos internos de identificadores de coluna (como `__table1.`) são removidos para maior clareza.
* **Passos de origem** (como `ReadFromMergeTree`) exibem suas colunas de saída.
* **Passos de filtro** exibem a condição de filtro em notação SQL. Quando há filtros de join em tempo de execução, eles são mostrados separadamente.
* **Passos de agregação** exibem chaves e funções de agregação com seus argumentos (por exemplo, `sum(c)`, `count()`).
* **Conjuntos de `IN`** de literais Tuple mostram seus valores (truncados para conjuntos grandes), conjuntos baseados em subconsulta são rotulados como `subquery1`, `subquery2` etc., e conjuntos de tabelas com o motor `Set` mostram o nome da tabela.
* **Passos de join** exibem a relação de join usando notação matemática, a contagem estimada de linhas do resultado,
  e quais colunas de saída vêm do lado esquerdo e do lado direito. Os símbolos a seguir são usados para
  representar diferentes tipos de join:

| Símbolo                | Tipo de junção         |
| ---------------------- | ---------------------- |
| `⋈`                    | Junção interna         |
| `⟕`                    | Junção à esquerda      |
| `⟖`                    | Junção à direita       |
| `⟗`                    | Junção completa        |
| `⋉`                    | Junção semi à esquerda |
| `⋊`                    | Junção semi à direita  |
| `⋉` with strikethrough | Junção anti à esquerda |
| `⋊` with strikethrough | Junção anti à direita  |
| `×`                    | Junção cruzada         |

Por exemplo, `t1 ⟕ t2` significa uma junção à esquerda entre as tabelas `t1` e `t2`.
O número entre colchetes após o nome da tabela (por exemplo, `t1[100]`) indica a contagem estimada de linhas
quando há estatísticas da tabela disponíveis.

A opção `pretty` funciona bem em conjunto com `compact = 1`, que oculta os passos `Expression` e as informações detalhadas de ações, tornando o plano mais fácil de ler.

Um exemplo detalhado com junções:

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

Configurações:

* `header` — Imprime o cabeçalho de cada porta de saída. Padrão: 0.
* `graph` — Imprime um grafo descrito na linguagem de descrição de grafos [DOT](https://en.wikipedia.org/wiki/DOT_\(graph_description_language\)). Padrão: 0.
* `compact` — Imprime o grafo no modo compacto se a configuração `graph` estiver habilitada. Padrão: 1.
* `compact_repeated_processor_chains` — Compacta cadeias repetidas adjacentes de processadores na saída de texto, mostrando uma cópia da cadeia com uma contagem de repetições. Isso pode facilitar a leitura de pipelines paralelos quando a mesma cadeia aparece muitas vezes, por exemplo, em junções. Isso não afeta a saída do grafo. Padrão: 0.

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

Quando `compact=0` e `graph=1`, os nomes dos processadores conterão um sufixo adicional com um identificador exclusivo do processador.

Exemplo:

```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-analyze">
  ### EXPLAIN ANALYZE
</div>

`EXPLAIN ANALYZE` de fato executa a consulta, descarta as linhas de resultado e imprime a mesma árvore de plano que `EXPLAIN PLAN`, com cada passo anotado com o que realmente aconteceu em tempo de execução.

Configurações:

`EXPLAIN ANALYZE` aceita as mesmas opções de exibição que `EXPLAIN PLAN` (documentadas na seção [EXPLAIN PLAN](#explain-plan)).

* `header` — consulte a seção [EXPLAIN PLAN](#explain-plan).
* `description` — consulte a seção [EXPLAIN PLAN](#explain-plan).
* `projections` — consulte a seção [EXPLAIN PLAN](#explain-plan).
* `sorting` — consulte a seção [EXPLAIN PLAN](#explain-plan).
* `input_headers` — consulte a seção [EXPLAIN PLAN](#explain-plan).
* `column_structure` — consulte a seção [EXPLAIN PLAN](#explain-plan).
* `actions` — consulte a seção [EXPLAIN PLAN](#explain-plan). Padrão: 1.
* `indexes` — consulte a seção [EXPLAIN PLAN](#explain-plan). Padrão: 1.
* `compact` — consulte a seção [EXPLAIN PLAN](#explain-plan). Padrão: 1.
* `pretty` — consulte a seção [EXPLAIN PLAN](#explain-plan). Padrão: 1.
* `processors` — Para `EXPLAIN ANALYZE`, imprime uma linha adicional por estágio com a distribuição do tempo decorrido por processador: `min`, `median`, `max` e `sum`. Útil para identificar desequilíbrio de carga entre processadores paralelos. Padrão: 0.
* `matches` — Para `EXPLAIN ANALYZE`, faz com que as etapas de junção realizem o processamento adicional necessário para as métricas `matched`, `match rate` e `fanout` nos casos em que esses números não podem ser derivados do que a junção produz de qualquer forma. Quando podem, são relatados sem essa opção. Consulte [Etapas de junção](#explain-analyze-join-steps). Padrão: 0.

<Note>
  Como `EXPLAIN ANALYZE` realmente executa a consulta encapsulada, ele se comporta como essa
  consulta — e, diferentemente das formas `EXPLAIN` que não executam — de várias maneiras:

  * **Cotas e limites.** Ele é contabilizado nas mesmas [quotas](/pt-BR/concepts/features/configuration/server-config/quotas)
    e está sujeito aos mesmos [limits](/pt-BR/concepts/features/configuration/settings/query-complexity)
    (por exemplo, `query_selects`, `read_rows`) que a execução direta da consulta. Fontes isentas de cotas durante o planejamento
    (como `system.one`) não são contabilizadas.
  * **Transações com falha.** Dentro de uma [transação](/pt-BR/concepts/features/operations/insert/transactions)
    que já falhou (`ROLLED_BACK`), ele é rejeitado com `INVALID_TRANSACTION`,
    assim como uma instrução `SELECT` simples — emita `ROLLBACK` primeiro.
  * **Leituras de streaming.** Em uma leitura de streaming (`FROM ... STREAM`), ele é rejeitado com
    `NOT_IMPLEMENTED`, porque esse tipo de leitura nunca termina.
  * **Consultas distribuídas.** Não há suporte para ele em consultas executadas em
    modo [Distributed](/pt-BR/reference/engines/table-engines/special/distributed).
</Note>

Exemplo:

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

Vamos examinar a saída. Primeiro, vamos ver o cabeçalho.

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

* `Time` — tempo total dividido entre as fases de planejamento (isto é, criação do plano + otimização do plano + construção do pipeline) e execução (execução do pipeline).
* `Read` — linhas e bytes não comprimidos lidos das tabelas, com a taxa de transferência — os mesmos números que o rodapé padrão da consulta informa como "Processed".
* `Peak memory` — pico de memória usado pela consulta.

Agora vamos examinar as novas linhas que aparecem no plano da consulta.

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

Linhas e bytes são informados uma vez para todo o passo (a linha `I/O`). O tempo e o paralelismo são informados por estágio do passo nas linhas indentadas abaixo.

* `rows <in> → <out>` — linhas que entraram e saíram do passo; (`<selectivity>`%) mostra o quanto o passo filtrou (`out/in`) ou expandiu os dados; fica oculto quando as linhas de entrada são iguais às linhas de saída e quando as linhas de entrada são iguais a `0`.
* `<bytes_in> → <bytes_out>` — bytes não comprimidos em memória que passam pelo passo (omitido quando ambos são zero).
* `time <t> (<share>%)` — tempo de relógio em que o estágio esteve ativo e sua participação no tempo de execução da consulta (isto é, sem o tempo de compilação). Observe que as participações podem somar mais de 100%, porque estágios e passos são executados de forma concorrente.
* `parallelism <avg>/<max>` — número médio de threads de CPU trabalhando ao mesmo tempo neste estágio, do máximo que ele poderia usar. Um valor próximo do máximo significa que o estágio foi bem paralelizado; próximo de 1 significa que ele foi executado principalmente de forma serial.
* `Stage (<stage>)` — o nome do estágio. Um passo com um único estágio imprime a linha de tempo diretamente, sem um rótulo `Stage (...)`. Passos com vários estágios imprimem uma linha identificada para cada estágio; por exemplo, `Aggregating` mostra `Stage (partial aggregation)` e `Stage (final aggregation)`, e um hash join mostra `Stage (build)` e `Stage (probe)`.

<Note>
  O ClickHouse paraleliza não apenas a execução de tarefas dentro de um passo do plano, mas também a execução dos próprios passos do plano. A métrica `parallelism` reflete apenas o trabalho deste passo. Outros passos podem ser executados de forma concorrente, portanto esse número não mostra como o paralelismo do passo se compara ao da consulta como um todo.
</Note>

<Note>
  O número máximo em `parallelism` é calculado como o valor mínimo entre:

  1. o número total de tarefas dentro do passo do plano;
  2. o número máximo de threads de processamento de consultas definido em `max_threads`.
</Note>

<div id="explain-analyze-join-steps">
  #### Etapas de junção
</div>

Em uma etapa de junção, `EXPLAIN ANALYZE` exibe linhas de *participação* para cada lado — `Left` e `Right` — seguidas de linhas específicas da implementação da junção. `Left` e `Right` correspondem aos lados lógicos da instrução SQL. Na maioria dos casos, `Left` também corresponde ao lado de sondagem da junção, enquanto `Right` corresponde ao lado de compilação da junção. No entanto, isso nem sempre ocorre devido à troca que pode acontecer durante a execução da junção. Todos os valores de [`join_algorithm`](/pt-BR/reference/settings/session-settings/join#join_algorithm) são contemplados (`hash`, `parallel_hash`, `grace_hash`, `partial_merge`, `full_sorting_merge`, `parallel_full_sorting_merge`, `direct`), assim como as duas implementações que essa configuração não pode selecionar: uma junção `CROSS` ou `COMMA`, qualquer seção `ON` sem igualdade de chave e o motor de tabela [`Join`](/pt-BR/reference/engines/table-engines/special/join). A maioria deles informa ambos os lados; alguns informam apenas o lado que materializam (por exemplo, `direct` exibe apenas `Left:`).

As linhas de cada lado seguem o mesmo formato:

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

Para cada lado, `EXPLAIN ANALYZE` informa:

* `rows <rows>` — o número total de linhas desse lado que passaram pela junção.
* `matched <matched_rows>` — o número de linhas desse lado que encontraram pelo menos uma linha correspondente no outro lado. Isso conta *linhas*, não chaves: se uma chave ocorrer três vezes no lado direito e houver correspondência, as três linhas da direita serão contadas como correspondentes.
* `match rate <match_rate>%` — a porcentagem de linhas desse lado que tiveram correspondência, calculada como `100 * <matched_rows> / <rows>`.
* `fanout <fanout>` — quantas linhas de saída, em média, cada linha correspondente desse lado produziu.

Um número que não pode ser calculado com exatidão é informado como `not collected`, em vez de `0`. `match rate` e `fanout` são derivados de `matched`; portanto, um lado sem esse valor informa os três como `not collected`.

<div id="explain-analyze-fanout">
  #### Fanout
</div>

`fanout` mede a multiplicação de linhas:

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

Uma junção externa gera uma linha de saída preenchida com `NULL` para cada linha de um lado preservado que não encontrou correspondência. Essas linhas são subtraídas para não diluírem a razão. Apenas um lado preservado as tem — o lado direito em `RIGHT` e `FULL`, e o esquerdo em `LEFT` e `FULL`:

* `fanout = 0` — as linhas correspondentes não produziram nenhuma linha de saída, como ocorre em uma junção `ANTI`: ela gera apenas as linhas que não encontraram correspondência.
* `fanout = 1` — uma junção 1:1 sem duplicações; cada linha correspondente produziu exatamente uma linha de saída.
* `fanout > 1` — uma junção 1:N; chaves duplicadas do outro lado multiplicaram as linhas. Um valor alto nos dois lados ao mesmo tempo é sinal de uma explosão cartesiana não intencional.

<div id="explain-analyze-matches">
  #### Quando os números exigem `matches = 1`
</div>

A maioria desses números decorre dos dados que a junção já gera e é informada por um simples `EXPLAIN ANALYZE`. Os demais exigem uma contabilização que a junção não faria de outra forma e, por isso, só são informados com `EXPLAIN ANALYZE matches = 1`. Quais são eles depende do algoritmo; na família hash, há dois casos:

* o lado **direito** de `ALL INNER` e `ALL LEFT`, que exige marcar todas as linhas correspondentes do lado direito;
* o lado **esquerdo** de `ALL LEFT` e `ALL FULL`, mas somente quando a consulta não seleciona nada da
  tabela à direita e a seção `ON` é uma simples igualdade de chave. Caso contrário, a sondagem já registra
  quais linhas do lado esquerdo tiveram correspondência — seja para materializar as colunas do lado direito ou para avaliar a condição
  residual — e a contagem é exata sem a opção.

`partial_merge` precisa disso para o lado **direito** dos quatro tipos `ALL`, pelo mesmo motivo.
`full_sorting_merge` e `parallel_full_sorting_merge` precisam disso para **ambos** os lados dos tipos `ANY`. Os
tipos `ALL` não precisam de nada.

<Note>
  A opção fica desativada por padrão porque a contabilização adicional tem custo, e não usá-la reduz a fidelidade da medição. O trabalho é realizado dentro do loop de sondagem e aumenta com o número de linhas de saída. Use `matches = 1` quando precisar saber as correspondências exatas encontradas pelas linhas dos lados esquerdo e direito.
</Note>

`matches = 1` não torna todas as combinações passíveis de coleta. Os lados que uma junção pode informar decorrem do que ela já precisa fazer de qualquer forma; portanto, isso depende tanto do algoritmo quanto do tipo e da strictness.

**Família hash.** `hash`, `parallel_hash` e `grace_hash` sempre se comportam da mesma forma:

| Junção                                           | `matched` esquerdo | `matched` direito |
| ------------------------------------------------ | ------------------ | ----------------- |
| `ALL INNER`, `ALL LEFT`, `ALL RIGHT`, `ALL FULL` | sim                | sim               |
| `SEMI LEFT`, `ANTI LEFT`                         | sim                | não               |
| `ANY RIGHT`, `ANTI RIGHT`                        | não                | sim               |
| `ASOF` (interna)                                 | sim                | não               |
| `SEMI RIGHT`                                     | não                | não               |
| `ANY INNER`, `ANY LEFT`, `ASOF LEFT`             | não                | não               |

O lado direito não está disponível sempre que a junção mantém apenas uma linha por chave em sua tabela hash, como fazem as junções `ANY`, `SEMI` e `ANTI`: as linhas duplicadas do lado direito nunca são armazenadas e, portanto, não podem ser contadas. O lado esquerdo não está disponível quando a junção suprime a saída de uma linha do lado esquerdo cuja correspondente já foi reivindicada por outra linha desse lado, fazendo com que as linhas emitidas sejam uma subcontagem das linhas com correspondência.

Ativar [`any_join_distinct_right_table_keys`](/pt-BR/reference/settings/session-settings/other#any_join_distinct_right_table_keys) muda `ANY` para a semântica mais antiga `RightAny`, que emite uma linha por linha do lado esquerdo e, portanto, mantém ambas as contagens. `ANY RIGHT` e `ANY FULL` passam a informar **ambos** os lados, e `ANY INNER` é reescrito como `SEMI LEFT`.

O motor de tabela [`Join`](/pt-BR/reference/engines/table-engines/special/join) segue a mesma tabela, usando o tipo e a strictness declarados no mecanismo: `Join(ALL, INNER, …)` informa ambos os lados, enquanto `Join(ANY, LEFT, …)` não informa nenhum deles.

**Algoritmos de merge.** `full_sorting_merge` e `parallel_full_sorting_merge` aceitam os quatro tipos `ALL`, `ANY INNER`, `ANY LEFT`, `ANY RIGHT`, `ASOF` e `ASOF LEFT`. Eles informam **ambos** os lados para todos os tipos, exceto `ASOF` e `ASOF LEFT`, nos quais o lado direito é `not collected`, mesmo sem `matches = 1` — eles percorrem as duas entradas ordenadas e veem todas as linhas de uma faixa de valores iguais à medida que as consomem, portanto nada precisa ser reconstruído posteriormente.

`partial_merge` aceita `ALL INNER`, `ALL LEFT`, `ALL RIGHT`, `ALL FULL`, `ANY INNER`, `ANY LEFT` e `SEMI LEFT`. Ele informa ambos os lados para os quatro tipos `ALL`, sendo o direito com `matches = 1`; para `ANY INNER`, `ANY LEFT` e `SEMI LEFT`, o lado direito é `not collected`.

**`direct`.** Somente o lado esquerdo. O lado direito é um armazenamento de chave-valor que nunca é materializado em linhas e, portanto, não possui nenhuma linha `Right:`.

**`CROSS`, `COMMA` e um `ON` constante.** Nenhum dos lados, conforme descrito [acima](#explain-analyze-join-algorithm-lines).

Quando ambos os algoritmos informam um número, os resultados coincidem. Os algoritmos de mesclagem simplesmente têm mais informações; eles não divergem quanto ao que constitui uma correspondência.

<div id="explain-analyze-join-algorithm-lines">
  #### Linhas específicas do algoritmo
</div>

Vejamos as linhas adicionais incluídas por cada implementação de junção.

Para as junções `hash` e `parallel_hash` e para o motor de tabela `Join`, uma linha `Hash table:` descreve a tabela hash criada a partir da tabela à direita:

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

* `unique keys <unique_keys>` — o número de chaves únicas armazenadas na tabela hash durante a fase de compilação.
* `memory <peak_memory>` — o pico de memória usado pela tabela hash durante a fase de compilação.

Para a junção `grace_hash`, a linha `Hash table:` também informa como a junção se adaptou ao limite de memória, e uma linha `Spill:` informa se os dados foram gravados em disco:

```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>` — o número de buckets que a junção grace hash teve ao final da execução. Esse número é sempre uma potência de `2`.
* `rehashes <rehashes>` — quantas vezes o número de buckets precisou ser dobrado para se ajustar ao limite de memória.
* `Spill:` — uma flag `yes`/`no` que indica se houve spill para disco. Quando houve, `left spilled <left_spilled_bytes>` e `right spilled <right_spilled_bytes>` informam os bytes comprimidos gravados em disco dos lados esquerdo (probe) e direito (build); quando não houve spill, a linha é simplesmente `Spill: no`.

Na junção `partial_merge`, a linha `Right:` contém informações adicionais sobre como a tabela da direita foi armazenada em buffer e ordenada, e o tempo de ordenação é exibido nas linhas `Stage (build)` e `Stage (probe)`:

```txt theme={null}
Right: 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>` — o tamanho em memória dos blocos da tabela à direita.
* `blocks <right_blocks>` — o número de blocos nos quais a tabela à direita foi armazenada em buffer.
* `storage <in-memory|external>` — indica se a tabela à direita coube na memória (`in-memory`) ou precisou ser descarregada em disco (`external`). Quando é `external`, um campo adicional `spilled <spilled_bytes>` informa os bytes comprimidos gravados em disco.
* `sort time <sort_time>` — o tempo gasto na ordenação da tabela à direita (no estágio de compilação) e de cada bloco recebido da esquerda (no estágio de sondagem).
* `sort share <sort_share>%` — `sort time` como proporção do *tempo de ocupação do próprio estágio* (a soma do tempo decorrido de seus processadores), diferentemente da porcentagem de `time` do estágio, que representa uma proporção do *tempo de execução de toda a consulta*.

Para a junção `full_sorting_merge`, são impressas apenas as linhas comuns `Left:` e `Right:`.

Para a junção `direct`, é impressa apenas a linha `Left:`, pois o lado direito é um armazenamento de chave-valor consultado diretamente, em vez de ser materializado em linhas.

Para uma junção `CROSS` ou `COMMA`, e para qualquer seção `ON` sem igualdade de chave, uma linha `Buffer:` descreve como a tabela à direita foi mantida na memória, e uma linha `Spill:` informa se ela foi descarregada em disco:

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

* `memory <peak_memory>` — o pico de memória ocupado pela tabela da direita armazenada em buffer.
* `compressed <yes|no>` — indica se pelo menos um bloco armazenado em buffer foi comprimido; nesse caso, os leitores descompactam todos os blocos armazenados.
* `Spill:` — a mesma flag `yes`/`no` de `grace_hash`, com `right spilled <right_spilled_bytes>` informando os bytes comprimidos gravados em disco.

Ambos os lados informam `matched not collected` aqui: um predicado constante associa todas as linhas da esquerda a todas as linhas da direita ou a nenhuma delas; portanto, não é possível determinar quais linhas individuais corresponderam.

Para uma junção com o motor de tabela [`Join`](/pt-BR/reference/engines/table-engines/special/join), ambos os lados são informados, juntamente com a linha `Hash table:`, que descreve a tabela pré-criada. O lado direito contabiliza as linhas armazenadas no mecanismo, não as linhas de uma compilação específica da consulta.

<div id="explain-analyze-processors">
  #### Tempos por processador
</div>

Com `processors = 1`, uma linha extra é impressa abaixo de cada estágio, mostrando a distribuição do tempo decorrido entre os processadores do estágio:

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

`<n>` é o número de processadores no estágio. Uma grande diferença entre `median` e `max` indica um desequilíbrio de carga entre processadores paralelos.

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

Mostra o número estimado de linhas, marcas e partes que serão lidas das tabelas durante o processamento da consulta. Funciona com tabelas da família [MergeTree](/pt-BR/reference/engines/table-engines/mergetree-family/mergetree).

**Exemplo**

Criando uma tabela:

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

Estima o benefício que um skip index hipotético teria em uma consulta `SELECT`, *sem* materializar o índice em disco. Defina um ou mais candidatos com [`CREATE HYPOTHETICAL INDEX`](/pt-BR/reference/statements/hypothetical-index#create-hypothetical-index) e, em seguida, execute `EXPLAIN WHATIF SELECT ...` para ver, para cada candidato: aplicabilidade, marcas lidas estimadas, bytes estimados e taxa de descarte.

**Sintaxe**

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

**Configurações**

* `empirical` — `1` (padrão) executa o índice em memória sobre os grânulos filtrados pela referência para medir a taxa de descarte (um limite superior). `0` ignora esse caminho. De qualquer forma, se `empirical` não produzir um resultado (por estar desabilitado ou porque o índice não pode ser avaliado em memória), o estimador recorre às [estatísticas](/pt-BR/reference/engines/table-engines/mergetree-family/mergetree#column-statistics) da coluna e, por fim, a um resumo apenas de aplicabilidade se nenhuma das duas opções estiver disponível.

**Saída**

```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` — como a estimativa foi gerada.
  * `empirical`: construiu o índice em memória sobre os grânulos remanescentes após o pruning de referência e contou os grânulos que o índice pularia. Este é um limite superior — veja as limitações em [`CREATE HYPOTHETICAL INDEX`](/pt-BR/reference/statements/hypothetical-index#limitations).
  * `statistical`: derivado de estatísticas de coluna. Usado quando a estimativa empírica está desabilitada (`empirical = 0`) ou não conseguiu produzir um resultado, e há estatísticas de coluna definidas nas colunas relevantes.
  * `applicability_only`: o índice é aplicável ao predicado, mas nem a estimativa empírica nem a estatística produziram um resultado (por exemplo, `empirical = 0` e nenhuma estatística de coluna definida). Informa `skip_ratio: 0.0%` como um limite conservador.
* `sampled_parts` / `sampled_marks` — `<baseline-pruned> / <total in the table>`. Mostra que fração da tabela restou após o pruning por PK, partição e índices existentes, ou seja, a entrada para o índice hipotético.
* `est_bytes` — uma estimativa dos bytes lidos, derivada do tamanho médio de linha da tabela; por isso, é aproximada e varia conforme o armazenamento e a compressão. A linha de referência aparece apenas quando a consulta lê linhas; a linha de cada candidato, apenas quando a estimativa de bytes da linha de referência é conhecida.

A configuração é escrita inline entre `WHATIF` e `SELECT` — não há palavra-chave `SETTINGS` (isso corresponde à forma como outras variantes de `EXPLAIN` aceitam suas opções).

Se nenhum índice hipotético estiver definido para a tabela, `EXPLAIN WHATIF` informa `status: not_applicable` com uma dica para criar um.

**Linha combinada (múltiplos candidatos)**

Quando dois ou mais candidatos são avaliados empiricamente, `EXPLAIN WHATIF` acrescenta um bloco extra chamado `(combined: idx_a, idx_b, ...)` após as linhas de cada candidato. Ele informa o benefício conjunto de ter *todos* esses índices ao mesmo tempo: uma leitura real mantém um grânulo apenas se ele sobreviver a *todos* os skip indexes, portanto a estimativa combinada é a interseção dos grânulos sobreviventes dos candidatos. Seu `skip_ratio` é, portanto, pelo menos tão alto quanto o do melhor candidato individual — índices complementares fazem mais pruning em conjunto, enquanto índices redundantes o mantêm inalterado.

Só contribuem os candidatos com `source: empirical`, porque a linha combinada é formada pela interseção dos conjuntos de sobrevivência por grânulo de cada um. Os candidatos estimados como `statistical` ou `applicability_only` não têm dados por grânulo e são excluídos; consequentemente, o bloco combinado aparece somente quando pelo menos dois candidatos produziram uma estimativa empírica, e é omitido caso contrário (por exemplo, com `empirical = 0`). Seus campos de estimativa são os mesmos de um bloco empírico por candidato, exceto que `elapsed_us` é `0` — a estimativa combinada é derivada das varreduras por candidato, não de uma nova varredura. O nome sintético `(combined: ...)` é apenas um rótulo do relatório e não pode ser usado com `force_data_skipping_indices`.

**Exemplo empírico**

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

O `minmax` hipotético reduziria de 100 marcas para 1 — `skip_ratio: 99.0%`. (`est_bytes` é uma estimativa com base no tamanho médio da linha, portanto o valor exato varia.)

**Exemplo estatístico**

As [estatísticas](/pt-BR/reference/engines/table-engines/mergetree-family/mergetree#column-statistics) de coluna vêm desativadas por padrão. Para usar o caminho `statistical`, primeiro defina-as nas colunas relevantes e aguarde a conclusão da mutação de materialização:

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

Em seguida, desative o caminho empírico para que o estimador passe a usar as estatísticas de coluna:

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

O número vem da seletividade das estatísticas da coluna de `b < 10` (cerca de 10 linhas em 10000) e é informado como um limite superior de `skip_ratio`. Não há `sampled_parts` / `sampled_marks` — nenhum dado foi lido.

Se nenhum dos dois caminhos estiver disponível (por exemplo, `empirical = 0` e nenhuma estatística de coluna definida), o estimador informa `source: applicability_only` e um `skip_ratio: 0.0%` conservador.

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

Mostra o resultado de um override de tabela em um esquema acessado por meio de uma função de tabela.
Também faz algumas validações, gerando uma exceção se o override causar algum tipo de falha.

**Exemplo**

Suponha que você tenha uma tabela MySQL remota como esta:

```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>
  A validação não está completa, portanto uma consulta bem-sucedida não garante que o override não cause problemas.
</Note>
