Vous êtes sur la page 1sur 10

6

Techniques doptimisation
standard des requtes
Loptimisation du SQL est un point trs dlicat car elle ncessite de pouvoir modifier
lapplicatif en veillant ne pas introduire de bogues.

6.1

Rcriture des requtes

Lutilisation dobjets doptimisation est un lment qui, de manire gnrale, amliore les performances trs significativement. Cependant, la faon dcrire votre
requte peut, elle aussi, dans certains cas, avoir un impact trs significatif.
Ltape de transformation de requte (voir ci-aprs) est de plus en plus performante
sur Oracle et SQL Server. Elle donne de trs bons rsultats, ce qui rend inutiles
certaines recommandations dans le cas gnral. Cependant, sur MySQL ou sur des
requtes compliques, o on peut considrer que le SGBDR narriverait pas faire
certaines transformations tout seul, lutilisation des critures les plus performantes
permettra damliorer les rsultats.
Nous allons faire quelques comparaisons dcritures qui intgreront parfois un hint
(voir Chapitre7, section7.1, "Utilisation des hints sous Oracle") afin dempcher
certaines transformations.
De faon gnrale, il faut privilgier les jointures. Le principe du modle relationnel
tant que, lors de linterrogation, des jointures seront faites, on peut supposer que les
diteurs de SGBDR ont concentr leurs efforts l-dessus. Cependant, sous MySQL
avec le moteur MyISAM, les sous-requtes sont souvent plus performantes.

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

154

tude et optimisation desrequtes

Axe 2

6.1.1 Transformation de requtes

Ltape de transformation de requtes (Query Transformation) est partie intgrante du


processus de traitement des requtes. Cest une des fonctions de loptimiseur CBO.
Cette tape consiste transformer la requte soumise en une requte quivalente
afin de la rendre plus performante. Cela va, au-del, de choisir le meilleur chemin
dexcution. La transformation de requtes peut, par exemple, dcider de transformer une sous-requte en jointure. Vous pouvez influer sur cette tape au moyen des
hints, mais cest rarement ncessaire.
Les principales transformations effectues par le CBO sont:

Subquery unesting. Cette transformation consiste transformer des sousrequtes en jointures.

Suppression dlments inutiles (certaines jointures, des colonnes slectionnes


dans les sous-requtes).

Predicate push. Cette transformation consiste dupliquer certains prdicats


dans les sous-vues et les sous-requtes.

View merging. Intgre lexcution des vues la requte principale.

Query Rewriting. Utilisation des vues matrialises (voir Chapitre5, section5.3.5,


"Les vues matrialises").

Or Expansion. Transforme des conditions OR en plusieurs requtes fusionnes


par une sorte d"Union ALL".

Il ny a rien de particulier faire pour bnficier du Query Rewriting. Connatre


lexistence de ce mcanisme vous aidera comprendre pourquoi il arrive parfois
quun plan dexcution ne ressemble vraiment pas votre requte. Cette fonction est
plus volue sous Oracle et SQL Server que sous MySQL.
INFO
Au cours de ce chapitre, nous prsentons des variations de notation qui peuvent avoir un
effet sur les performances. L'optimiseur tant assez performant, il transforme automatiquement les requtes soumises en la version la plus performante. Nous recourrons des hints
pour empcher ces transformations afin de comparer les notations. L'utilisation de hint n'est
nullement une recommandation, nous le mentionnons titre indicatif de faon que, si vous
excutez ces requtes, vous puissiez reproduire les mmes rsultats que ceux prsents ici.
Comme cela sera expliqu au Chapitre7, section7.1, "Utilisation des hints sous Oracle", il
faut les utiliser en dernier recours et seulement en toute connaissance de cause.

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

Chapitre 6

Techniques doptimisation standard des requtes 155

6.1.2 IN versus jointure

Selon que lcriture dune requte se fasse avec une sous-requte et loprateur IN
ou avec une jointure, limpact sera diffrent. Pour forcer le parcours des tables,
et non seulement celui des index, nous utilisons dans la requte des champs non
indexs (Quantit et Editeur).
Listing6.1: Requte utilisant une jointure
select sum(CL.quantite) from cmd_lignes CL,livres L
where CL.nolivre = L.nolivre and L.editeur='Pearson'

Listing6.2: Requte utilisant une sous-requte


