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

> Documentation de référence pour EXPLAIN

# Instruction EXPLAIN

Affiche le plan d’exécution d’une instruction.

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

Syntaxe :

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

Exemple :

```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">
  ## Types d’EXPLAIN
</div>

* `AST` — Arbre syntaxique abstrait.
* `SYNTAX` — Texte de la requête après les optimisations au niveau de l’AST.
* `QUERY TREE` — Arbre de requête après les optimisations au niveau du Query Tree.
* `PLAN` — Plan d’exécution de la requête.
* `PIPELINE` — Pipeline d’exécution de la requête.
* `ANALYZE` — Exécute la requête et annote le plan d’exécution avec les runtime metrics mesurées.
* `ESTIMATE` — Nombre estimé de lignes, de marks et de parts à lire depuis les tables pendant le traitement de la requête.
* `TABLE OVERRIDE` — Résultat validé d’une surcharge de table sur le schéma d’une fonction de table.

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

Affiche l’AST de la requête. Prend en charge tous les types de requêtes, pas uniquement `SELECT`.

Paramètres :

* `graph` – Affiche l’AST sous forme de graphe, décrit dans le langage de description de graphes [DOT](https://en.wikipedia.org/wiki/DOT_\(graph_description_language\)). Par défaut : 0.

Exemples :

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

Affiche l'arbre syntaxique abstrait (AST) d'une requête après l'analyse syntaxique.

Cela consiste à analyser la requête, à construire l'AST et l'arbre de requête, à exécuter éventuellement l'analyseur de requêtes et les passes d'optimisation, puis à reconvertir l'arbre de requête en AST.

Paramètres :

* `oneline` – Affiche la requête sur une seule ligne. Valeur par défaut : `0`.
* `run_query_tree_passes` – Exécute les passes de l'arbre de requête avant d'en produire le dump. Valeur par défaut : `0`.
* `query_tree_passes` – Si `run_query_tree_passes` est défini, indique combien de passes exécuter. Si `query_tree_passes` n'est pas spécifié, toutes les passes sont exécutées.

Exemples :

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

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

Paramètres :

* `run_passes` — Exécute toutes les passes de l'arbre de requête avant d'en afficher le contenu. Valeur par défaut : `1`.
* `dump_passes` — Affiche des informations sur les passes utilisées avant d'afficher l'arbre de requête. Valeur par défaut : `0`.
* `passes` — Indique le nombre de passes à exécuter. S'il est défini sur `-1`, exécute toutes les passes. Valeur par défaut : `-1`.
* `dump_tree` — Affiche l'arbre de requête. Valeur par défaut : `1`.
* `dump_ast` — Affiche l'AST de la requête généré à partir de l'arbre de requête. Valeur par défaut : `0`.

Exemple :

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

Affiche les étapes du plan de requête.

Paramètres :

* `optimize` — Détermine si les optimisations du plan de requête sont appliquées avant l’affichage du plan. Par défaut : 1.
* `header` — Affiche l’en-tête de sortie pour l’étape. Par défaut : 0.
* `description` — Affiche la description de l’étape. Par défaut : 1.
* `indexes` — Affiche les index utilisés, le nombre de parts filtrées et le nombre de granules filtrés pour chaque index appliqué. Par défaut : 0. Pris en charge pour les tables [MergeTree](/docs/fr/reference/engines/table-engines/mergetree-family/mergetree). À partir de ClickHouse >= v25.9, cette instruction ne produit un résultat pertinent que lorsqu’elle est utilisée avec `SETTINGS use_query_condition_cache = 0, use_skip_indexes_on_data_read = 0`.
* `projections` — Affiche toutes les projections analysées et leur effet sur le filtrage au niveau des parts en fonction des conditions sur la clé primaire de la projection. Pour chaque projection, cette section inclut des statistiques comme le nombre de parts, de lignes, de marks et de plages évaluées à l’aide de la clé primaire de la projection. Elle indique également combien de parts de données ont été ignorées grâce à ce filtrage, sans lecture depuis la projection elle-même. Le champ `description` permet de déterminer si une projection a réellement été utilisée pour la lecture ou seulement analysée pour le filtrage. Par défaut : 0. Pris en charge pour les tables [MergeTree](/docs/fr/reference/engines/table-engines/mergetree-family/mergetree).
* `actions` — Affiche des informations détaillées sur les actions de l’étape. Par défaut : 1.
* `sorting` — Affiche la description du tri pour chaque étape du plan produisant une sortie triée. Par défaut : 0.
* `keep_logical_steps` — Conserve les étapes logiques du plan pour les jointures au lieu de les convertir en implémentations physiques de jointure. Par défaut : 0.
* `json` — Affiche les étapes du plan de requête sous la forme d’une ligne au format [JSON](/docs/fr/reference/formats/JSON/JSON). Par défaut : 0. Il est recommandé d’utiliser le format [TabSeparatedRaw (TSVRaw)](/docs/fr/reference/formats/TabSeparated/TabSeparatedRaw) pour éviter les séquences d’échappement inutiles.
* `input_headers` — Affiche les en-têtes d’entrée pour l’étape. Par défaut : 0. Utile principalement aux développeurs pour le Débogage des problèmes liés à une incompatibilité entre les en-têtes d’entrée et de sortie.
* `column_structure` — Affiche également la structure des colonnes dans les en-têtes, en plus de leur nom et de leur type. Par défaut : 0. Utile principalement aux développeurs pour le Débogage des problèmes liés à une incompatibilité entre les en-têtes d’entrée et de sortie.
* `distributed` — Affiche les plans de requête exécutés sur des nœuds distants pour les tables distribuées ou les répliques parallèles. N’est pas pris en charge avec `json`. Par défaut : 0.
* `compact` — Lorsqu’il est activé, masque dans le plan les étapes d’expression et les informations détaillées sur les actions (entrées, fonctions, alias et positions de sortie). N’a d’effet que lorsque `actions = 1`. Par défaut : 1.
* `pretty` — Affiche l’arborescence du plan à l’aide de caractères de dessin de lignes (├──, └──, │) au lieu de l’indentation, afin de visualiser la hiérarchie. Formate également les propriétés des étapes de jointure de manière intégrée. Par défaut : 1.

<Note>
  Par défaut, `explain_query_plan_default = 'pretty'`, donc `actions`, `compact` et `pretty` sont initialisés à `1` et le plan est affiché sous une forme compacte, soignée et annotée avec les actions. Le fait de spécifier explicitement l’une de ces options dans l’instruction `EXPLAIN` (par exemple, `EXPLAIN actions = 0, compact = 0, pretty = 0 SELECT ...`) remplace toujours la valeur par défaut.

  Avant ClickHouse 26.7, les valeurs par défaut de `actions`, `compact` et `pretty` étaient `0`. Vous pouvez toujours obtenir cette sortie en définissant `explain_query_plan_default = 'legacy'` (globalement ou dans `SETTINGS` par requête), ou en définissant `compatibility` sur n’importe quelle version antérieure à `26.7`.

  Les options `json` et `distributed` n’activent pas les valeurs par défaut de `pretty` (`actions`, `compact` et `pretty`), même lorsque `explain_query_plan_default = 'pretty'`. Pour inclure les détails des actions dans leur sortie, définissez manuellement `actions = 1`.
</Note>

Exemple :

```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>
  L’estimation du coût des étapes et de la requête n’est pas prise en charge.
