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

tudeetoptimisationdesrequtes

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

Techniquesdoptimisation tandarddesrequtes 155 s

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 MyISAM Temps Key_read_requests InnoDB Temps Reads Jointure 12,89s 5242448 Sous-requte 13,53s 5243442 0,01s 158

Avec index Jointure Sous-requte 13,53s 5243442 Sous-requte 13,51s 2606480

Jointure 3,48s 3700

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 Oracle Temps Consistent Gets Jointure 6,15s 8336 Sous-requte 8,75s 4891735 0,01s 807 Avec index Jointure Sous-requte 0,60s 12175

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

156

tudeetoptimisationdesrequtes

Axe 2

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 MS SQL Server Temps Estimated Cost Jointure 0,625s 35,27 Avec index Sous-requte 0,046s 60,44

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

Techniquesdoptimisation tandarddesrequtes 157 s

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 Temps Key_read_requests Anti-jointure 10,11s 29850364 Sous-requte 10,11s 29850364

Avec index Anti-jointure 0,02s 2974 Sous-requte 0,02s 2974

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


InnoDB Temps Reads Anti-jointure 24,31s 2976 Sous-requte 25,34s 5948

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 Oracle Temps Consistent Gets Anti-jointure 0,340s 6976 Sous-requte 2,282s 113419 0,375s 5522 Avec index Anti-jointure Sous-requte 0,031s 8909

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


Sans index MS SQL Server Temps Estimated Cost Anti-jointure 2,437s 47,49 Avec index Anti-jointure 0,079s 0,54

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

158

tudeetoptimisationdesrequtes

Axe 2

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 Temps Key_read_requests InnoDB Temps Reads Oracle Temps Consistent Gets

Exists 0,020s 11915 Exists 22,940s 8900 Exists 0,031s 8909

Count 1,110s 166782 Count 29,590s 2606477 Count 0,031s 8909

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

Chapitre 6

Exists 0,062s

Techniquesdoptimisation tandarddesrequtes 159 s

MS SQL Server Temps

Count 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
IN

Une sous-requte utilisant loprateur requte utilisant loprateur EXISTS.

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 Temps Key_read_requests InnoDB Temps Reads Oracle Temps Consistent Gets

In 14,010s 5243442 In 7,420s 2606480 In 8,594s 4891735

Exists 15,340s 5242448 Exists 8,200s 5212684 Exists 8,594s 4891735 Forage index 4,234s 2462067

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

160

tudeetoptimisationdesrequtes

Axe 2

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 0,625s Forage index 0,046s
WITH

Comme sous Oracle, nous devons forcer lutilisation de lindex avec le hint
(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

Techniquesdoptimisation tandarddesrequtes 161 s

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 MyISAM Temps Reads Jointure 5,890s 4873397 Petite requte InnoDB Temps Reads Jointure 0,670s 1001449 Expression sous-requte 0,670s 1000175 Expression sous-requte 0,060s 3316 Expression sous-requte 0,109s Expression sous-requte 0,160s 736 Grosse requte Jointure 6,810s 4873397 Expression sous-requte 1,950s 564513 Expression sous-requte 4,030s 1632143 Expression sous-requte 1,51s 117464 Expression sous-requte 0,953s

Grosse requte Jointure 3,580s 2264285

Petite requte Oracle Temps Consistent Gets Jointure 0,390s 11490

Grosse requte Jointure 1,48s 11490

Petite requte MS SQL Server Temps Jointure 0,125s

Grosse requte Jointure 0,812s

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro

162

tudeetoptimisationdesrequtes

Axe 2

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 Temps Reads InnoDB Temps Reads Oracle Temps Consistent Gets MS SQL Server Temps

Having 0,080s 91109 Having 0,090s 91132 Having 0,047s 624 Having 0,046s

Where 0,030s 45950 Where 0,050s 45960 Where 0,015s 624 Where 0,046s

2010 Pearson Education France Optimisation des bases de donnes Laurent Navarro