EXPLAIN – Analyser les requêtes
Bonne lecture et bon apprentissage !
Junior TSAFACK – 08/08/2026
⏱️ Temps de lecture estimé : 7 minutes
Dans le langage SQL, l’instruction EXPLAIN est à utiliser juste avant un SELECT et permet d’afficher le plan d’exécution d’une requête SQL. Cela permet de savoir de quelle manière le Système de Gestion de Base de Données (SGBD) va exécuter la requête, s’il va utiliser des index et lesquels.
Bon à savoir : En utilisant cette commande, la requête ne renverra pas les résultats du
SELECTmais plutôt une analyse de cette requête. C’est un outil indispensable pour l’optimisation des performances.
Syntaxe
Section titled “Syntaxe”La syntaxe ci-dessous représente une requête SQL utilisant la commande EXPLAIN pour MySQL ou PostgreSQL :
EXPLAIN SELECT * FROM utilisateur ORDER BY id DESC;Rappel : Dans cet exemple, la requête retournera des informations sur le plan d’exécution, mais n’affichera pas les « vrais » résultats de la requête.
Compatibilité entre SGBD
Section titled “Compatibilité entre SGBD”Le nom de cette instruction diffère selon les SGBD :
| SGBD | Commande |
|---|---|
| MySQL | EXPLAIN |
| MariaDB | EXPLAIN |
| PostgreSQL | EXPLAIN (et EXPLAIN ANALYZE pour l’exécution réelle) |
| Oracle | EXPLAIN PLAN |
| SQLite | EXPLAIN QUERY PLAN |
| SQL Server | SET SHOWPLAN_ALL ON; ou SET STATISTICS PROFILE ON; |
Bon à savoir : Le résultat de cette instruction est différent selon les SGBD. Il est important de consulter la documentation de votre SGBD pour interpréter correctement les résultats.
Exemple concret
Section titled “Exemple concret”Pour expliquer concrètement le fonctionnement de l’instruction EXPLAIN, nous allons prendre une table des fuseaux horaires.
Création de la table « timezones » (MySQL) :
CREATE TABLE IF NOT EXISTS `timezones` ( `timezone_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `timezone_groupe_fr` varchar(50) DEFAULT NULL, `timezone_groupe_en` varchar(50) DEFAULT NULL, `timezone_detail` varchar(100) DEFAULT NULL, PRIMARY KEY (`timezone_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=698;Cette table contient plusieurs centaines d’enregistrements (les fuseaux horaires du monde).
Analyse d’une requête sans index
Section titled “Analyse d’une requête sans index”Imaginons que l’on souhaite compter le nombre de fuseaux horaires par groupe, en français. Pour cela, nous pouvons utiliser la requête SQL suivante :
SELECT timezone_groupe_fr, COUNT(timezone_detail) AS total_timezoneFROM timezonesGROUP BY timezone_groupe_frORDER BY timezone_groupe_fr ASC;Utilisation de EXPLAIN pour analyser la requête
Section titled “Utilisation de EXPLAIN pour analyser la requête”Nous allons voir dans notre exemple comment MySQL va exécuter cette requête. Pour cela, il faut utiliser l’instruction EXPLAIN :
EXPLAIN SELECT timezone_groupe_fr, COUNT(timezone_detail) AS total_timezoneFROM timezonesGROUP BY timezone_groupe_frORDER BY timezone_groupe_fr ASC;Résultat (MySQL) :
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | timezones | ALL | NULL | NULL | NULL | NULL | 421 | Using temporary; Using filesort |
Interprétation des colonnes
Section titled “Interprétation des colonnes”| Colonne | Valeur | Explication |
|---|---|---|
id |
1 | Identifiant du SELECT (un seul ici) |
select_type |
SIMPLE | Type de SELECT (simple, sans sous-requête ni union) |
table |
timezones | Table concernée |
type |
ALL | Type d’accès : parcours complet de la table (le moins performant) |
possible_keys |
NULL | Index potentiellement utilisables (aucun) |
key |
NULL | Index effectivement utilisé (aucun) |
key_len |
NULL | Longueur de la clé utilisée |
ref |
NULL | Colonnes ou constantes utilisées pour la jointure |
rows |
421 | Estimation du nombre de lignes analysées |
Extra |
Using temporary; Using filesort | Utilisation d’une table temporaire et d’un tri externe |
Interprétation :
type = ALL: MySQL parcourt toute la table (scan complet). C’est le moins efficace.possible_keys = NULL: aucun index n’est disponible pour cette colonne.Extra = Using temporary; Using filesort: MySQL doit créer une table temporaire pour leGROUP BYet faire un tri (ORDER BY). C’est coûteux en ressources.
Conclusion : Cette requête est très lourde sur une table de 421 lignes. Sur une table de plusieurs millions de lignes, elle serait rédhibitoire.
Ajout d’un index pour optimiser la requête
Section titled “Ajout d’un index pour optimiser la requête”Il est possible d’ajouter un index sur la colonne timezone_groupe_fr pour améliorer les performances :
ALTER TABLE timezones ADD INDEX (timezone_groupe_fr);Analyse après l’ajout de l’index
Section titled “Analyse après l’ajout de l’index”Exécutons à nouveau la même requête avec EXPLAIN :
EXPLAIN SELECT timezone_groupe_fr, COUNT(timezone_detail) AS total_timezoneFROM timezonesGROUP BY timezone_groupe_frORDER BY timezone_groupe_fr ASC;Résultat :
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | timezones | index | timezone_groupe_fr | timezone_groupe_fr | 153 | NULL | 421 | NULL |
Interprétation des changements :
| Colonne | Ancienne valeur | Nouvelle valeur | Explication |
|---|---|---|---|
type |
ALL | index | Utilisation de l’index |
possible_keys |
NULL | timezone_groupe_fr | Index disponible |
key |
NULL | timezone_groupe_fr | Index effectivement utilisé |
key_len |
NULL | 153 | Longueur de la clé utilisée |
Extra |
Using temporary; Using filesort | NULL | Plus besoin de table temporaire ni de tri |
Conclusion : L’ajout de l’index a transformé la requête. Elle est maintenant beaucoup plus efficace, et il n’est plus nécessaire de créer une table temporaire.
EXPLAIN ANALYZE (PostgreSQL)
Section titled “EXPLAIN ANALYZE (PostgreSQL)”PostgreSQL propose EXPLAIN ANALYZE qui exécute réellement la requête et affiche des statistiques précises (temps d’exécution, nombre de lignes réelles, etc.) :
EXPLAIN ANALYZESELECT timezone_groupe_fr, COUNT(timezone_detail) AS total_timezoneFROM timezonesGROUP BY timezone_groupe_frORDER BY timezone_groupe_fr ASC;Résultat (PostgreSQL) :
GroupAggregate (cost=12.34..18.56 rows=10 width=48) (actual time=0.123..0.456 rows=10 loops=1) Group Key: timezone_groupe_fr -> Index Only Scan using idx_timezone_groupe_fr on timezones (cost=0.28..10.42 rows=421 width=48) (actual time=0.019..0.156 rows=421 loops=1)Planning Time: 0.089 msExecution Time: 0.523 msInterprétation :
cost: coût estimé (unités arbitraires).actual time: temps réel d’exécution.rows: nombre de lignes traitées.Execution Time: temps total d’exécution de la requête.
Les différents types d’accès (type)
Section titled “Les différents types d’accès (type)”La colonne type est cruciale pour évaluer la performance. Voici les types d’accès du plus efficace au moins efficace :
| Type | Description |
|---|---|
system |
Table avec une seule ligne (meilleur cas possible) |
const |
Une seule ligne correspond (clé primaire ou unique) |
eq_ref |
Une seule ligne pour chaque ligne de la table précédente (jointure sur clé primaire) |
ref |
Plusieurs lignes correspondent (index simple) |
range |
Recherche dans un intervalle (BETWEEN, >, <) |
index |
Parcours complet d’un index (meilleur qu’un scan complet de la table) |
ALL |
Parcours complet de la table (le pire) |
Objectif : obtenir const, eq_ref, ref ou range si possible. Éviter ALL.
Que signifient les colonnes Extra ?
Section titled “Que signifient les colonnes Extra ?”Les valeurs les plus courantes dans la colonne Extra :
| Valeur | Signification |
|---|---|
Using index |
L’index couvre toutes les colonnes nécessaires (pas besoin d’accéder à la table) |
Using where |
Le SGBD filtre les lignes avec la clause WHERE après avoir lu la table |
Using temporary |
Utilisation d’une table temporaire (souvent pour GROUP BY ou DISTINCT) |
Using filesort |
Tri externe nécessaire (pas d’index pour ORDER BY) |
Using index condition |
Utilisation de l’optimisation “Index Condition Pushdown” (ICP) |
Bonnes pratiques avec EXPLAIN
Section titled “Bonnes pratiques avec EXPLAIN”-
Utilisez
EXPLAINsystématiquement pour les requêtes complexes ou lentes. -
Cherchez les
ALLet lesUsing temporary; Using filesort: ce sont des signaux d’alerte indiquant qu’un index pourrait améliorer les performances. -
Vérifiez que vos index sont bien utilisés (
keydoit être non NULL). -
Testez avec des données réelles (ou une copie représentative). Les plans d’exécution peuvent changer selon la distribution des données.
-
Comparez avant / après pour mesurer l’impact de vos optimisations.
-
Surveillez les index composites : ils sont utilisés uniquement si les colonnes sont dans l’ordre.
-
Pour PostgreSQL, utilisez
EXPLAIN ANALYZEpour obtenir des temps d’exécution réels (attention, la requête est exécutée).
Erreurs courantes
Section titled “Erreurs courantes”| Erreur | Cause | Solution |
|---|---|---|
| Index créé mais pas utilisé | Colonne non sélective ou type incompatible | Vérifier la colonne et le type |
Using temporary malgré un index |
L’index ne couvre pas la requête | Ajouter un index composite |
Using filesort malgré un index |
L’ORDER BY ne peut pas utiliser l’index |
Indexer la colonne de tri |
| Résultat différent selon l’environnement | Données différentes (taille, distribution) | Tester avec des données représentatives |
Prochain chapitre
Section titled “Prochain chapitre”Vous savez désormais comment analyser vos requêtes avec EXPLAIN pour optimiser les performances. Il ne reste plus qu’à apprendre à commenter vos requêtes pour les rendre plus compréhensibles.
👉 Chapitre 46 : Commentaires SQL – Documenter vos requêtes
Bonne continuation !
Junior TSAFACK – 08/08/2026