</Note>

Lorsque `json = 1`, le plan de requête est représenté au format JSON. Chaque nœud est un dictionnaire qui possède toujours les clés `Node Type`, `Node Id` et `Plans`. `Node Type` est une chaîne contenant le nom de l’étape, et `Node Id` est un identifiant d’étape unique (le nom de l’étape avec un suffixe numérique, par ex. `Union_10`). `Plans` est un tableau contenant les descriptions des étapes enfants. D’autres clés facultatives peuvent être ajoutées selon le type de nœud et les paramètres.

Exemple :

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

Avec `description` = 1, la clé `Description` est ajoutée à l’étape :

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

Avec `header` = 1, la clé `Header` est ajoutée à l’étape sous forme d’un tableau de colonnes.

Exemple :

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

Avec `indexes` = 1, la clé `Indexes` est ajoutée. Elle contient un tableau des index utilisés. Chaque index est décrit en JSON avec la clé `Type` (une chaîne `Partition Min-Max`, `Partition`, `Statistics`, `PrimaryKey` ou `Skip`) et, éventuellement, les clés suivantes :

* `Name` — Le nom de l’index (actuellement utilisé uniquement pour les index `Skip`).
* `Keys` — Le tableau des colonnes utilisées par l’index.
* `Condition` — La condition utilisée.
* `Description` — La description de l’index (actuellement utilisée uniquement pour les index `Skip`).
* `Parts` — Le nombre de parts après/avant l’application de l’index.
* `Granules` — Le nombre de granules après/avant l’application de l’index.
* `Ranges` — Le nombre de plages de granules après l’application de l’index.

