EXPLAIN SELECT * FROM wp_reservations WHERE statut = 'confirmee' AND date_debut > '2022-11-01' : cette seule commande, lancée dans phpMyAdmin sur une table de réservations d’une extension maison, a suffi à expliquer pourquoi l’écran d’administration mettait plus de six secondes à s’afficher une fois la table passée à quelques dizaines de milliers de lignes.
La colonne rows du résultat d’EXPLAIN affichait un nombre proche du total de lignes de la table, et la colonne key restait vide : MySQL parcourait l’intégralité de la table à chaque affichage de l’écran, faute d’index utilisable pour cette combinaison de filtres.
Le diagnostic à l’EXPLAIN
Avant toute correction, il faut confirmer le diagnostic plutôt que de deviner. La commande EXPLAIN placée devant une requête SELECT retourne un plan d’exécution : type de balayage (type), index envisagés (possible_keys), index réellement utilisé (key) et estimation du nombre de lignes examinées (rows). Une valeur ALL dans la colonne type signale un balayage complet de table, le signe le plus net d’un index manquant.
Sur la table en question, deux colonnes étaient systématiquement filtrées ensemble dans le code de l’extension : statut et date_debut. Un index existait déjà sur date_debut seule, posé lors d’une optimisation précédente, mais il n’aidait pas cette requête précise, car le moteur devait quand même vérifier le statut ligne par ligne après avoir localisé la plage de dates.
Pourquoi un index simple ne suffit pas

Un index sur une seule colonne accélère les requêtes filtrées uniquement sur cette colonne. Dès qu’une requête combine deux conditions avec un AND, MySQL ne peut exploiter pleinement qu’un seul index à la fois dans la plupart des plans d’exécution simples : il utilise le premier pour réduire le nombre de lignes candidates, puis vérifie la seconde condition en parcourant ces lignes une par une.
Un index composite, couvrant les deux colonnes dans un ordre précis, permet au moteur de localiser directement les lignes qui satisfont les deux conditions sans étape de filtrage supplémentaire. L’ordre des colonnes dans l’index compte : une requête qui filtre d’abord sur l’égalité (statut = 'confirmee') puis sur une plage (date_debut > ...) tire parti d’un index qui place la colonne d’égalité en premier.
La migration par dbDelta
La correction ne passe pas par un ALTER TABLE lancé à la main sur la base de production, car rien ne garantit que cette commande sera rejouée sur les environnements de développement, de recette ou chez un client hébergé ailleurs. La bonne pratique WordPress consiste à décrire la structure complète de la table dans une chaîne SQL et à la confier à dbDelta(), qui compare la définition fournie à la structure existante et applique uniquement les différences.
function extension_maj_schema_reservations() {
global $wpdb;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$table = $wpdb->prefix . 'reservations';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
statut VARCHAR(20) NOT NULL,
date_debut DATETIME NOT NULL,
client_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (id),
KEY statut_date_debut (statut, date_debut)
) $charset_collate;";
dbDelta( $sql );
}
Le nom donné à l’index composite, statut_date_debut, reflète l’ordre des colonnes qu’il couvre : c’est une convention utile pour retrouver rapidement, des mois plus tard, quelle requête il sert à accélérer. La fonction s’accroche typiquement à un hook de mise à jour de version, comparé à une option stockée en base, pour ne s’exécuter qu’une fois par montée de version de l’extension.
Vérifier le résultat
Après application de la migration, relancer EXPLAIN sur la même requête doit afficher statut_date_debut dans la colonne key et une valeur bien plus faible dans rows. Sur le cas traité ici, le nombre de lignes examinées est passé de plusieurs dizaines de milliers à quelques centaines, et le temps de réponse de l’écran d’administration est descendu sous la demi-seconde.
- Vérifier l’index avec
SHOW INDEX FROM wp_reservationspour confirmer sa présence après migration. - Mesurer avant et après avec
EXPLAIN, pas seulement à l’œil sur le temps de chargement de la page. - Ne pas multiplier les index composites au hasard : chaque index ralentit légèrement les écritures et occupe de l’espace disque.
Notre verdict
Un index composite bien choisi coûte quelques lignes de migration et se paie immédiatement en confort d’utilisation dès que la table dépasse quelques milliers de lignes. La difficulté n’est pas technique mais méthodologique : sans passage par EXPLAIN, on optimise souvent la mauvaise requête ou on pose un index inutile. Ce diagnostic porte sur une correction ponctuelle a posteriori ; il ne remplace pas une réflexion sur la conception initiale du schéma, qui reste un sujet à part entière.