select sum(quantite) from cmd_lignes
where nolivre in (select nolivre from livres where editeur='Pearson')

Nous allons tester ces requtes avec et sans index sur la colonne Nolivre de la table
cmd_lignes.
create index IS_cmd_lignes on cmd_lignes(nolivre);

Sans index

Avec index

MyISAM

Jointure

Sous-requte

Jointure

Sous-requte

Temps

12,89s

13,53s

0,01s

13,53s

5242448

5243442

158

5243442

Jointure

Sous-requte

Temps

3,48s

13,51s

Reads

3700

2606480

Key_read_requests
InnoDB

InnoDB crant automatiquement un index dans la table fille de la Foreign Key, il y


a forcement un index sur la colonne Nolivre de la table cmd_lignes. La cration de
lindex est donc sans effets.
Sans index

Avec index

Oracle

Jointure

Sous-requte

Jointure

Sous-requte

Temps

6,15s

8,75s

0,01s

0,60s

Consistent Gets

8336

4891735

807

12175

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

156

Axe 2

tude et optimisation desrequtes

Pour viter la transformation de la version utilisant une sous-requte en version


jointure par le mcanisme de transformation de requte, nous devons utiliser le hint
/*+no_unnest*/ et pour forcer lusage de lindex, le hint /*+ index (CL IS_cmd_
lignes)*/. Si nous ne mettons aucun hint, loptimiseur choisit systmatiquement de
faire une jointure sans utiliser lindex.
Sans index

Avec index

MS SQL Server

Jointure

Sous-requte

Temps

0,625s

0,046s

35,27

60,44

Estimated Cost

Afin de comparer les rsultats entre les bases, nous dsactivons le paralllisme sous
SQL Server au moyen du code option (MAXDOP 1).
Nous navons pas trouv de combinaisons de hints permettant de forcer lutilisation dune sous-requte tant quil ny a pas dindex. Une fois lindex plac, nous
avons d contraindre son utilisation avec le hint WITH (INDEX(IS_CMD_LIGNES)). En
revanche, nous navons pas russi forcer une jointure pertinente utilisant cet index.
Conclusion : Oracle et SQL Server, grce leur capacit de transformation de
requte volue, excutent les requtes de la mme faon quelle que soit la notation
employe, si aucun hint nest utilis.
Lorsque la transformation nest pas effectue, on constate que la solution base de
jointure est gnralement plus performante.
Au cas o le SGBDR narriverait pas faire la transformation, il est prfrable de
prendre lhabitude dcrire des jointures plutt que des sous-requtes.
6.1.3 Sous-requtes versus anti-jointures

Une anti-jointure est une jointure ouverte pour laquelle vous ne slectionnez que les
valeurs non jointes.
Les requtes ci-dessous, permettent de slectionner les livres qui nont jamais t
commands.
Listing6.3: Version Exists
select * from livres L
where not exists (select 1 from cmd_lignes CL where CL.NOLIVRE=L.NOLIVRE)

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

Chapitre 6

Techniques doptimisation standard des requtes 157

Listing6.4: Version anti-jointure


select L.* from livres L
left outer join cmd_lignes CL on CL.NOLIVRE=L.NOLIVRE
where cl.nolivre is null

Nous allons tester ces requtes avec et sans index sur la colonne Nolivre de la table
cmd_lignes.
create index IS_cmd_lignes on cmd_lignes(nolivre);

Sans index
MyISAM

Avec index

Anti-jointure

Sous-requte

Anti-jointure

Sous-requte

10,11s

10,11s

0,02s

0,02s

29850364

29850364

2974

2974

Anti-jointure

Sous-requte

Temps

24,31s

25,34s

Reads

2976

5948

Temps
Key_read_requests

L'anti-jointure est transforme en sous-requte.


InnoDB

InnoDB crant automatiquement un index dans la table fille de la Foreign Key, il y


a forcment un index sur la colonne Nolivre de la table cmd_lignes. La cration de
lindex est donc sans effets.
Sans index

Avec index

Oracle

Anti-jointure

Sous-requte

Anti-jointure

Sous-requte

Temps

0,340s

2,282s

0,375s

0,031s

6976

113419

5522

8909

Consistent Gets