Exemple :

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

Avec `projections` = 1, la clé `Projections` est ajoutée. Elle contient un tableau de projections analysées. Chaque projection est décrite en JSON avec les clés suivantes :

* `Name` — Le nom de la projection.
* `Condition` — La condition de clé primaire utilisée pour la projection.
* `Description` — La description de l’utilisation de la projection (par ex. filtrage au niveau des parties).
* `Selected Parts` — Nombre de parties sélectionnées par la projection.
* `Selected Marks` — Nombre de marques sélectionnées.
* `Selected Ranges` — Nombre de plages sélectionnées.
* `Selected Rows` — Nombre de lignes sélectionnées.
* `Filtered Parts` — Nombre de parties ignorées en raison du filtrage au niveau des parties.

Exemple :

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

Avec `actions` = 1, les clés ajoutées dépendent du type d'étape.

Exemple :

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

Avec `compact = 0` et `actions = 1`, les étapes `Expression` sont visibles, accompagnées d'informations détaillées sur les expressions :

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

Avec `distributed` = 1, la sortie inclut non seulement le plan de requête local, mais également les plans de requête qui seront exécutés sur les nœuds distants. Cela s'avère utile pour analyser et déboguer les requêtes distribuées.

<Note>
  `distributed` est affiché uniquement sous la forme `legacy` (non-`pretty`), car la sortie `pretty` n’intègre pas les plans des segments distants dans l’arbre du plan. Pour cette raison, l’activation de `distributed` désactive automatiquement les options par défaut de `pretty` (`actions`, `compact` et `pretty`), quelle que soit la valeur de `explain_query_plan_default`. Vous pouvez toujours définir `actions=1` manuellement. L’option `distributed` n’est pas non plus prise en charge avec `json`.
</Note>

Exemple avec une table distribuée :

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

Exemple avec des répliques parallèles :

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

Dans les deux exemples, le plan de requête affiche le flux d'exécution complet, y compris les étapes locales et distantes.

Avec `pretty` = 1, l'arbre du plan est affiché à l'aide de caractères de tracé plutôt que par indentation, et des informations supplémentaires sont affichées pour les étapes clés :

