Question 1Quand le résultat d'EXPLAIN QUERY PLAN passe de SCAN perf_sales à SEARCH perf_sales USING INDEX ..., qu'est-ce que ça veut dire ?
Index — Composite, partiel, expression et UNIQUE
Sur la table perf_sales de 50 000 lignes, tu créeras des index avec CREATE INDEX, construiras des index composites, un index partiel sur status='pending', un index sur l'expression amount/100, et un index UNIQUE sur sale_id, puis observeras EXPLAIN QUERY PLAN basculer de FULL SCAN (SCAN) à INDEX SCAN (SEARCH).
Le jeu de données de cet article — la table perf_sales
Un index fonctionne comme l'index à la fin d'un livre : il te laisse trouver rapidement les lignes ayant une valeur de colonne donnée.
Cet article parcourt la création d'index avec CREATE INDEX, la construction d'index composites, partiels, sur expression et UNIQUE, et l'usage d'EXPLAIN QUERY PLAN pour confirmer comment la stratégie de recherche change.
CREATE INDEX et EXPLAIN QUERY PLAN — transformer un parcours complet de table en recherche par index
Crée un index avec CREATE INDEX nom_index ON table(colonne);.
Quand tu utilises une colonne indexée dans une condition d'égalité ou de plage WHERE, l'optimiseur peut choisir une recherche par index au lieu d'un parcours complet de table et sauter directement aux lignes correspondantes.
Vérifie la stratégie de recherche avec EXPLAIN QUERY PLAN.
Préfixe ta requête avec EXPLAIN QUERY PLAN et tu verras le plan pour trouver les lignes au lieu des résultats réels.
Sans index tu verras SCAN perf_sales (aussi appelé FULL SCAN — examiner chaque ligne de la table), et une fois qu'un index est utilisé tu verras quelque chose comme SEARCH perf_sales USING INDEX ... (aussi appelé INDEX SCAN — restreindre la recherche avec l'index).
La formulation exacte du plan varie selon l'environnement, donc cet article considère une exécution réussie comme la bonne réponse et lit le sens du plan dans la prose et les explications.
-- Plan sans index (exemple séparé en lecture seule)
EXPLAIN QUERY PLAN
SELECT sale_id, amount FROM perf_sales WHERE region = 'East';
--> SCAN perf_sales (examiner chaque ligne)
-- Créer un index, puis regarder le même plan
DROP INDEX IF EXISTS ix_region;
CREATE INDEX ix_region ON perf_sales(region);
EXPLAIN QUERY PLAN
SELECT sale_id, amount FROM perf_sales WHERE region = 'East';
--> SEARCH perf_sales USING INDEX ix_region (region=?)
-- Note : exécute DROP -> CREATE -> EXPLAIN d'un bout à l'autre dans la même exécution
Index composites — indexer plusieurs colonnes ensemble
Écrire plusieurs colonnes entre parenthèses — CREATE INDEX nom ON table(col1, col2); — produit un index composite (aussi appelé index multi-colonnes).
Il accélère les requêtes qui utilisent les conditions à partir de la colonne de tête, comme WHERE col1 = ... AND col2 >= ....
L'ordre des colonnes compte : un index composite n'aidera pas les requêtes qui n'utilisent pas sa colonne de tête dans la clause WHERE.
Si ton schéma d'accès est fixe — par exemple « égalité sur emp_id et plage sur sale_date » — construire un index composite dans cet ordre permet à un seul index de couvrir les deux conditions.
-- Index composite (exemple séparé en lecture seule)
DROP INDEX IF EXISTS ix_demo;
CREATE INDEX ix_demo ON perf_sales(region, sale_date);
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales
WHERE region = 'East' AND sale_date >= '2024-01-01';
--> SEARCH perf_sales USING INDEX ix_demo (region=? AND sale_date>?)
-- Restreindre par region (égalité), puis restreindre sale_date par plage
Index partiels et index sur expression
Un index partiel se construit avec CREATE INDEX nom ON table(colonne) WHERE condition; et n'indexe qu'un sous-ensemble des lignes de la table.
Si tu cherches fréquemment des lignes avec une valeur précise comme status = 'pending', indexer uniquement ces lignes garde l'index petit et concentré sur les requêtes que tu exécutes vraiment.
Dans perf_sales, seules 5 000 lignes (10 % du total) ont status = 'pending', donc un index partiel avec WHERE status = 'pending' est un choix naturel.
Un index sur expression se construit avec CREATE INDEX nom ON table(expression); et indexe le résultat d'une expression calculée plutôt qu'une colonne brute.
Utilise-le pour les requêtes qui appliquent la même expression côté recherche, comme WHERE amount / 100 = ....
Un index ordinaire sur une colonne n'aide normalement plus dès que tu appliques une fonction ou une opération arithmétique à cette colonne, mais si tu indexes l'expression elle-même, les recherches utilisant l'expression correspondante peuvent s'en servir.
-- Index partiel (exemple séparé en lecture seule)
DROP INDEX IF EXISTS ix_part;
CREATE INDEX ix_part ON perf_sales(sale_date) WHERE status = 'refunded';
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales
WHERE status = 'refunded' AND sale_date >= '2024-01-01';
--> SEARCH perf_sales USING ... INDEX ix_part (sale_date>?)
-- Index sur expression
DROP INDEX IF EXISTS ix_expr;
CREATE INDEX ix_expr ON perf_sales(qty * amount);
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales WHERE qty * amount = 100000;
--> SEARCH perf_sales USING ... INDEX ix_expr (<expr>=?)
Index UNIQUE — un index qui interdit les doublons
Un index UNIQUE — construit avec CREATE UNIQUE INDEX nom ON table(colonne); — accélère les recherches et empêche l'insertion de valeurs en doublon dans la colonne.
Si tu essaies de créer un index UNIQUE sur une colonne qui contient déjà des doublons, tu obtiens une erreur UNIQUE constraint failed et l'index n'est pas créé du tout.
Le revers : si tu en crées un sur une colonne dont les valeurs sont uniques (une clé primaire ou une clé métier unique), tu obtiens la prévention des doublons plus une recherche rapide par égalité.
Dans perf_sales, product (200 valeurs distinctes) a beaucoup de répétitions, donc un index UNIQUE n'y est pas possible. sale_id, en revanche, est la clé primaire et unique, donc tu peux en construire un.
L'exercice suivant crée un index UNIQUE sur sale_id, confirmant qu'une colonne unique accepte l'index et que les recherches par égalité sur cette colonne l'utilisent.
-- Index UNIQUE (exemple séparé en lecture seule)
DROP INDEX IF EXISTS ix_u_demo;
CREATE UNIQUE INDEX ix_u_demo ON perf_sales(sale_id);
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales WHERE sale_id = 12345;
-- Essayer d'ajouter UNIQUE sur une colonne avec doublons est rejeté
-- CREATE UNIQUE INDEX ix_bad ON perf_sales(product);
--> UNIQUE constraint failed: perf_sales.product
-- product a 200 valeurs distinctes répétées sur 50 000 lignes, donc ne peut pas être rendu UNIQUE
Astuces — quand ajouter un index
Règle empirique : un index bat un SCAN quand tu peux restreindre le résultat à environ 15-20 % ou moins de la table. Au-delà, l'optimiseur choisit souvent SCAN automatiquement de toute façon.
- À ajouter : recherches par égalité ou par plage sur une colonne d'une grosse table, clés primaires / clés étrangères / colonnes UNIQUE, clés de JOIN, colonnes référencées par ORDER BY / GROUP BY
- À éviter : petites tables de quelques milliers de lignes ou moins, colonnes peu sélectives comme gender, tables à fortes écritures, LIKE '%foo%' (un joker en tête ne peut pas utiliser d'index)
Le flux de travail de base : vérifie le plan SCAN / SEARCH avec EXPLAIN QUERY PLAN et ajoute des index uniquement aux requêtes lentes.
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Quelle requête bénéficie le plus de l'index composite CREATE INDEX ix ON perf_sales(emp_id, sale_date); ?
Question 3Que se passe-t-il quand tu lances CREATE UNIQUE INDEX sur la colonne product, qui contient des doublons ?