1️⃣ Pourquoi une requête SQL est lente ?
En général, c’est à cause de :
❌ Absence ou mauvais index
❌ Requêtes qui scannent toute la table (Full Table Scan)
❌ Mauvaise écriture SQL
❌ Trop de données retournées
❌ Fonctions dans
WHERE❌ Mauvais JOIN
❌ Verrouillage (locks, deadlocks)
2️⃣ Règle n°1 : utiliser les index correctement
❌ Mauvais exemple (lent)
SELECT *
FROM Orders
WHERE YEAR(OrderDate) = 2024;
👉 Problème :
La fonction
YEAR()empêche l’utilisation d’un index
✅ Bon exemple (rapide)
SELECT OrderId, CustomerId, OrderDate
FROM Orders
WHERE OrderDate >= '2024-01-01'
AND OrderDate < '2025-01-01';
✔ Index utilisable
✔ Moins de données lues
🔹 Créer un index
CREATE INDEX IX_Orders_OrderDate
ON Orders(OrderDate);
3️⃣ Règle n°2 : éviter SELECT *
❌ Mauvais
SELECT *
FROM Customers;
✅ Bon
SELECT CustomerId, Name, Email
FROM Customers;
👉 Moins :
I/O disque
Mémoire
Réseau
4️⃣ Règle n°3 : Index sur les colonnes de JOIN
❌ Lent
SELECT *
FROM Orders o
JOIN Customers c ON o.CustomerId = c.CustomerId;
✅ Optimisé
CREATE INDEX IX_Orders_CustomerId ON Orders(CustomerId);
CREATE INDEX IX_Customers_CustomerId ON Customers(CustomerId);
👉 Le JOIN devient Index Seek au lieu de Scan
5️⃣ Règle n°4 : filtrer AVANT de JOIN
❌ Mauvais
SELECT *
FROM Orders o
JOIN Customers c ON o.CustomerId = c.CustomerId
WHERE c.Country = 'FR';
✅ Meilleur
SELECT o.OrderId, o.OrderDate
FROM Orders o
JOIN Customers c
ON o.CustomerId = c.CustomerId
WHERE c.Country = 'FR';
➕ Index :
CREATE INDEX IX_Customers_Country
ON Customers(Country);
6️⃣ Règle n°5 : éviter les sous-requêtes coûteuses
❌ Lent
SELECT *
FROM Orders
WHERE CustomerId IN (
SELECT CustomerId FROM Customers WHERE Country = 'FR'
);
✅ Plus rapide
SELECT o.*
FROM Orders o
JOIN Customers c ON o.CustomerId = c.CustomerId
WHERE c.Country = 'FR';
7️⃣ Règle n°6 : utiliser EXISTS au lieu de IN (gros volumes)
❌ Lent
SELECT *
FROM Orders o
WHERE o.CustomerId IN (
SELECT CustomerId FROM Customers
);
✅ Rapide
SELECT *
FROM Orders o
WHERE EXISTS (
SELECT 1 FROM Customers c
WHERE c.CustomerId = o.CustomerId
);
8️⃣ Règle n°7 : index couvrant (INCLUDE)
🎯 Objectif
Éviter les accès supplémentaires à la table
CREATE INDEX IX_Orders_Covering
ON Orders(CustomerId)
INCLUDE (OrderDate, TotalAmount);
✔ La requête est satisfaite uniquement par l’index
9️⃣ Règle n°8 : attention aux fonctions dans WHERE
❌ Lent
WHERE UPPER(Email) = 'TEST@MAIL.COM'
✅ Rapide
WHERE Email = 'test@mail.com'
Ou :
CREATE INDEX IX_Customers_Email
ON Customers(Email);
🔟 Règle n°9 : Pagination efficace
❌ Mauvais (OFFSET trop grand)
SELECT *
FROM Orders
ORDER BY OrderDate
OFFSET 100000 ROWS FETCH NEXT 20 ROWS ONLY;
✅ Bon (keyset pagination)
SELECT *
FROM Orders
WHERE OrderDate < @LastOrderDate
ORDER BY OrderDate DESC
FETCH NEXT 20 ROWS ONLY;
1️⃣1️⃣ Lire le plan d’exécution (INDISPENSABLE)
SQL Server
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
Ou :
Actual Execution Plan
Rechercher :
❌ Table Scan
❌ Key Lookup
❌ Nested Loop coûteux
1️⃣2️⃣ Exemple concret complet
Tables
Customers(CustomerId, Name, Country)
Orders(OrderId, CustomerId, OrderDate, Total)
Requête optimisée
SELECT o.OrderId, o.Total
FROM Orders o
JOIN Customers c ON o.CustomerId = c.CustomerId
WHERE c.Country = 'FR'
AND o.OrderDate >= '2024-01-01';
Index
CREATE INDEX IX_Customers_Country_Id
ON Customers(Country, CustomerId);
CREATE INDEX IX_Orders_Customer_Date
ON Orders(CustomerId, OrderDate)
INCLUDE (Total);
1️⃣3️⃣ Checklist rapide (à retenir)
✅ Pas de SELECT *
✅ Index sur WHERE / JOIN / ORDER BY
✅ Pas de fonctions sur colonnes filtrées
✅ Préférer EXISTS
✅ Lire le plan d’exécution
✅ Limiter le volume de données
Plan d’exécution avec EXPLAIN PLAN, et c’est l’outil n°1 pour optimiser les performances SQL.
1️⃣ Qu’est-ce que EXPLAIN PLAN dans Oracle ?
EXPLAIN PLAN montre comment Oracle prévoit d’exécuter ta requête :
Index ou Full Table Scan
Ordre des JOIN
Coût estimé
Méthode de JOIN (Nested Loop, Hash Join…)
⚠️ Important :
EXPLAIN PLAN= plan estiméPour le plan réel, on utilise
DBMS_XPLAN.DISPLAY_CURSOR
2️⃣ Pré-requis (table PLAN_TABLE)
Dans la plupart des bases Oracle modernes, elle existe déjà.
Si besoin :
@?/rdbms/admin/utlxplan.sql
3️⃣ Syntaxe de base : EXPLAIN PLAN
Exemple simple
EXPLAIN PLAN FOR
SELECT *
FROM employees
WHERE department_id = 10;
👉 Cela n’exécute pas la requête
👉 Ça stocke le plan dans PLAN_TABLE
4️⃣ Afficher le plan d’exécution
Méthode classique
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Résultat typique
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 3 |
| 1 | TABLE ACCESS FULL | EMPLOYEES | 10 | 3 |
--------------------------------------------------------------------------------
👉 Ici :
❌
TABLE ACCESS FULL→ pas d’index utilisé
5️⃣ Optimisation simple avec index
Créer un index
CREATE INDEX IDX_EMP_DEPT
ON employees(department_id);
Rejouer le plan
EXPLAIN PLAN FOR
SELECT *
FROM employees
WHERE department_id = 10;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Nouveau plan
--------------------------------------------------------------------------------
| Id | Operation | Name | Cost |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 |
| 1 | INDEX RANGE SCAN | IDX_EMP_DEPT | 1 |
--------------------------------------------------------------------------------
✅ Index Range Scan
✅ Coût réduit → meilleure performance
6️⃣ Exemple avec JOIN (cas réel)
Requête
EXPLAIN PLAN FOR
SELECT e.employee_id, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE d.location_id = 1700;
Plan affiché
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Plan possible
| Id | Operation | Name |
------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | NESTED LOOPS | |
| 2 | TABLE ACCESS FULL | DEPARTMENTS |
| 3 | TABLE ACCESS FULL | EMPLOYEES |
❌ Deux Full Table Scan
7️⃣ Optimisation du JOIN
Index recommandés
CREATE INDEX IDX_DEPT_LOC
ON departments(location_id);
CREATE INDEX IDX_EMP_DEPT
ON employees(department_id);
Nouveau plan
| Id | Operation | Name |
------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | NESTED LOOPS | |
| 2 | INDEX RANGE SCAN | IDX_DEPT_LOC |
| 3 | INDEX RANGE SCAN | IDX_EMP_DEPT |
✅ Index utilisés
✅ JOIN rapide
8️⃣ Plan RÉEL (beaucoup mieux)
EXPLAIN PLAN ne montre pas le plan réellement exécuté.
Étape 1 : exécuter la requête
SELECT /*+ gather_plan_statistics */
e.employee_id, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE d.location_id = 1700;
Étape 2 : afficher le plan réel
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST'));
👉 Tu verras :
Nombre de lignes réelles
Temps réel
Écarts estimé vs réel
9️⃣ Comment optimiser à partir du plan ?
À surveiller ⚠️
| Élément | Action |
|---|---|
| TABLE ACCESS FULL | Ajouter index |
| COST élevé | Réécrire requête |
| NESTED LOOP sur gros volumes | Forcer HASH JOIN |
| FILTER | Éviter fonctions |
| KEY LOOKUP | Index couvrant |
🔟 Forcer un plan (si nécessaire)
⚠️ À utiliser avec prudence
SELECT /*+ INDEX(e IDX_EMP_DEPT) */
*
FROM employees e
WHERE department_id = 10;
1️⃣1️⃣ Résumé rapide (entretien / pratique)
✅ EXPLAIN PLAN FOR → plan estimé
✅ DBMS_XPLAN.DISPLAY → affichage
✅ DBMS_XPLAN.DISPLAY_CURSOR → plan réel
✅ Index = arme n°1
✅ Toujours comparer estimé vs réel
Aucun commentaire:
Enregistrer un commentaire