* Les **colonnes de sortie de la requête** sont affichées en haut du plan.
* Les **expressions** dans les filtres, les clés d’agrégation, les descriptions de tri et les fonctions de fenêtre sont affichées dans une notation SQL lisible par l’humain (par ex. `a + 1 > 5` au lieu de `greater(plus(a, 1), 5)`). Les préfixes internes des identifiants de colonnes (tels que `__table1.`) sont supprimés pour plus de clarté.
* Les **étapes source** (telles que `ReadFromMergeTree`) affichent leurs colonnes de sortie.
* Les **étapes de filtre** affichent la condition de filtre en notation SQL. Lorsque des filtres de jointure à l’exécution sont présents, ils sont affichés séparément.
* Les **étapes d’agrégation** affichent les clés et les fonctions d’agrégation avec leurs arguments (par ex. `sum(c)`, `count()`).
* Les **ensembles IN** issus de littéraux `Tuple` affichent leurs valeurs (tronquées pour les grands ensembles), les ensembles basés sur des sous-requêtes sont libellés `subquery1`, `subquery2`, etc., et les ensembles provenant de tables utilisant le moteur `Set` affichent le nom de la table.
* Les **étapes de jointure** affichent la relation de jointure à l’aide d’une notation mathématique, le nombre estimé de lignes du résultat,
  et quelles colonnes de sortie proviennent du côté gauche ou du côté droit. Les symboles suivants sont utilisés pour
  représenter les différents types de JOIN :

| Symbole                | Type de jointure     |
| ---------------------- | -------------------- |
| `⋈`                    | Jointure interne     |
| `⟕`                    | Jointure gauche      |
| `⟖`                    | Jointure droite      |
| `⟗`                    | Jointure complète    |
| `⋉`                    | Semi-jointure gauche |
| `⋊`                    | Semi-jointure droite |
| `⋉` with strikethrough | Anti-jointure gauche |
| `⋊` with strikethrough | Anti-jointure droite |
| `×`                    | Jointure croisée     |

Par exemple, `t1 ⟕ t2` signifie une jointure gauche entre les tables `t1` et `t2`.
Le nombre entre crochets après le nom de la table (par ex. `t1[100]`) indique le nombre estimé de lignes
lorsque les statistiques de table sont disponibles.

L’option `pretty` fonctionne bien avec `compact = 1`, qui masque les étapes `Expression` et les informations détaillées sur les actions, ce qui rend le plan plus lisible.

Un exemple détaillé avec des jointures :

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

Paramètres :

