Règles a suivre pour optimiser les requêtes SQL

Transcription

Règles a suivre pour optimiser les requêtes SQL
RÈGLES A SUIVRE POUR OPTIMISER LES REQUÊTES SQL
1.
2.
3.
4.
5.
6.
7.
8.
9.
Le but de ce rapport est d’énumérer quelques règles pratiques à appliquer dans
l’élaboration des requêtes. Il permettra de comprendre pourquoi certaines requêtes SQL
sont longues à s’exécuter et d’éviter quelques pièges.
Créer des index, ils améliorent la performance des requêtes qui retournent un faible
pourcentage de rangées d’une table.
Ne créer les index sur une table qu’après avoir inséré les données dans celle-ci
Limiter le nombre d’index par table. Si des rangées sont insérées ou détruites dans une
table, alors tous les index de cette table doivent être mis à jour. Si une table a beaucoup
d’index et qu’on a beaucoup d’insertions, de mises à jour, de destructions, alors cela peut
affecter la performance de l’application.
Ne pas créer d’index pour les colonnes qui sont fréquemment modifiées car les pointeurs
de l’index doivent constamment être mis à jour.
Les clés primaires des tables doivent faire partie d’un index unique.
Les colonnes qui servent de prédicats pour les requêtes avec jointure doivent faire partie
d’un index.
Les colonnes qui sont fréquemment utilisées dans les clauses WHERE de requêtes
doivent faire partie d’un index.
Un index peut être utilisé si une colonne de l’index est référencée dans la clause WHERE
d’une requête et que la colonne indexée n’a pas été modifiée par une fonction et qu’on ne
teste pas avec ‘IS NULL’ ou ‘IS NOT NULL’.
Certains prédicats dans une clause ‘WHERE’ n’utiliseront pas d’index même si les
colonnes sont incluses dans un index :
• colonne1 > colonne2
• colonne1 < colonne2
• colonne1 >= colonne2
• colonne1 <= colonne2
• où colonne1 et colonne2 sont dans la même table.
• colonne IS NULL
• colonne NOT IN (...)
• colonne != expression
• colonne LIKE ‘%YYY’
Voici des exemples pour illustrer ces notions de base :
On a une table qui représente les écritures comptables pour une compagnie fictive.
CREATE TABLE ECRITURE (
ECR_NO
NUMBER
NOT NULL,
ECR_COMPTE
NUMBER
NOT NULL,
ECR_DATE
DATE
NOT NULL,
ECR_TYPE
CHAR(5),
ECR_DESCRIPTION
CHAR(80),
ECR_MONTANT
NUMBER(10,2)
NOT NULL,
ECR_SENS
CHAR(1)
NOT NULL) ;
1
CREATE UNIQUE INDEX ECR_IDX1 ON ECRITURE (ECR_NO) ;
CREATE UNIQUE INDEX ECR_IDX2
ON ECRITURE (ECR_DATE,ECR_TYPE,ECR_SENS) ;
Les requêtes suivantes utiliseront un index :
SELECT *
FROM ecriture
WHERE ecr_no = 1234 ;
Selon explain plan : Table access by rowid ecriture index unique scan ecr_idx1
SELECT *
FROM ecriture
WHERE ecr_no BETWEEN 200 AND 500 ;
Selon explain plan : Table access by rowid ecriture Index unique scan ecr_idx1
Les requêtes suivantes n’utiliseront pas d’index car on modifie la colonne indexée :
SELECT *
FROM ecriture
WHERE ecr_no*10=4320 ;
Selon explain plan : Table access full ecriture
SELECT *
FROM ecriture
WHERE to_char(ecr_date,’YYYYMMDD’) = ‘19970408’ ;
Selon explain plan : Table access full ecriture
Supposons qu’on a un index sur la colonne ecr_date. Les requêtes suivantes n’utiliseront
pas l’index car les valeurs null n’ont pas d’index même si la colonne ecr_date fait partie
d’un index.
SELECT *
FROM ecriture
WHERE ecr_date IS NULL ;
Selon explain plan : Table access full ecriture
ou
2
SELECT *
FROM ecriture
WHERE ecr_date IS NOT NULL ;
Selon explain plan : Table access full ecriture
Par contre l’index sera utilisé dans la requête suivante :
SELECT *
FROM ecriture
WHERE ecr_date = to_date(‘19970408’,’yyyymmdd’) ;
Selon explain plan : Table access by rowid ecriture index range scan ecr_idx2
Les prédicat avec négations vont empêcher d’utiliser l’index comme par exemple :
SELECT *
FROM ecriture
WHERE ecr_date != to_date(‘19970408’,’yyyymmdd’) ;
Selon explain plan : Table access full ecriture
Convertissez vos négations de la façon suivante :
AVANT
NOT >
NOT >=
NOT <
NOT <=
APRES
<=
<
>=
>
10. Oracle utilise un système de pointage pour les prédicats. Ils sont ordonnées du plus
efficace au moins efficace. On peut en déduire qu’une requête comme :
UPDATE ecriture
WHERE rowid=ecr_rec.rowid ;
sera plus rapide que la requête :
UPDATE ecriture
WHERE ecr_no=ecr_rec.ecr_no ;
1) rowid=constante
2) colonne indexée unique=constante
3) index entier concaténé unique=constante
3
4) index entier concaténé non unique=constante
5) index non unique=constante
6) index entier concaténé >= borne inférieure
7) index partiel concaténé
8) colonne indexée unique avec BETWEEN borne inférieure et borne supérieure
9) colonne indexée non unique avec BETWEEN borne inférieure et borne supérieure
10) colonne indexée unique <ou> constante
11) colonne indexée non unique
12) jointure seulement
13) max ou min d’une colonne indexée
14) order by index entier
15) recherche entière dans la table
On remarque qu’un index unique est plus efficient qu’un index non unique car la clé a une
seule entrée.
11. Un index concaténé contient 2 ou plusieurs colonnes. Il faut faire attention lors de l’usage
de ces index. Lorsque l’on crée un index concaténé, la colonne la plus utilisée doit être
spécifiée en premier.
Détruire l’index ecr_idx2 et créer l’index suivant :
CREATE INDEX ecr_idx3 ON ecriture (ecr_compte,ecr_date) ;
La requête suivante n’utilisera pas l’index car elle ne réfère pas a la partie de tête de
l’index :
SELECT *
FROM ecriture
WHERE ecr_date=to_date9`19970408’,’yyyymmdd’) ;
Selon explain plan : Table access full ecriture
La requête suivante utilisera l’index car elle réfère a la partie de tête de l’index :
SELECT *
FROM ecriture
WHERE ecr_compte=4000 ;
Selon explain plan : Table access by rowid ecriture index range scan ecr_idx3
12. L’index ne sera pas utilisé dans une clause ‘OR’ si :
• la requête contient une clause ‘connect by’
• la requête contient une jointure externe (outer join)
• l’optimiseur décide de ne pas utiliser d’index car cela n’améliorait pas la
performance de la requête.
4
13. Si le but d’une sous-requête est de vérifier l’existence d’une rangée alors il est préférable
d’utiliser la clause ‘WHERE EXISTS’. Vérifier avec explain plan.
CREATE TABLE compte (
cpt_no
NUMBER
NOT NULL,
cpt_desc CHAR(50)) ;
CREATE INDEX cpt_idx1 ON compte (cpt_no) ;
SELECT *
FROM ecriture e
WHERE e.ecr_compte IN
(SELECT c.cpt_no
FROM compte c
WHERE c.cpt_desc LIKE ‘compte%’) ;
Selon explain plan : Nested loops View Sort Join Table access full compte Table access
by rowid ecriture Index range scan ecr_idx3
SELECT *
FROM ecriture e
WHERE EXIST (SELECT 1
FROM compte c
WHERE c.cpt_no=e.ecr_compte
‘compte%’) ;
AND
c.cpt_desc
LIKE
Selon explain plan : Filter Table access full ecriture Table access by rowid compte index
range scan cpt_idx1
Si le nombre de rangées dans la table des écritures est plus élevé que dans la table des
comptes alors un ‘Table access full’ pour écriture sera désavantageux. Donc la prémière
requête semblerait la plus efficace puisqu’elle utilise l’index ecr_idx3 pour ecriture.
14. Vous devriez indexer les colonnes qui servent de jointure (Join) entre les tables. La Table
qui est la dernière dans la clause FROM devient la table dominante (driving table) si les
prédicats sont égaux selon le système de pointage.
Si aucun index n’a été créé, alors Oracle devra effectuer un sort Merge Join qui créera une
table temporaire triée pour chaque table dans la jointure. Ces tables temporaires seront
fusionnées selon les prédicats de la jointure.
Par exemple, la requête suivante sera longue a s’exécuter puisqu’elle exige une lecture
complète des tables.
SELECT e.ecr_type, c.cpt_nom
FROM ecriture e, compte c
WHERE e.ecr_compte=c.cpt_no AND e.ecr_type !=’J’;
5
En premier la table compte est mise dans une table temporaire et triée. Ceci exige une
lecture complète de la table compte.
SELECT cpt_no
FROM compte
ORDER BY cpt_no
Après, la table ecriture est examinée et pour chaque rangée, l’information est mise dans
la table temporaire et triée. Ceci exige une lecture complète de la table ecriture.
SELECT ect_type, ecr_compte
FROM ecriture
WHERE ecr_type !=’J’
ORDER BY ecr_compte
Et les deux tables sont fusionnées selon le prédicat de la jointure ecr_compte=cpt_no.
Pour chaque rangée de la table compte (driving table), on doit aller chercher la rangée
correspondante dans la table ecriture. Comme la table ecriture n’est pas indexée, on devra
faire une lecture complète de la table pour trouver le numéro de compte correspondant.
Selon explain plan : Merge Join Sort join Table access full compte Sort join Table access
full ecriture.
15. La table qui n’est pas indexée dans un ‘join’ devient la table dominante. (driving table)
DROP INDEX cpt_idx1 ;
selon la requête suivante :
SELECT e.ecr_type, c.cpt_nom
FROM ecriture e, compte c
WHERE e.ecr_compte=c.cpt_no AND e.ecr_type !=’J’ ;
Selon explain plan : Nested loops Table access full compte Table access by rowid ecriture
Index range scan ecr_idx3
S’il y a un index sur la colonne ecr_compte, alors compte devient la ‘driving table’. Donc
pour chaque rangée de compte, Oracle ira chercher l’information correspondante
(ecr_compte=cpt_no) dans la table ecriture avec l’index sur ecr_compte au lieu de faire
peut-être une lecture complète de la table ecriture.
16. Placer la table avec le plus petit nombre de rangées qualifiées la dernière dans la clause
‘FROM’.
6

Documents pareils