Oracle
Oracle
Exemple :
SQL
-- Générer un rapport AWR entre deux snapshots
@?/rdbms/admin/[Link]
Pourquoi c'est important : L'AWR permet d'identifier les requêtes problématiques et les goulets d'étranglement système sur des périodes
prolongées.
AWR : Vue globale sur des périodes longues (minimum 10 minutes), idéal pour les tendances
ASH : Vue détaillée en temps quasi-réel (seconde par seconde), parfait pour les problèmes transitoires
Exemple d'utilisation :
SQL
-- Voir les sessions actives
SELECT * FROM V$ACTIVE_SESSION_HISTORY
WHERE sample_time > SYSDATE - 1/24;
Contenu :
Fichiers trace
Journal d'alerte (alert log)
Rapports du moniteur de santé
Données d'incidents
Outils d'accès :
bash
# Utilisation d'ADRCI (command line tool)
adrci> show home
adrci> show alert
adrci> show incident
adrci> purge
Hard Parse : Processus complet d'analyse, transformation et optimisation d'une requête (coûteux en ressources)
Soft Parse : Réutilisation d'un plan d'exécution existant dans le shared pool (beaucoup plus rapide)
SQL
ALTER SYSTEM FLUSH SHARED_POOL;
SELECT * FROM dual WHERE dummy='LITERAL1';
SELECT * FROM dual WHERE dummy='LITERAL2'; -- Nouveau hard parse
SQL
SELECT * FROM dual WHERE dummy='LITERAL2';
-- Même requête, soft parse (le compteur d'exécutions augmente)
Exemple :
SQL
-- Mauvaise pratique (littéraux)
SELECT * FROM emp WHERE deptno = 10;
SELECT * FROM emp WHERE deptno = 20;
Exemple :
SQL
EXPLAIN PLAN FOR
SELECT [Link], [Link]
FROM emp e JOIN dept d ON [Link] = [Link]
WHERE [Link] > 2000;
-- Afficher le plan
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
2.4 AUTOTRACE
Concept : Utilitaire SQL*Plus qui exécute la requête et affiche des statistiques de performance.
Exemple :
SQL
SET AUTOTRACE ON
SELECT * FROM emp WHERE sal > 2000;
Scénario : Afficher tous les employés qui ont un département correspondant dans la table DEPT.
SQL
SELECT ename, sal, [Link]
FROM emp
JOIN dept ON [Link] = [Link];
SQL
SELECT ename, sal, deptno
FROM emp
WHERE deptno IN (SELECT deptno FROM dept);
SQL
SELECT ename, sal, deptno
FROM emp
WHERE EXISTS (SELECT null FROM dept WHERE [Link] = [Link]);
SQL
SELECT * FROM (
SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC
) WHERE rownum <= 5;
SQL
SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC
FETCH FIRST 5 ROWS ONLY;
SQL
SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC
FETCH FIRST 5 ROWS WITH TIES;
-- Inclut tous les employés ayant le même salaire que la 5ème position
Fonctionnement détaillé :
Exemple concret :
3) Insertion de 27 → Split
- Si un bloc peut contenir 3 clés, 27 provoque un débordement
- Trie: 5, 12, 18, 27
- Clé médiane: 18
- Nouvel arbre:
[18] ← racine
/ \
[5,12] [18,27] ← feuilles chaînées
Hauteur logarithmique (généralement 2-3 niveaux même pour des tables de millions de lignes)
Recherche rapide: coût moyen = log(n) vs n/2 pour un scan linéaire
Parcours séquentiel efficace grâce aux feuilles chaînées
Quand l'utiliser :
Exemple :
SQL
-- Création (B-Tree est le type par défaut)
CREATE INDEX idx_emp_ename ON emp(ename);
-- Utilisation
SELECT * FROM emp WHERE ename = 'SMITH'; -- Utilisera l'index
Fonctionnement :
Pour chaque valeur distincte, un bit est défini pour chaque ligne de la table
Bit = 1 si la ligne a cette valeur, 0 sinon
Les opérations logiques (AND, OR) permettent de combiner plusieurs conditions
Avantages :
Inconvénients :
Quand l'utiliser :
Exemple :
SQL
CREATE BITMAP INDEX idx_emp_job ON emp(job);
Règles d'utilisation :
L'ordre des colonnes dans l'index est crucial
Placez d'abord les colonnes les plus sélectives
Les colonnes utilisées avec des opérateurs d'égalité (=) devraient précéder celles avec des intervalles (>, <, BETWEEN)
Quand l'utiliser :
Exemple :
SQL
-- Création d'un index composite
CREATE BITMAP INDEX idx_sales_color_size
ON sales(color, tsize);
-- Utilisé quand:
SELECT COUNT(*) FROM sales
WHERE color = 'BLACK' AND tsize = 'L';
Quand l'utiliser :
Exemple :
SQL
-- Sans index fonctionnel:
SELECT * FROM emp WHERE INITCAP(ename) = 'Smith'; -- Full scan!
Fonctionnement :
Exemple :
SQL
SELECT e.last_name, [Link], d.department_name
FROM employees e, departments d
WHERE d.department_name IN ('Marketing','Sales')
AND e.depart_id = d.depart_id;
-- Employees (107 lignes), Departments (27 lignes)
-- Nested Loops sera probablement choisi
Fonctionnement détaillé :
Exemple concret :
Cas problématique : Si la table de hash ne tient pas en mémoire, Oracle partitionne le hash et écrit sur TEMP, ce qui dégrade les
performances.
Fonctionnement :
Syntaxe :
SQL
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name = table2.column_name;
SQL
SELECT column_name(s)
FROM table1
LEFT JOIN table2
ON table1.column_name = table2.column_name;
-- OU
SELECT column_name(s)
FROM table1, table2
WHERE table1.column_name = table2.column_name(+);
SQL
SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name = table2.column_name;
Solutions :
SQL
-- 1. Collecter des statistiques représentatives
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA', 'TABLE_NAME');
Fonctionnement :
Exemple :
Statistics Collector
│
├── Nested Loops (valide pour 1-100 lignes)
└── Hash Join (utilisé si >100 lignes)
Règles de l'optimiseur :
3. Si la fusion de vues n'est pas possible, joindre d'abord les tables dans la vue
SQL
-- Utiliser le hint LEADING pour voir l'ordre
SELECT /*+ LEADING(@"SEL$1" "D"@"SEL$1" "E"@"SEL$1") */
d.dept_name, [Link]
FROM departments d, employees e
WHERE e.dept_id = d.dept_id;
Exemples courants :
SQL
-- Forcer l'utilisation d'un index
SELECT /*+ INDEX(emp idx_emp_ename) */ *
FROM emp WHERE ename = 'SMITH';
-- Éviter un index
SELECT /*+ NO_INDEX(emp idx_emp_sal) */ *
FROM emp WHERE sal > 1000;
Règles importantes :
Les hints sont des directives, pas des commandes (l'optimiseur peut les ignorer)
Pas d'espace entre /* et +
Les hints contradictoires sont ignorés (FULL vs INDEX sur la même table)
Aucune notification quand un hint est ignoré
Avantages :
SQL
-- Sans vue inline (calculs redondants)
SELECT orderid, color, qty,
(cost + 0.20 * cost) AS gross,
(cost + 0.20 * cost) * 0.10 AS tax
FROM sales;
SQL
-- Méthode traditionnelle
SELECT * FROM (
SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC
) WHERE rownum <= 5;
-- Méthode moderne (12c+)
SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC
FETCH FIRST 5 ROWS ONLY;
Création :
SQL
-- Par défaut: ON COMMIT DELETE ROWS (données supprimées au COMMIT)
CREATE GLOBAL TEMPORARY TABLE temp_table (
id NUMBER,
name VARCHAR2(50)
) ON COMMIT DELETE ROWS;
Avantages :
Exemple d'utilisation :
SQL
-- Créer une table temporaire avec un sous-ensemble de données
CREATE GLOBAL TEMPORARY TABLE black_tshirts (
orderid NUMBER,
color VARCHAR2(10),
tsize VARCHAR2(4),
qty NUMBER,
cost NUMBER
) ON COMMIT PRESERVE ROWS;
Syntaxe :
SQL
WITH cte_name AS (
SELECT...
FROM...
WHERE...
)
SELECT * FROM cte_name;
Exemple d'optimisation :
SQL
-- Sans CTE (agrégation répétée)
SELECT color, SUM(qty)
FROM sales
GROUP BY color
HAVING SUM(qty) > (
SELECT SUM(qty)
FROM sales
WHERE color = 'Blue'
);
Création :
SQL
CREATE MATERIALIZED VIEW mv_sales_summary
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
AS
SELECT color, tsize, SUM(qty) AS total_qty, AVG(cost) AS avg_cost
FROM sales
GROUP BY color, tsize;
Options de rafraîchissement :
Rafraîchissement manuel :
SQL
-- Rafraîchir une vue matérialisée
BEGIN
DBMS_MVIEW.REFRESH('mv_sales_summary', 'F'); -- 'F' = FAST, 'C' = COMPLETE
END;
/
Exemple concret :
SQL
-- Création d'une vue matérialisée pour le reporting
CREATE MATERIALIZED VIEW state_summary AS
SELECT [Link],
SUM([Link]) AS "Total Cost",
SUM([Link]) AS "Total Quantity"
FROM sales s
JOIN customers c ON [Link] = [Link]
GROUP BY [Link];
Bonnes pratiques :
Évitez les MViews contenant plus de 70% des données des tables source
Planifiez le rafraîchissement pendant les périodes creuses
Surveillez l'espace disque utilisé par les MViews
📊 Plan d'Exécution
EXPLAIN PLAN : Voir le plan sans exécuter
⚡ Parsing
Hard Parse : Évitez-le! Coûteux en CPU
Soft Parse : Réutilisation des plans existants
Variables Bind : Clé pour réduire les hard parses
SQL
VARIABLE deptno NUMBER;
EXEC :deptno := 10;
SELECT * FROM emp WHERE deptno = :deptno;
📑 Indexation
Type
Quand Utiliser Exemple Notes
d'Index
🔗 Stratégies de Jointure
Type de Jointure Quand Utilisée Caractéristiques
Nested Loops Petites tables, index sur table interne Comme des boucles imbriquées
Sort Merge Tables déjà triées, prédicats non-égaux Deux phases: tri puis fusion
Inner Join JOIN ou WHERE [Link] = [Link] Correspondances dans les deux tables
Left Outer LEFT JOIN ou WHERE [Link] = [Link](+) Toutes les lignes de gauche + correspondances
Right Outer RIGHT JOIN ou WHERE [Link](+) = [Link] Toutes les lignes de droite + correspondances
Full Outer FULL OUTER JOIN Toutes les lignes des deux tables
🚀 Techniques Avancées
Hints : /*+ INDEX(table index_name) */ (utilisez avec prudence!)
CTE : WITH cte_name AS (SELECT...) SELECT... (réutilisation des résultats)
⚡ Optimisation EXISTS vs IN
SQL
-- Méthode la plus lente
SELECT ename, sal FROM emp JOIN dept ON [Link] = [Link];
-- Méthode moyenne
SELECT ename, sal FROM emp WHERE deptno IN (SELECT deptno FROM dept);
🔧 Bonnes Pratiques
1. Évitez les SELECT * - Sélectionnez uniquement les colonnes nécessaires
2. Indexez les colonnes de jointure et de WHERE
3. Utilisez des variables bind pour les applications
4. Analysez les statistiques régulièrement : DBMS_STATS.GATHER_TABLE_STATS
⚠️ Pitfalls Courants
Trop d'index ralentit les INSERT/UPDATE/DELETE
Les index bitmap sur tables OLTP = problèmes de verrouillage
Les hints peuvent devenir obsolètes après des changements de schéma
Les MViews occupent de l'espace disque et nécessitent un rafraîchissement
Les statistiques obsolètes entraînent de mauvaises décisions d'optimisation
Les jointures manquantes créent des produits cartésiens coûteux
Les variables bind non utilisées causent des hard parses excessifs
Les CTE et vues inline mal conçues peuvent dégrader les performances
Impact de l'utilisateur :
Exemple pratique :
SQL
-- Pour voir les paramètres de session actuels
SELECT name, value FROM v$parameter WHERE name LIKE 'optimizer%';
Pour les examens : Un changement de schéma peut modifier complètement le plan d'exécution d'une requête identique.
Différences fondamentales :
SQL
-- ON COMMIT DELETE ROWS (comportement par défaut)
CREATE GLOBAL TEMPORARY TABLE temp_orders (
order_id NUMBER,
amount NUMBER
) ON COMMIT DELETE ROWS;
Pour les examens : Deux sessions peuvent insérer des données dans la même table temporaire simultanément sans jamais voir les
données de l'autre session.
Ordre d'exécution :
Exemple illustratif :
SQL
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
WHERE hire_date > '2020-01-01'
GROUP BY department_id
HAVING AVG(salary) > 5000
ORDER BY avg_salary DESC;
Les alias définis dans SELECT ne sont pas disponibles dans WHERE ou GROUP BY
HAVING s'applique après l'agrégation, WHERE avant
ORDER BY est la dernière étape et peut utiliser des alias de colonnes
Performance Plus rapide pour des données fixes Meilleure utilisation de l'espace
Limite max 2000 octets 4000 octets (ou 32767 avec extended)
Exemples pratiques :
SQL
-- CHAR(10) occupe toujours 10 octets
CREATE TABLE fixed_length (
code CHAR(10) -- 'ABC' stocké comme 'ABC '
);
Pour les examens : Les index sur les colonnes CHAR peuvent être moins efficaces à cause du padding, surtout pour les recherches
partielles.
C.5 Suppression des doublons : DISTINCT, GROUP BY, UNION vs UNION ALL
Concept clé : Différentes méthodes pour éliminer les doublons avec des performances variables.
Méthodes disponibles :
SQL
-- Méthode 1: DISTINCT (la plus simple mais pas toujours la plus rapide)
SELECT DISTINCT department_id FROM employees;
-- Méthode 4: UNION ALL (beaucoup plus rapide si les doublons ne sont pas un problème)
SELECT department_id FROM employees WHERE salary > 5000
UNION ALL
SELECT department_id FROM employees WHERE hire_date > '2020-01-01';
Pour les examens : UNION ALL est significativement plus rapide que UNION car il n'a pas besoin de trier et de comparer les résultats pour
éliminer les doublons.
✅ SELECT : Peut utiliser des alias de tables, mais pas ses propres alias
✅ ORDER BY : Peut utiliser des alias de colonnes définis dans SELECT
❌ WHERE : Ne peut pas utiliser d'alias définis dans SELECT (exécuté avant SELECT)
❌ GROUP BY : Ne peut pas utiliser d'alias définis dans SELECT (exécuté avant SELECT)
❌ HAVING : Ne peut pas utiliser directement les alias de colonnes (mais peut utiliser les alias d'agrégations)
Exemples corrects et incorrects :
Pour les examens : L'ordre logique d'exécution explique pourquoi les alias ne sont pas disponibles dans WHERE et GROUP BY.
SQL
SET AUTOTRACE ON EXPLAIN
SELECT employee_id, first_name, last_name
FROM employees
WHERE department_id = 90 AND salary > 10000;
Sortie typique :
Execution Plan
----------------------------------------------------------
Plan hash value: 1444908879
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 183 | 2 (0)|
|* 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 3 | 183 | 2 (0)|
|* 2 | INDEX RANGE SCAN | EMP_DEPT_IX | 6 | | 1 (0)|
--------------------------------------------------------------------------------
Pour les examens : Un bon index devrait transformer les FILTER PREDICATES en ACCESS PREDICATES pour améliorer les
performances.
Limites principales :
Pour les examens : Dépasser ces limites génère des erreurs ORA-00910 (nombre de colonnes trop élevé) ou ORA-01450 (clé d'index trop
grande).
SQL
-- Vérifier le ratio de compression avant création
SELECT
(SELECT COUNT(*) FROM sales WHERE color = 'BLUE') AS filtered_count,
(SELECT COUNT(*) FROM sales) AS total_count,
ROUND((SELECT COUNT(*) FROM sales WHERE color = 'BLUE') * 100 /
(SELECT COUNT(*) FROM sales), 2) AS percentage
FROM dual;
Yahia Charif