* `header` — Affiche l’en-tête de chaque port de sortie. Valeur par défaut : 0.
* `graph` — Affiche un graphe décrit dans le langage de description de graphes [DOT](https://en.wikipedia.org/wiki/DOT_\(graph_description_language\)). Valeur par défaut : 0.
* `compact` — Affiche le graphe en mode compact si le paramètre `graph` est activé. Valeur par défaut : 1.
* `compact_repeated_processor_chains` — Regroupe les chaînes de processeurs répétées et adjacentes dans la sortie texte en n’affichant qu’une seule occurrence de la chaîne avec un nombre de répétitions. Cela peut faciliter la lecture des pipelines parallèles lorsque la même chaîne apparaît de nombreuses fois, par exemple dans des jointures. Cela n’affecte pas la sortie du graphe. Valeur par défaut : 0.

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

Lorsque `compact=0` et `graph=1`, les noms des processeurs contiendront un suffixe supplémentaire indiquant un identifiant de processeur unique.

Exemple :

```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` exécute effectivement la requête, ignore les lignes de résultat et affiche le même arbre de plan que `EXPLAIN PLAN`, avec pour chaque étape des annotations indiquant ce qui s’est réellement produit à l’exécution.

Paramètres :

`EXPLAIN ANALYZE` accepte les mêmes options d’affichage que `EXPLAIN PLAN` (documentées dans la section [EXPLAIN PLAN](#explain-plan)).

* `header` — voir la section [EXPLAIN PLAN](#explain-plan).
* `description` — voir la section [EXPLAIN PLAN](#explain-plan).
* `projections` — voir la section [EXPLAIN PLAN](#explain-plan).
* `sorting` — voir la section [EXPLAIN PLAN](#explain-plan).
* `input_headers` — voir la section [EXPLAIN PLAN](#explain-plan).
* `column_structure` — voir la section [EXPLAIN PLAN](#explain-plan).
* `actions` — voir la section [EXPLAIN PLAN](#explain-plan). Par défaut : 1.
* `indexes` — voir la section [EXPLAIN PLAN](#explain-plan). Par défaut : 1.
* `compact` — voir la section [EXPLAIN PLAN](#explain-plan). Par défaut : 1.
* `pretty` — voir la section [EXPLAIN PLAN](#explain-plan). Par défaut : 1.
* `processors` — Pour `EXPLAIN ANALYZE`, affiche une ligne supplémentaire par étape avec la répartition du temps écoulé par processeur : `min`, `median`, `max` et `sum`. Utile pour repérer un déséquilibre de charge entre les processeurs parallèles. Par défaut : 0.

<Note>
  Comme `EXPLAIN ANALYZE` exécute réellement la requête encapsulée, il se comporte comme cette
  requête — et contrairement aux formes `EXPLAIN` qui n’exécutent rien — à plusieurs égards :

  * **Quotas et limites.** Il est comptabilisé dans les mêmes [quotas](/docs/fr/concepts/features/configuration/server-config/quotas)
    et soumis aux mêmes [limites](/docs/fr/concepts/features/configuration/settings/query-complexity)
    (par ex. `query_selects`, `read_rows`) que si la requête était exécutée directement. Les sources exemptées de quotas pendant la planification
    (comme `system.one`) ne sont pas comptabilisées.
  * **Transactions en échec.** À l’intérieur d’une [transaction](/docs/fr/concepts/features/operations/insert/transactions)
    ayant déjà échoué (`ROLLED_BACK`), il est rejeté avec `INVALID_TRANSACTION`,
    tout comme un simple `SELECT` — exécutez d’abord `ROLLBACK`.
  * **Lectures en streaming.** Sur une lecture en streaming (`FROM ... STREAM`), il est rejeté avec
    `NOT_IMPLEMENTED`, car une telle lecture ne se termine jamais.
  * **Requêtes distribuées.** Il n’est pas pris en charge pour les requêtes exécutées en
    mode [distributed](/docs/fr/reference/engines/table-engines/special/distributed).
</Note>

Exemple :

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

Examinons la sortie. Commençons par l’en-tête.

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

* `Time` — temps total, réparti entre les phases de planification (c.-à-d. création du plan + optimisation du plan + construction du pipeline) et d’exécution (exécution du pipeline).
* `Read` — lignes et octets non compressés lus dans les tables, avec leur débit — les mêmes valeurs que celles indiquées dans le pied de page d’une requête classique sous la forme "Processed".
* `Peak memory` — mémoire maximale utilisée par la requête.

Examinons maintenant les nouvelles lignes qui apparaissent dans le plan de requête.

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

Le nombre de lignes et d’octets est indiqué une seule fois pour l’ensemble de l’étape (la ligne `I/O`). Le temps et le parallélisme sont indiqués pour chaque étape de l’étape sur la ou les lignes en retrait qui suivent.

* `rows <in> → <out>` — lignes entrées dans l’étape et qui en sont sorties ; (`<selectivity>`%) indique dans quelle mesure l’étape a filtré (`out/in`) ou étendu les données. Cette information est masquée lorsque le nombre de lignes en entrée est égal au nombre de lignes en sortie, ainsi que lorsque le nombre de lignes en entrée est égal à `0`.
* `<bytes_in> → <bytes_out>` — octets en mémoire non compressés traversant l’étape (omis lorsque les deux valeurs sont nulles).
* `time <t> (<share>%)` — temps d’horloge pendant lequel l’étape a été active, ainsi que sa part du temps d’exécution de la requête (c.-à-d. hors temps de build). Notez que les parts peuvent totaliser plus de 100 % car les étapes et les étapes s’exécutent en parallèle.
* `parallelism <avg>/<max>` — nombre moyen de threads CPU travaillant simultanément dans cette étape, par rapport au maximum qu’elle pouvait utiliser. Une valeur proche du maximum signifie que l’étape a été bien parallélisée ; une valeur proche de 1 signifie qu’elle s’est exécutée principalement en série.
* `Stage (<stage>)` — le nom de l’étape. Une étape avec une seule étape affiche directement la ligne de temps, sans libellé `Stage (...)`. Les étapes comportant plusieurs étapes affichent une ligne libellée par étape ; par exemple, `Aggregating` affiche `Stage (partial aggregation)` et `Stage (final aggregation)`, et un hash join affiche `Stage (build)` et `Stage (probe)`.

<Note>
  ClickHouse parallélise non seulement l’exécution des tâches au sein d’une étape du plan, mais aussi l’exécution des étapes du plan. La métrique `parallelism` ne reflète que le travail de cette étape. D’autres étapes peuvent s’exécuter en parallèle ; ce nombre n’indique donc pas comment le parallélisme de l’étape se compare à celui de l’ensemble de la requête.
</Note>

<Note>
  Le nombre maximal dans `parallelism` est calculé comme le minimum entre :

  1. le nombre total de tâches dans l’étape du plan ;
  2. le nombre maximal de threads de traitement des requêtes défini dans `max_threads`.
</Note>

Avec `processors = 1`, une ligne supplémentaire est affichée sous chaque phase, montrant la répartition du temps écoulé entre les processeurs de la phase :

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

`<n>` correspond au nombre de processeurs dans l’étape. Un écart important entre `median` et `max` indique un déséquilibre de charge entre les processeurs parallèles.

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

Affiche le nombre estimé de lignes, de marks et de parts à lire dans les tables lors du traitement de la requête. Fonctionne avec les tables de la famille [MergeTree](/docs/fr/reference/engines/table-engines/mergetree-family/mergetree).

**Exemple**

Création d’une table :

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

Estime le bénéfice qu’un index de saut hypothétique apporterait à une requête `SELECT`, *sans* matérialiser l’index sur le disque. Définissez un ou plusieurs candidats avec [`CREATE HYPOTHETICAL INDEX`](/docs/fr/reference/statements/hypothetical-index#create-hypothetical-index), puis exécutez `EXPLAIN WHATIF SELECT ...` pour voir, pour chaque candidat : son applicabilité, le nombre estimé de marks lus, le volume estimé en octets et le taux d’exclusion.

**Syntaxe**

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

**Paramètres**

* `empirical` — `1` (par défaut) exécute l’index en mémoire sur les granules élaguées par rapport à la référence afin de mesurer le taux d’exclusion (une borne supérieure). `0` ignore cette voie. Dans tous les cas, si `empirical` ne produit pas de résultat (désactivé, ou si l’index ne peut pas être évalué en mémoire), l’estimateur se rabat sur les [statistiques de colonne](/docs/fr/reference/engines/table-engines/mergetree-family/mergetree#column-statistics), puis, en dernier recours, sur un résumé limité à l’applicabilité si aucune des deux options n’est disponible.

**Sortie**

```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` — indique comment l’estimation a été obtenue.
  * `empirical` : l’index a été construit en mémoire sur les granules conservés après l’élagage de référence, puis les granules que l’index permettrait d’ignorer ont été comptés. Il s’agit d’une borne supérieure — voir les limites dans [`CREATE HYPOTHETICAL INDEX`](/docs/fr/reference/statements/hypothetical-index#limitations).
  * `statistical` : dérivée des statistiques de colonnes. Utilisée lorsque l’estimation empirique est désactivée (`empirical = 0`) ou n’a pas pu produire de résultat, et que des statistiques de colonnes sont définies sur les colonnes concernées.
  * `applicability_only` : l’index s’applique au prédicat, mais ni l’estimation empirique ni l’estimation statistique n’ont produit de résultat (par ex. `empirical = 0` et aucune statistique de colonnes définie). Indique `skip_ratio: 0.0%` comme borne prudente.
* `sampled_parts` / `sampled_marks` — `<baseline-pruned> / <total in the table>`. Indique quelle fraction de la table reste après l’élagage par la PK, la partition et les index existants, c.-à-d. l’entrée de l’index hypothétique.
* `est_bytes` — estimation du nombre d’octets lus, dérivée de la taille moyenne des lignes de la table ; elle est donc approximative et varie selon le stockage et la compression. La ligne de référence n’apparaît que lorsque la requête lit des lignes ; la ligne par candidat n’apparaît que lorsque l’estimation de référence des octets est connue.

Le paramètre est écrit en intégré entre `WHATIF` et `SELECT` — il n’y a pas de mot-clé `SETTINGS` (comme pour les autres variantes de `EXPLAIN`, qui acceptent leurs options de cette manière).

Si aucun index hypothétique n’est défini pour la table, `EXPLAIN WHATIF` indique `status: not_applicable` avec une suggestion d’en créer un.

**Ligne combinée (plusieurs candidats)**

Lorsque deux candidats ou plus sont évalués empiriquement, `EXPLAIN WHATIF` ajoute un bloc supplémentaire nommé `(combined: idx_a, idx_b, ...)` après les lignes par candidat. Il indique le bénéfice combiné de la présence simultanée de *tous* ces index : lors d’une lecture réelle, un granule n’est conservé que s’il survit à *chaque* skip index, donc l’estimation combinée correspond à l’intersection des granules survivants des candidats. Son `skip_ratio` est donc au moins aussi élevé que celui du meilleur candidat pris isolément — des index complémentaires élaguent davantage ensemble, tandis que des index redondants le laissent inchangé.

Seuls les candidats avec `source: empirical` contribuent, car la ligne combinée est construite en intersectant leurs ensembles de survie par granule. Les candidats estimés comme `statistical` ou `applicability_only` n’ont pas de données par granule et sont exclus ; par conséquent, le bloc combiné n’apparaît que lorsqu’au moins deux candidats ont produit une estimation empirique, et il est omis dans le cas contraire (par exemple avec `empirical = 0`). Les champs de son estimation sont les mêmes que pour un bloc empirique par candidat, à ceci près que `elapsed_us` vaut `0` — l’estimation combinée est dérivée des analyses par candidat, et non d’une nouvelle analyse. Le nom synthétique `(combined: ...)` n’est qu’une étiquette de rapport et ne peut pas être utilisé avec `force_data_skipping_indices`.

**Exemple empirique**

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

Le `minmax` hypothétique réduirait 100 marks à 1 — `skip_ratio: 99.0%`. (`est_bytes` est une estimation basée sur la taille moyenne des lignes, donc la valeur exacte peut varier.)

**Exemple statistique**

Les [statistiques de colonne](/docs/fr/reference/engines/table-engines/mergetree-family/mergetree#column-statistics) sont désactivées par défaut. Pour utiliser le chemin `statistical`, définissez-les d’abord sur les colonnes concernées, puis attendez que la mutation de matérialisation se termine :

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

Désactivez ensuite le chemin empirique afin que l’estimateur revienne aux statistiques de colonne :

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

Le nombre provient de la sélectivité des statistiques de colonne pour `b < 10` (environ 10 lignes sur 10 000) et est indiqué comme borne supérieure de `skip_ratio`. Il n’y a ni `sampled_parts` ni `sampled_marks` — aucune donnée n’a été lue.

Si aucun des deux mécanismes n’est disponible (par ex. `empirical = 0` et aucune statistique de colonne définie), l’estimateur indique `source: applicability_only` ainsi qu’un `skip_ratio: 0.0%` conservateur.

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

Affiche le résultat d'une surcharge de table appliquée au schéma d'une table accessible via une fonction de table.
Effectue également certaines vérifications et lève une exception si la surcharge aurait entraîné un échec.

**Exemple**

Supposons que vous ayez une table MySQL distante comme celle-ci :

```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 validation n'est pas exhaustive ; une requête réussie ne garantit donc pas que l'override ne posera pas de problèmes.
</Note>