Nous utilisons le hint /*+NO_QUERY_TRANSFORMATION*/ pour empcher la transformation de la sous-requte en anti-jointure.

MS SQL Server
Temps
Estimated Cost

Sans index

Avec index

Anti-jointure

Anti-jointure

2,437s

0,079s

47,49

0,54

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

158

Axe 2

tude et optimisation desrequtes

Quelle que soit la notation utilise, la requte est transforme en anti-jointure.


Les rsultats sont mitigs sous Oracle. Malheureusement, ltape de transformation
de requte nopte pas toujours pour la meilleure option. Loptimiseur choisit systmatiquement lanti-jointure qui est, en effet, gnralement la meilleure option. Ici, la
table livre est bien plus petite que la table cmd_lignes, dans ce cas, si un index est
prsent sur la table fille de la Nested Loop, la solution utilisant une sous-requte est
la meilleure. Il faut donc utiliser un hint pour forcer ce choix. Sur les autres SGBDR,
ltape de transformation ne laisse pas le choix, les deux critures se valent.
6.1.4 Exists versus Count

Les requtes effectuant un test dexistence sont quivalentes un comptage gal 0.


Listing6.5: Version Exists
select * from livres L
where not exists (select 1 from cmd_lignes CL where CL.NOLIVRE=L.NOLIVRE)

Listing6.6: Version 0=count(*)


select * from livres L
where 0 = (select count(*) from cmd_lignes CL where CL.NOLIVRE=L.NOLIVRE)

Nous allons tester ces requtes avec un index sur la colonne Nolivre de la table
cmd_lignes.
create index IS_cmd_lignes on cmd_lignes(nolivre);

MyISAM

Exists

Count

Temps

0,020s

1,110s

Key_read_requests

11915

166782

InnoDB

Exists

Count

Temps

22,940s

29,590s

Reads

8900

2606477

Oracle

Exists

Count

Temps

0,031s

0,031s

Consistent Gets

8909

8909

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

Chapitre 6

Techniques doptimisation standard des requtes 159

MS SQL Server

Exists

Count

Temps

0,062s

0,062s

Oracle et SQL Server considrent ces deux critures quivalentes, alors que MySQL
les voit bien diffrentes. On suppose que dans la version utilisant COUNT(*), il
dnombre toutes les occurrences pour tester lgalit 0, alors que la version utilisant EXISTS sarrte la premire occurrence trouve.
6.1.5 Exists versus IN

Une sous-requte utilisant loprateur


requte utilisant loprateur EXISTS.

IN

peut facilement tre convertie en sous-

Nous allons tester ces requtes avec un index sur la colonne Nolivre de la table
cmd_lignes.
create index IS_cmd_lignes on cmd_lignes(nolivre);

Listing6.7: Version sous-requte IN


select sum(quantite) from cmd_lignes
where nolivre in (select nolivre from livres where editeur='Pearson')

Listing6.8: Version sous-requte Exists


select sum(quantite) from cmd_lignes cl
where exists (select nolivre from livres L
where editeur='Pearson' and CL.nolivre=L.nolivre )

MyISAM

In

Exists

Temps

14,010s

15,340s

Key_read_requests

5243442

5242448

InnoDB

In

Exists

Temps

7,420s

8,200s

Reads

2606480

5212684

Oracle

In

Exists

Forage index

Temps

8,594s

8,594s

4,234s

Consistent Gets

4891735

4891735

2462067

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

160

Axe 2

tude et optimisation desrequtes

Loptimiseur Oracle dcide de ne pas utiliser lindex. Si nous forons son usage
laide du hint /*+ index(cl is_cmd_lignes)*/, nous notons une amlioration des
performances.
Nous utilisons le hint /*+NO_QUERY_TRANSFORMATION*/ pour empcher la transformation des sous-requtes en anti-jointures qui ont un temps de rponse de lordre
0,34seconde.
MS SQL Server
Temps

In ou Exists

Forage index

0,625s

0,046s

Comme sous Oracle, nous devons forcer lutilisation de lindex avec le hint

WITH

(INDEX(IS_CMD_LIGNES)).

Oracle et SQL Server considrent ces deux critures quivalentes, alors que MySQL
les voit diffrentes, lavantage tant loprateur IN sur les deux moteurs de MySQL.
6.1.6 Clause Exists * versus constante

