Table des matières

Ancien TME Jointure 2017

En 2018 aller vers le sujet suivant: TME Jointure

L'objectif de ce TME est de comprendre l'optimisation des requêtes qui contiennent des jointures.

Le TME dure 2 séances. Faire l'ex1 pendant la première séance et l'ex2 pendant la deuxième. L'ex3 est facultatif.

Ne pas oublier de consulter les questions fréquentes en bas de cette page

Préparation du TME

Lire l'énoncé de l'exercice dans le poly de TD: Exercice 3: Club de joueurs

commandedescription
cd mon_répertoire aller dans votre répertoire de travail
tar zxvf /Infos/bd/public/tmeJointure.tgz installer l'archive dans votre répertoire principal
cd tmeJointure aller dans le répertoire du TME
emacs tmeJointure.sql & éditer le fichier à compléter pendant le TME
Alt-x my/sql-oracle ou Atl-x sql-oracle se connecter à Oracle. Voir ConnexionOracle
aller sur le paragraphe contenant @base3 et faire Ctrl-C Ctrl-C créer vos tables J, C, F, les index et les statistiques nécessaires à l'optimisation basée sur le coût

Les tables d'un club de sport sont :

qu'on abrège dans ce TME en :

Les index existants s'appellent: I_J_CNUM pour J(cnum), I_J_SALAIRE pour J(salaire), I_C_CNUM pour C(cnum), I_C_DVISION pour C(division) et I_F_CNUM pour F(cnum)

Il y a aussi la table BigJoueur(licence, cnum, salaire, sport, profil) dont l'attribut profil contient 4000 caratères.

Pour voir les plans proposés par le SGBD et leur coût :

   SET autotrace trace EXPLAIN

Exercice préliminaire

Combien de n-uplets ont chacune des relations J, C, F et BigJoueur?

SET autotrace trace EXPLAIN
SELECT * FROM J;

Quel est le coût d'accès à chaque table ?

le coût d'accès à un plan se lit dans la colonne Cost, pour la racine de l'arbre représentant le plan (première ligne).

Exercice 1 : Jointure entre 2 relations

On considère la requête R1.

   SELECT J.licence, C.nom
   FROM J, C
   WHERE J.cnum = C.cnum
   AND J.salaire > 10;

Ecrire en français ce que retourne la requête R1

Question 1) Jointure par hachage

Afficher le plan de la requête R1 et constater qu'il s'agit d'une jointure par hachage. Dessiner l'arbre du plan d'exécution proposé par le SGBD. Numéroter les opérateurs de 0 à 3 (valeur du champ Id).

La jointure par hachage entre deux tables s'effectue en deux étapes :

a) Quel est le coût du plan ? Est-il égal à la somme des coûts du parcours séquentiel de J et C ?

Observer que le coût est le même lorsqu'on inverse l'ordre de lecture des relations : lire J avant C ou lire C avant J. Remarque, l'ordre est fixé par la clause FROM quand on utilise la directive ordered.

   SELECT /*+ ordered */ J.licence, C.nom
   FROM J, C 
   WHERE J.cnum = C.cnum
   AND J.salaire > 10;
   SELECT /*+ ordered */ J.licence, C.nom
   FROM C, J 
   WHERE J.cnum = C.cnum
   AND J.salaire > 10;

b) On remplace la table Joueur par une table plus grande 'BigJoueur'. On étudie la jointure par hachage entre les tables C et BigJoueur. Observer que la jointure dans l'ordre C, BigJoueur a un coût plus petit que celle dans l'ordre BigJoueur, C

SELECT /*+ ordered */ *
FROM C, BigJoueur j
WHERE j.cnum = c.cnum;
SELECT /*+ ordered */ *
FROM BigJoueur j, C
WHERE j.cnum = c.cnum;

Le coût total de la jointure est la somme du coûts de lecture des tables plus le coût de constuire la HashMap (cf. colonne TmpSpc). En déduire le coût de construction de la HashMap.

