> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Documentación de EXPLAIN

# Sentencia EXPLAIN

Muestra el plan de ejecución de una sentencia.

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

Sintaxis:

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

Ejemplo:

```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` — Árbol de sintaxis abstracta.
* `SYNTAX` — Texto de la consulta tras las optimizaciones a nivel de AST.
* `QUERY TREE` — Árbol de consulta tras las optimizaciones a nivel de Query Tree.
* `PLAN` — Plan de ejecución de la consulta.
* `PIPELINE` — Canalización de ejecución de la consulta.
* `ANALYZE` — Ejecuta la consulta y anota el plan de ejecución con métricas de runtime medidas.
* `ESTIMATE` — Número estimado de filas, marcas y partes que se leerán de las tablas al procesar la consulta.
* `TABLE OVERRIDE` — Resultado validado de una sobrescritura de tabla sobre el esquema de una función de tabla.

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

Muestra el AST de la consulta. Admite todo tipo de consultas, no solo `SELECT`.

Configuración:

* `graph` – Imprime el AST como un grafo descrito en el lenguaje de descripción de grafos [DOT](https://en.wikipedia.org/wiki/DOT_\(graph_description_language\)). Valor predeterminado: 0.

Ejemplos:

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

Muestra el árbol de sintaxis abstracta (AST) de una consulta tras el análisis sintáctico.

Esto se hace analizando la consulta, construyendo el AST y el árbol de consulta, ejecutando opcionalmente el analizador de consultas y las pasadas de optimización, y convirtiendo después el árbol de consulta de nuevo en el AST de la consulta.

Configuración:

* `oneline` – Imprime la consulta en una sola línea. Valor predeterminado: `0`.
* `run_query_tree_passes` – Ejecuta las pasadas del árbol de consulta antes de volcar el árbol de consulta. Valor predeterminado: `0`.
* `query_tree_passes` – Si se establece `run_query_tree_passes`, especifica cuántas pasadas se ejecutan. Si no se especifica `query_tree_passes`, se ejecutan todas las pasadas.

Ejemplos:

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

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

Configuración:

* `run_passes` — Ejecuta todas las pasadas del árbol de consulta antes de volcarlo. Valor predeterminado: `1`.
* `dump_passes` — Vuelca información sobre las pasadas utilizadas antes de volcar el árbol de consulta. Valor predeterminado: `0`.
* `passes` — Especifica cuántas pasadas ejecutar. Si se establece en `-1`, ejecuta todas las pasadas. Valor predeterminado: `-1`.
* `dump_tree` — Muestra el árbol de consulta. Valor predeterminado: `1`.
* `dump_ast` — Muestra el AST de la consulta generado a partir del árbol de consulta. Valor predeterminado: `0`.

Ejemplo:

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

Muestra los pasos del plan de consulta.

Configuración:

* `optimize` — Controla si las optimizaciones del plan de consulta se aplican antes de mostrar el plan. Predeterminado: 1.
* `header` — Imprime el encabezado de salida del paso. Predeterminado: 0.
* `description` — Imprime la descripción del paso. Predeterminado: 1.
* `indexes` — Muestra los índices utilizados, el número de partes filtradas y el número de gránulos filtrados para cada índice aplicado. Predeterminado: 0. Compatible con tablas [MergeTree](/docs/es/reference/engines/table-engines/mergetree-family/mergetree). A partir de ClickHouse >= v25.9, esta sentencia solo muestra una salida adecuada cuando se usa con `SETTINGS use_query_condition_cache = 0, use_skip_indexes_on_data_read = 0`.
* `projections` — Muestra todas las proyecciones analizadas y su efecto en el filtrado a nivel de parte según las condiciones de la clave primaria de la proyección. Para cada proyección, esta sección incluye estadísticas como el número de partes, filas, marcas y rangos evaluados con la clave primaria de la proyección. También muestra cuántas partes de datos se omitieron debido a este filtrado, sin leer de la propia proyección. Si una proyección se usó realmente para la lectura o solo se analizó para el filtrado puede determinarse mediante el campo `description`. Predeterminado: 0. Compatible con tablas [MergeTree](/docs/es/reference/engines/table-engines/mergetree-family/mergetree).
* `actions` — Imprime información detallada sobre las acciones del paso. Predeterminado: 1.
* `sorting` — Imprime la descripción de ordenación de cada paso del plan que produce una salida ordenada. Predeterminado: 0.
* `keep_logical_steps` — Conserva los pasos lógicos del plan para joins en lugar de convertirlos en implementaciones físicas de join. Predeterminado: 0.
* `json` — Imprime los pasos del plan de consulta como una fila en formato [JSON](/docs/es/reference/formats/JSON/JSON). Predeterminado: 0. Se recomienda usar el formato [TabSeparatedRaw (TSVRaw)](/docs/es/reference/formats/TabSeparated/TabSeparatedRaw) para evitar escapes innecesarios.
* `input_headers` — Imprime los encabezados de entrada del paso. Predeterminado: 0. Principalmente útil solo para desarrolladores al depurar problemas relacionados con discrepancias entre los encabezados de entrada y salida.
* `column_structure` — Imprime también la estructura de las columnas en los encabezados, además de su nombre y tipo. Predeterminado: 0. Principalmente útil solo para desarrolladores al depurar problemas relacionados con discrepancias entre los encabezados de entrada y salida.
* `distributed` — Muestra los planes de consulta ejecutados en nodos remotos para tablas distribuidas o réplicas paralelas. No compatible con `json`. Predeterminado: 0.
* `compact` — Cuando está habilitado, oculta del plan los pasos de expresión y la información detallada de las acciones (entradas, funciones, alias y posiciones de salida). Solo tiene efecto cuando `actions = 1`. Predeterminado: 1.
* `pretty` — Imprime el árbol del plan usando caracteres de dibujo de líneas (├──, └──, │) en lugar de sangría para visualizar la jerarquía. También muestra en línea las propiedades del paso de join. Predeterminado: 1.

<Note>
  De forma predeterminada, `explain_query_plan_default = 'pretty'`, por lo que `actions`, `compact` y `pretty` se inicializan en `1` y el plan se representa en la forma compacta, visualmente estructurada y anotada con acciones. Especificar cualquiera de estas opciones explícitamente en la sentencia `EXPLAIN` (por ejemplo, `EXPLAIN actions = 0, compact = 0, pretty = 0 SELECT ...`) siempre sobrescribe el valor predeterminado.

  Antes de ClickHouse 26.7, los valores predeterminados de `actions`, `compact` y `pretty` eran `0`. Aún puede obtener esa salida configurando `explain_query_plan_default = 'legacy'` (globalmente o en `SETTINGS` por consulta), o configurando `compatibility` en cualquier versión anterior a `26.7`.

  Las opciones `json` y `distributed` no habilitan los valores predeterminados de `pretty` (`actions`, `compact` y `pretty`), incluso cuando `explain_query_plan_default = 'pretty'`. Para incluir detalles de las acciones en su salida, configure `actions = 1` manualmente.
</Note>

Ejemplo:

```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>
  No se admite la estimación del costo de los pasos ni de la consulta.
</Note>

Cuando `json = 1`, el plan de consulta se representa en formato JSON. Cada nodo es un diccionario que siempre tiene las claves `Node Type`, `Node Id` y `Plans`. `Node Type` es una cadena con el nombre del paso, y `Node Id` es un identificador único del paso (el nombre del paso con un sufijo numérico, p. ej. `Union_10`). `Plans` es un array con descripciones de pasos secundarios. Pueden añadirse otras claves opcionales según el tipo de nodo y la configuración.

Ejemplo:

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

Con `description` = 1, se añade la clave `Description` al paso:

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

Con `header` = 1, la clave `Header` se agrega al paso como un array de columnas.

Ejemplo:

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

Con `indexes` = 1, se añade la clave `Indexes`. Contiene un array de los índices usados. Cada índice se describe como JSON con la clave `Type` (una cadena `Partition Min-Max`, `Partition`, `Statistics`, `PrimaryKey` o `Skip`) y claves opcionales:

* `Name` — El nombre del índice (actualmente solo se usa para índices `Skip`).
* `Keys` — El array de columnas que usa el índice.
* `Condition` — La condición usada.
* `Description` — La descripción del índice (actualmente solo se usa para índices `Skip`).
* `Parts` — El número de partes después/antes de aplicar el índice.
* `Granules` — El número de gránulos después/antes de aplicar el índice.
* `Ranges` — El número de rangos de gránulos después de aplicar el índice.

Ejemplo:

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

Con `projections` = 1, se añade la clave `Projections`. Contiene un array de proyecciones analizadas. Cada proyección se describe como JSON con las siguientes claves:

* `Name` — El nombre de la proyección.
* `Condition` — La condición de la clave primaria usada por la proyección.
* `Description` — La descripción de cómo se usa la proyección (p. ej., filtrado a nivel de partes).
* `Selected Parts` — Número de partes seleccionadas por la proyección.
* `Selected Marks` — Número de marcas seleccionadas.
* `Selected Ranges` — Número de rangos seleccionados.
* `Selected Rows` — Número de filas seleccionadas.
* `Filtered Parts` — Número de partes omitidas debido al filtrado a nivel de partes.

Ejemplo:

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

Con `actions` = 1, las claves añadidas dependen del tipo de paso.

Ejemplo:

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

Con `compact = 0` y `actions = 1`, se pueden ver los pasos `Expression` junto con información detallada sobre las expresiones:

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

Con `distributed` = 1, la salida incluye no solo el plan de consulta local, sino también los planes de consulta que se ejecutarán en los nodos remotos. Esto resulta útil para analizar y depurar consultas distribuidas.

<Note>
  `distributed` se representa solo en la forma `legacy` (no `pretty`), porque la salida `pretty` no integra los planes de los segmentos remotos en el árbol del plan. Por este motivo, habilitar `distributed` desactiva automáticamente los valores predeterminados de `pretty` (`actions`, `compact` y `pretty`), independientemente de `explain_query_plan_default`. Aun así, puede establecer `actions=1` manualmente. La opción `distributed` tampoco se admite junto con `json`.
</Note>

Ejemplo con tabla distribuida:

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

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

En ambos ejemplos, el plan de consulta muestra el flujo de ejecución completo, incluidos los pasos locales y remotos.

Con `pretty` = 1, el árbol del plan se muestra con caracteres de dibujo de líneas en lugar de sangría, y se muestra información adicional para los pasos clave:

* Las **columnas de salida de la consulta** se imprimen en la parte superior del plan.
* Las **expresiones** en filtros, claves de agregación, descripciones de ordenación y funciones de ventana se muestran en una notación legible para humanos similar a SQL (por ejemplo, `a + 1 > 5` en lugar de `greater(plus(a, 1), 5)`). Los prefijos internos de los identificadores de columna (como `__table1.`) se eliminan para mayor claridad.
* Los **pasos de origen** (como `ReadFromMergeTree`) muestran sus columnas de salida.
* Los **pasos de filtro** muestran la condición de filtro en notación SQL. Cuando hay filtros de join en tiempo de ejecución, se muestran por separado.
* Los **pasos de agregación** muestran las claves y las funciones de agregación con sus argumentos (por ejemplo, `sum(c)`, `count()`).
* Los **conjuntos `IN`** de literales de tupla muestran sus valores (truncados para conjuntos grandes), los conjuntos basados en subconsultas se etiquetan como `subquery1`, `subquery2`, etc., y los conjuntos de tablas con motor `Set` muestran el nombre de la tabla.
* Los **pasos de join** muestran la relación de join mediante notación matemática, el número estimado de filas del resultado,
  y qué columnas de salida provienen del lado izquierdo frente al derecho. Se usan los siguientes símbolos para
  representar distintos tipos de JOIN:

| Símbolo     | Tipo de JOIN        |
| ----------- | ------------------- |
| `⋈`         | JOIN interno        |
| `⟕`         | JOIN izquierdo      |
| `⟖`         | JOIN derecho        |
| `⟗`         | JOIN completo       |
| `⋉`         | JOIN semi izquierdo |
| `⋊`         | JOIN semi derecho   |
| `⋉` tachado | JOIN anti izquierdo |
| `⋊` tachado | JOIN anti derecho   |
| `×`         | JOIN cruzado        |

Por ejemplo, `t1 ⟕ t2` significa un JOIN izquierdo entre las tablas `t1` y `t2`.
El número entre corchetes después del nombre de la tabla (p. ej., `t1[100]`) indica el número estimado de filas
cuando hay estadísticas de tabla disponibles.

La opción `pretty` funciona bien junto con `compact = 1`, que oculta los pasos `Expression` y la información detallada de las acciones, lo que hace que el plan sea más fácil de leer.

Un ejemplo detallado con JOIN:

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

Configuración:

* `header` — Muestra el encabezado de cada puerto de salida. Valor predeterminado: 0.
* `graph` — Muestra un grafo descrito en el lenguaje de descripción de grafos [DOT](https://en.wikipedia.org/wiki/DOT_\(graph_description_language\)). Valor predeterminado: 0.
* `compact` — Muestra el grafo en modo compacto si la configuración `graph` está habilitada. Valor predeterminado: 1.
* `compact_repeated_processor_chains` — Compacta las cadenas repetidas de procesadores adyacentes en la salida de texto mostrando una sola copia de la cadena junto con el número de repeticiones. Esto puede facilitar la lectura de las canalizaciones paralelas cuando la misma cadena aparece muchas veces, por ejemplo, en joins. No afecta a la salida del grafo. Valor predeterminado: 0.

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

Cuando `compact=0` y `graph=1`, los nombres de los procesadores contendrán un sufijo adicional con un identificador único de procesador.

Ejemplo:

```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` sí ejecuta la consulta, descarta las filas del resultado y muestra el mismo árbol del plan que `EXPLAIN PLAN`, con cada paso anotado con lo que realmente ocurrió en tiempo de ejecución.

Configuración:

`EXPLAIN ANALYZE` acepta las mismas opciones de visualización que `EXPLAIN PLAN` (documentadas en la sección [EXPLAIN PLAN](#explain-plan)).

* `header` — consulte la sección [EXPLAIN PLAN](#explain-plan).
* `description` — consulte la sección [EXPLAIN PLAN](#explain-plan).
* `projections` — consulte la sección [EXPLAIN PLAN](#explain-plan).
* `sorting` — consulte la sección [EXPLAIN PLAN](#explain-plan).
* `input_headers` — consulte la sección [EXPLAIN PLAN](#explain-plan).
* `column_structure` — consulte la sección [EXPLAIN PLAN](#explain-plan).
* `actions` — consulte la sección [EXPLAIN PLAN](#explain-plan). Valor predeterminado: 1.
* `indexes` — consulte la sección [EXPLAIN PLAN](#explain-plan). Valor predeterminado: 1.
* `compact` — consulte la sección [EXPLAIN PLAN](#explain-plan). Valor predeterminado: 1.
* `pretty` — consulte la sección [EXPLAIN PLAN](#explain-plan). Valor predeterminado: 1.
* `processors` — En `EXPLAIN ANALYZE`, muestra una línea adicional por etapa con la distribución del tiempo transcurrido por procesador: `min`, `median`, `max` y `sum`. Es útil para detectar desequilibrios de carga entre procesadores paralelos. Valor predeterminado: 0.

<Note>
  Como `EXPLAIN ANALYZE` realmente ejecuta la consulta encapsulada, se comporta como esa
  consulta — y, a diferencia de las formas `EXPLAIN` que no ejecutan — de varias maneras:

  * **Cuotas y límites.** Se contabiliza en las mismas [cuotas](/docs/es/concepts/features/configuration/server-config/quotas)
    y está sujeto a los mismos [límites](/docs/es/concepts/features/configuration/settings/query-complexity)
    (por ejemplo, `query_selects`, `read_rows`) que ejecutar la consulta directamente. Las fuentes exentas de cuotas durante la planificación
    (como `system.one`) no se contabilizan.
  * **Transacciones fallidas.** Dentro de una [transacción](/docs/es/concepts/features/operations/insert/transactions)
    que ya falló (`ROLLED_BACK`), se rechaza con `INVALID_TRANSACTION`,
    igual que un `SELECT` simple: primero ejecute `ROLLBACK`.
  * **Lecturas en streaming.** En una lectura en streaming (`FROM ... STREAM`), se rechaza con
    `NOT_IMPLEMENTED`, porque esa lectura nunca finaliza.
  * **Consultas distribuidas.** No es compatible con consultas ejecutadas en
    modo [distribuido](/docs/es/reference/engines/table-engines/special/distributed).
</Note>

Ejemplo:

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

Veamos la salida. Primero, veamos el encabezado.

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

* `Time` — tiempo total dividido en las fases de planificación (es decir, creación del plan + optimización del plan + construcción de la canalización) y ejecución (ejecución de la canalización).
* `Read` — filas y bytes sin comprimir leídos de las tablas, junto con el rendimiento: los mismos valores que el pie de la consulta normal muestra como "Processed".
* `Peak memory` — memoria máxima utilizada por la consulta.

Ahora veamos las nuevas líneas que aparecen en el plan de consulta.

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

Las filas y los bytes se informan una vez para todo el paso (la línea `I/O`). El tiempo y el paralelismo se informan por etapa del paso en las siguientes líneas con sangría.

* `rows <in> → <out>` — filas que entraron y salieron del paso; (`<selectivity>`%) muestra cuánto filtró (`out/in`) o expandió los datos el paso; se oculta cuando las filas de entrada son iguales a las de salida y cuando las filas de entrada son iguales a `0`.
* `<bytes_in> → <bytes_out>` — bytes en memoria sin comprimir que fluyen por el paso (se omite cuando ambos son cero).
* `time <t> (<share>%)` — tiempo de reloj durante el que la etapa estuvo activa y su proporción del tiempo de ejecución de la consulta (es decir, sin incluir el tiempo de build). Tenga en cuenta que las proporciones pueden sumar más del 100% porque las etapas y los pasos se ejecutan de forma concurrente.
* `parallelism <avg>/<max>` — número medio de hilos de CPU trabajando a la vez dentro de esta etapa, sobre el máximo que podía usar. Un valor cercano al máximo significa que la etapa estuvo bien paralelizada; un valor cercano a 1 significa que se ejecutó casi siempre en serie.
* `Stage (<stage>)` — el nombre de la etapa. Un paso con una sola etapa imprime la línea de tiempo directamente, sin una etiqueta `Stage (...)`. Los pasos con varias etapas imprimen una línea etiquetada por etapa; por ejemplo, `Aggregating` muestra `Stage (partial aggregation)` y `Stage (final aggregation)`, y un hash join muestra `Stage (build)` y `Stage (probe)`.

<Note>
  ClickHouse paraleliza no solo la ejecución de tareas dentro de un paso del plan, sino también la ejecución de los propios pasos del plan. La métrica `parallelism` refleja solo el trabajo de este paso. Otros pasos pueden ejecutarse de forma concurrente, por lo que este valor no muestra cómo se compara el paralelismo del paso con el de toda la consulta.
</Note>

<Note>
  El valor máximo en `parallelism` se calcula como el mínimo entre:

  1. el número total de tareas dentro del paso del plan;
  2. el número máximo de hilos de procesamiento de consultas configurado en `max_threads`.
</Note>

Con `processors = 1`, se imprime una línea adicional debajo de cada etapa que muestra la distribución del tiempo transcurrido entre los procesadores de la etapa:

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

`<n>` es el número de procesadores de la etapa. Una gran diferencia entre `median` y `max` apunta a un desequilibrio de carga entre los procesadores paralelos.

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

Muestra el número estimado de filas, marcas y partes que se leerán de las tablas al procesar la consulta. Funciona con tablas de la familia [MergeTree](/docs/es/reference/engines/table-engines/mergetree-family/mergetree).

**Ejemplo**

Crear una tabla:

```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 el beneficio que tendría un índice de omisión hipotético en una consulta `SELECT`, *sin* materializar el índice en disco. Defina uno o más candidatos con [`CREATE HYPOTHETICAL INDEX`](/docs/es/reference/statements/hypothetical-index#create-hypothetical-index) y luego ejecute `EXPLAIN WHATIF SELECT ...` para ver, para cada candidato, si aplica, las marcas estimadas leídas, los bytes estimados y la tasa de omisión.

**Sintaxis**

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

**Configuración**

* `empirical` — `1` (predeterminado) ejecuta el índice en memoria sobre los gránulos descartados según la línea base para medir la tasa de descarte (un límite superior). `0` omite ese paso. En cualquier caso, si `empirical` no produce un resultado (porque está deshabilitado o porque el índice no puede evaluarse en memoria), el estimador recurre a las [estadísticas de columna](/docs/es/reference/engines/table-engines/mergetree-family/mergetree#column-statistics) y, por último, a un resumen únicamente de aplicabilidad si ninguna de las dos está disponible.

**Salida**

```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` — cómo se generó la estimación.
  * `empirical`: construyó el índice en memoria sobre los gránulos podados por la línea base y contó los gránulos que el índice omitiría. Este es un límite superior; consulta las limitaciones en [`CREATE HYPOTHETICAL INDEX`](/docs/es/reference/statements/hypothetical-index#limitations).
  * `statistical`: se deriva de las estadísticas de columnas. Se usa cuando `empirical` está deshabilitado (`empirical = 0`) o cuando `empirical` no pudo producir un resultado, y hay estadísticas de columnas definidas en las columnas relevantes.
  * `applicability_only`: el índice es aplicable al predicado, pero ni la estimación `empirical` ni la `statistical` produjeron un resultado (p. ej., `empirical = 0` y no hay estadísticas de columnas definidas). Informa `skip_ratio: 0.0%` como límite conservador.
* `sampled_parts` / `sampled_marks` — `<baseline-pruned> / <total in the table>`. Muestra qué fracción de la tabla quedó tras la poda por PK, partición e índices existentes; es decir, la entrada del índice hipotético.
* `est_bytes` — una estimación de los bytes leídos, derivada del tamaño medio de fila de la tabla, por lo que es aproximada y varía según el almacenamiento y la compresión. La línea de base aparece solo cuando la consulta lee filas; la línea por candidato, solo cuando se conoce la estimación de bytes de la línea de base.

La configuración se escribe en línea entre `WHATIF` y `SELECT`; no hay palabra clave `SETTINGS` (esto coincide con cómo otras variantes de `EXPLAIN` aceptan sus opciones).

Si no hay índices hipotéticos definidos para la tabla, `EXPLAIN WHATIF` informa `status: not_applicable` con una sugerencia para crear uno.

**Fila combinada (múltiples candidatos)**

Cuando dos o más candidatos se evalúan empíricamente, `EXPLAIN WHATIF` agrega un bloque adicional llamado `(combined: idx_a, idx_b, ...)` después de las filas de cada candidato. Informa del beneficio conjunto de tener *todos* esos índices a la vez: una lectura real conserva un gránulo solo si sobrevive a *todos* los índices de omisión, por lo que la estimación combinada es la intersección de los gránulos supervivientes de los candidatos. Por tanto, su `skip_ratio` es al menos tan alta como la del mejor candidato individual: los índices complementarios podan más juntos, mientras que los redundantes la dejan sin cambios.

Solo contribuyen los candidatos con `source: empirical`, porque la fila combinada se construye al intersectar sus conjuntos de supervivencia por gránulo. Los candidatos estimados como `statistical` o `applicability_only` no tienen datos por gránulo y se excluyen; en consecuencia, el bloque combinado aparece solo cuando al menos dos candidatos produjeron una estimación empírica y, en caso contrario, se omite (por ejemplo, con `empirical = 0`). Sus campos de estimación se leen igual que los de un bloque empírico por candidato, excepto que `elapsed_us` es `0` — la estimación combinada se deriva de los escaneos por candidato, no de un escaneo nuevo. El nombre sintético `(combined: ...)` es solo una etiqueta del informe y no puede usarse con `force_data_skipping_indices`.

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

El `minmax` hipotético podaría de 100 marcas a 1 — `skip_ratio: 99.0%`. (`est_bytes` es una estimación basada en el tamaño promedio de la fila, por lo que la cifra exacta puede variar.)

**Ejemplo estadístico**

Las [estadísticas de columna](/docs/es/reference/engines/table-engines/mergetree-family/mergetree#column-statistics) están desactivadas de forma predeterminada. Para usar la ruta `statistical`, defínalas primero en las columnas correspondientes y espere a que finalice la mutación de materialización:

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

Luego, deshabilite el método empírico para que el estimador vuelva a usar las estadísticas de columna:

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

El número proviene de la selectividad de las estadísticas de columna para `b < 10` (aproximadamente 10 filas de 10000) y se muestra como un límite superior de `skip_ratio`. No hay `sampled_parts` / `sampled_marks`: no se leyó ningún dato.

Si ninguna de las dos vías está disponible (p. ej., `empirical = 0` y no hay estadísticas de columna definidas), el estimador informa `source: applicability_only` y un `skip_ratio: 0.0%` conservador.

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

Muestra el resultado de una sobrescritura de tabla aplicada al esquema de una tabla a la que se accede mediante una función de tabla.
También realiza ciertas validaciones y genera una excepción si la sobrescritura hubiera provocado algún tipo de fallo.

**Ejemplo**

Suponga que tiene una tabla 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>
  La validación aún no es exhaustiva, por lo que una consulta correcta no garantiza que la sobrescritura no vaya a causar problemas.
</Note>