On enseigne, depuis des annes, que lorsquon emploie des sous-requtes corrles
utilisant loprateur EXISTS, il faut retourner une constante (numrique ou texte)
dans la clause SELECT de la sous-requte. Est-ce seulement pour des raisons de clart
ou aussi de performances?
Listing6.9: Version Exists constante
select * from livres L
where not exists (select 1 from cmd_lignes CL where CL.NOLIVRE=L.NOLIVRE);

Listing6.10: Version Exists *


select * from livres L
where not exists (select * from cmd_lignes CL where CL.NOLIVRE=L.NOLIVRE);

Nous ne documentons pas le dtail des rsultats. On constate que les colonnes retournes dans la sous-requte nont aucun impact sur les performances, alors quelles en
avaient sur danciennes versions dOracle.
Cette pratique de mettre une constante garde cependant tout son sens du point de
vue de la comprhension. Parfois, certains dveloppeurs mettent ici un champ particulier laissant ainsi croire quil y aurait une sorte de lien possible avec ce champ.

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

Chapitre 6

Techniques doptimisation standard des requtes 161

6.1.7 Expressions sous requtes


Listing6.11: Requte utilisant une jointure
select c.nocmd,datecommande ,sum(montant) Total
from cmd c,cmd_lignes cl where cl.nocmd=c.nocmd and datecommande<to_
date('2004-04-18','yyyy-mm-dd')
group by c.nocmd, datecommande

Listing6.12: Requte utilisant une expression sous-requte


select nocmd,datecommande
,(select sum(montant) from cmd_lignes where nocmd=c.nocmd) Total
from cmd c where datecommande<to_date('2004-04-18','yyyy-mm-dd')

Nous choisissons ici une requte qui empche dappliquer le prdicat de la table
cmd directement la table cmd_lignes et qui ne retourne que 173lignes. En changeant la date, nous testons une autre version qui ramne beaucoup plus de lignes
(132141).
Petite requte

Grosse requte

MyISAM

Jointure

Expression
sous-requte

Jointure

Expression
sous-requte

Temps

5,890s

0,160s

6,810s

1,950s

Reads

4873397

736

4873397

564513

Petite requte

Grosse requte

InnoDB

Jointure

Expression
sous-requte

Jointure

Expression
sous-requte

Temps

0,670s

0,670s

3,580s

4,030s

Reads

1001449

1000175

2264285

1632143

Petite requte

Grosse requte

Oracle

Jointure

Expression
sous-requte

Jointure

Expression
sous-requte

Temps

0,390s

0,060s

1,48s

1,51s

Consistent Gets

11490

3316

11490

117464

Petite requte

Grosse requte

MS SQL Server

Jointure

Expression
sous-requte

Jointure

Expression
sous-requte

Temps

0,125s

0,109s

0,812s

0,953s

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

162

Axe 2

tude et optimisation desrequtes

Avec des requtes ramenant peu de lignes, l'avantage est aux expressions sousrequtes. Avec des requtes ramenant beaucoup de lignes, l'avantage passe la version jointure, sauf pour le moteur MyISAM qui a l'air plus l'aise avec l'utilisation
systmatique d'expressions sous-requtes. Seul l'optimiseur de SQL Server adapte
automatiquement son plan d'excution la solution la plus efficace, indpendamment de l'criture. Nous avons donc utilis les hints loop join et merge join pour
effectuer ces tests.
6.1.8 Agrgats: Having versus Where

Quand cest possible, les conditions doivent tre places dans la clause WHERE plutt
que dans la clause HAVING qui est value aprs les oprations dagrgation. Cela
permet de profiter dventuels index et rduit le volume traiter durant lopration
dagrgation.
Listing6.13: Requte utilisant Having (incorrecte)
select pays,ville,count(*) from clients t
group by pays,ville
having pays='France'

Listing6.14: Requte utilisant Where


select pays,ville,count(*) from clients t
where t.pays='France'
group by pays,ville

MyISAM

Having

Where

Temps

0,080s

0,030s

Reads

91109

45950

InnoDB

Having

Where

Temps

0,090s

0,050s

Reads

91132

45960

Oracle

Having

Where

Temps

0,047s

0,015s

Consistent Gets

624

624

MS SQL Server

Having

Where

Temps

0,046s

0,046s

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

Vous aimerez peut-être aussi