Enlever maintenant la directive ordered et observer que le SGBD choisit de lire C avant BigJoueur car cela est moins coûteux.

c) Observer que lorsque le prédicat de sélection sur le salaire n'est pas très sélectif, le plan est toujours le même. Répondre en remplaçant sal>10 par sal>100, …, sal>1000.

d) On étudie maintenant le cas où le prédicat de sélection est très sélectif. Expliquer le plan de la requête suivante. Détailler comment chaque valeur de la colonne Cost est calculée.

SELECT J.licence, C.nom
FROM C, j
WHERE J.cnum = C.cnum
AND salaire < 10050;
| Id  | Operation		     | Name	   | Rows  | Bytes | Cost (%CPU)| Time	   |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	     |		   |	50 |  1300 |	60   (2)| 00:00:01 |
|*  1 |  HASH JOIN		     |		   |	50 |  1300 |	60   (2)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| J	   |	50 |   700 |	52   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN	     | I_J_SALAIRE |	50 |	   |	 2   (0)| 00:00:01 |
|   4 |   TABLE ACCESS FULL	     | C	   |  5000 | 60000 |	 7   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information:
   1 - access("J"."CNUM"="C"."CNUM")
   3 - access("SALAIRE"<10050)

Question 2) Jointure par boucles imbriquées avec index sur l'attribut de jointure

a) Expliquer le plan de la requête R2 suivante. Détailler comment chaque valeur de la colonne Cost est calculée.

SELECT J.licence, C.nom
FROM C, j
WHERE J.cnum = C.cnum
AND salaire < 10006;
| Id  | Operation		      | Name	    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	      | 	    |	  6 |	156 |	 14   (0)| 00:00:01 |
|   1 |  NESTED LOOPS		      | 	    |	    |	    |		 |	    |
|   2 |   NESTED LOOPS		      | 	    |	  6 |	156 |	 14   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID| J	    |	  6 |	 84 |	  8   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN	      | I_J_SALAIRE |	  6 |	    |	  2   (0)| 00:00:01 |
|*  5 |    INDEX UNIQUE SCAN	      | I_C_CNUM    |	  1 |	    |	  0   (0)| 00:00:01 |
|   6 |   TABLE ACCESS BY INDEX ROWID | C	    |	  1 |	 12 |	  1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information :
   4 - access("SALAIRE"<10006)
   5 - access("J"."CNUM"="C"."CNUM")

b) On veut utiliser l'index sur l'attribut de jointure cnum pour évaluer R1. Pour cela, on utilise une jointure par boucles imbriquées. La directive USE_NL indique que la jointure doit être traitée par boucles imbriquées.

   SELECT /*+ USE_NL(J,C) */ J.licence, C.nom
   FROM J, C
   WHERE J.cnum = C.cnum
   AND J.salaire > 10;

Dessiner l'arbre obtenu. Quelle table est parcourue séquentiellement une seule fois ? L'index choisi sur cnum est-il celui de la table C ou J ?

Quel est le coût de ce plan ? Expliquer comment il est obtenu à partir des feuilles. Pourquoi est-il très supérieur au coût de la jointure par hachage pour laquelle on lit J et C une seule fois?

Inversement, on évalue R1 en utilisant l'index sur cnum de la table J en fixant l'ordre (on impose d'itérer sur C avant J).

   SELECT /*+ USE_NL(J,C) ordered*/ J.licence, C.nom
   FROM C,J
   WHERE J.cnum = C.cnum
   AND J.salaire > 10;

Dessiner l'arbre obtenu. Expliquer pourquoi le coût est plus grand. Ce plan fait combien d'accès à l'index ?

c) Jointure et sélection avec/sans index

Comparer les plans obtenus pour R2 avec ou sans index

   SELECT /*+ no_index(J I_J_salaire) */ J.licence, C.nom
   FROM J, C
   WHERE J.cnum = C.cnum
   AND J.salaire <10006;

Exercice 2. Jointure entre 3 relations (FACULTATIF)

Soit la requête R3 :

   SELECT /*+ use_nl(J,C,F) */ c.nom, f.budget 
   FROM J, C, F
   WHERE J.cnum = C.cnum AND C.cnum = F.cnum
   AND c.division=1 AND J.salaire > 59000  
   AND j.sport = 'sport1';

La directive USE_NL permet d'imposer des jointures par boucles imbriquées afin d'utiliser les index sur l'attribut de jointure cnum de C et J et F.

On sait (cf. cours) qu'il y a 6 ordres de jointure possibles pour évaluer cette requête.

a) Compléter le tableau des ordres possibles :

ordre coût
J, C, F 88
F, J, C

D'après les résultats du tableau, dessiner l'arbre correspondant à l'ordre de moindre coût. Quels index sont utilisés ?

Pourquoi l'ordre J, F, C n'utilise pas l'index sur F.cnum? Observer que le SGBD propose de faire un produit cartésien entre J et F au lieu d'une jointure. Remplacer le prédicat de jointure J.cnum = C.cnum par le prédicat équivalent J.cnum = F.cnum, déduit par transitivité, et observer que cela permet d'utiliser l'index sur F.cnum pour la jointure de J avec F. Que peut-on conclure concernant la capacité du SGBD à explorer tous les ordres de jointure équivalents ?

b) Modifier R3 en une requête R4 de jointure et de sélection avec index : modifier seulement la valeur du salaire (59000) pour que le plan obtenu choisisse l'index sur le salaire. Quel serait le coût de R4 sans utiliser l'index sur le salaire ?

c) On force les index sur les attributs “J.salaire” et “C.division” pour évaluer R3

   SELECT /*+ use_nl(J,C,F) index(C I_C_division) index(J I_J_salaire)*/ c.nom, f.budget 
   FROM J, C, F
   WHERE J.cnum = C.cnum AND C.cnum = F.cnum
   AND c.division=1 AND J.salaire > 59000
   AND j.sport = 'sport1'; 

Dessiner le plan obtenu. Quel est l'inconvénient d'utiliser l'index sur l'attribut division ?

Exercice 3. Forme des arbres de jointure (FACULTATIF)

a) Soit R4:

   SELECT /*+ use_nl(J,C,F) */ C.nom, F.budget, J.licence
   FROM J, C, F
   WHERE J.cnum = C.cnum AND C.cnum = F.cnum   ;

Dessiner l'arbre obtenu. Justifier l'odre choisit. Pourquoi lire F en premier et J en dernier ?

b) Compléter la clause WHERE de R4 pour obtenir R5 de telle sorte que l'ordre choisi par le SGBD soit C,F,J

c) Ajouter une 4ème table Z et proposer une requête de jointure entre les 4 tables de telle sorte que le plan de moindre coût ne soit pas un arbre linéaire à gauche. C'est à dire que le coût de P1 soit inférieur à P2 avec :

Questions fréquentes

Le plan

|   1 |    NESTED LOOPS                           
|*  2 |     TABLE ACCESS FULL           | J | 
|*  3 |     TABLE ACCESS BY INDEX ROWID | C |
|*  4 |      INDEX UNIQUE SCAN          | I_C_CNUM |

est indentique au plan

|   1 |  NESTED LOOPS                             
|   2 |   NESTED LOOPS                                   
|*  3 |    TABLE ACCESS FULL           | J |
|*  4 |    INDEX UNIQUE SCAN           | I_C_CNUM |
|   5 |   TABLE ACCESS BY INDEX ROWID  | C |

Dans les deux plans, la jointure est la même. Lire Joueur pour itérer: pour chaque n-uplet de Joueur, accéder à l'index sur (C.cnum) pour connaitre l'identifiant du nuplet du Club. Puis accéder à la table Club pour lire le n-uplet correspondant. (pour plus d'explications voir Lien

Divers

Documentation sur l'optimiseur d'Oracle

Aller vers BDR