site:enseignement:master:bdr:tmeindex
Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
site:enseignement:master:bdr:tmeindex [25/01/2017 12:44] – [Exercice 5. Coût d'une sélection pour le choix d'un index] hubert | site:enseignement:master:bdr:tmeindex [20/02/2019 15:32] (Version actuelle) – hubert | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
{{indexmenu_n> | {{indexmenu_n> | ||
- | ====== TME Index====== | ||
+ | /** TODO | ||
+ | * remplacer | ||
+ | **/ | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ====== TME Index ====== | ||
L' | L' | ||
- | * Séance 1 : Exercices 1, 2 et 3 | + | * Séance 1 : Exercices 1, 2, 3 |
* Séance 2 : Exercices 4 et suivants | * Séance 2 : Exercices 4 et suivants | ||
+ | |||
+ | |||
===== Préparation du TME ===== | ===== Préparation du TME ===== | ||
+ | |||
+ | Lire la doc sur la [[site: | ||
^commande^description^ | ^commande^description^ | ||
Ligne 14: | Ligne 25: | ||
| emacs tmeIndex.sql & | éditer le fichier à compléter pendant le TME| | | emacs tmeIndex.sql & | éditer le fichier à compléter pendant le TME| | ||
| **Alt-x** my/ | | **Alt-x** my/ | ||
- | | aller sur la ligne contenant @annuaire et faire Ctrl-C Ctrl-C | définir la table Annuaire | + | | aller sur la ligne contenant @annuaire et faire Ctrl-C Ctrl-C | définir la table Annuaire et un synonyme |
- | La table **Annuaire**(nom, | ||
- | * L'âge va de 1 à 100 | ||
- | * Les codes postaux (cp) sont des multiples de 100 et sont compris entre 1000 et 100 900. Il y a 1000 valeurs distinctes. | ||
- | * Le numéro de téléphone est unique ; c'est une chaîne de 10 chiffres commençant par 0. | ||
- | * Il y a 90 prénoms et 100 noms différents. | ||
- | * Le profil est une chaine de 1500 caractères. | ||
- | La table **<fc #ff0000>BigAnnuaire</ | + | Dans ce TME on interroge les deux tables Annuaire et BigAnnuaire. |
+ | ^Table^Nbre de tuples^Taille | ||
+ | |Annuaire|2000| 3846 | | ||
+ | |BigAnnuaire |220 000| 3846 | | ||
- | La table **<fc # | + | Les schémas des deux tables sont indentiques et contiennent les attributs suivants : |
- | Lire la section //Questions fréquentes// | + | ^Attribut^Valeurs distinctes^Domaine^Type^Index^ |
+ | |âge|100|[1-100]| NUMBER(3)|IndexAge| | ||
+ | |cp|1000|[1000, | ||
+ | |nom|100|-|VARCHAR2(30)|-| | ||
+ | |prénom|90|-|VARCHAR2(30)|-| | ||
+ | |tel|100 000 pour BigAnnuaire|-|VARCHAR2(10)|-| | ||
+ | |profil|90 000 pour BigAnnuaire|-|VARCHAR2(4000)|-| | ||
+ | Les deux tables sont indexées : **IndexAge** sur l' | ||
+ | /* | ||
+ | Dans le poly de TD, **<fc # | ||
+ | */ | ||
+ | |||
+ | Lire la section [[# | ||
- | ===== Ex1: Requêtes avec prédicat de sélection ===== | ||
On étudie des requêtes de sélection sur 1 ou 2 attributs. Il y a trois types de prédicats de sélection : l' | On étudie des requêtes de sélection sur 1 ou 2 attributs. Il y a trois types de prédicats de sélection : l' | ||
- | Les requêtes seront posées sur la table <fc #ff0000>**BigAnnuaireSimple ou BigAnnuaire.**</ | + | |
+ | Le SGBD transforme une requête en un //plan// avant de l' | ||
+ | Pour **afficher** le plan d'une requête, commencer chaque requête par | ||
+ | <code ascii> | ||
+ | | ||
+ | </ | ||
+ | puis terminer chaque requête par | ||
+ | @p3 | ||
+ | |||
+ | |||
+ | Remarque1 : pour régler l' | ||
+ | |||
+ | |||
+ | Remarque2 : **Problème d' | ||
+ | |||
+ | <showif isloggedin> | ||
+ | <fs x-large>< | ||
+ | </ | ||
+ | |||
+ | ===== Exercice préliminaire. Statistiques sur les tables ===== | ||
+ | |||
+ | Pour déterminer le plan d'une requête, le SGBD s' | ||
+ | Les statistiques sont, entre autres, la cardinalité d'une table et le nombre de valeurs distinctes d'un attribut. | ||
+ | |||
+ | Observer les statistiques qu' | ||
+ | |||
+ | La cardinalité d'une table : | ||
<code sql> | <code sql> | ||
- | select * from BigAnnuaireSimple | + | explain plan for |
- | where ... | + | |
+ | @p3 | ||
</ | </ | ||
+ | |||
<code sql> | <code sql> | ||
- | select * from BigAnnuaire | + | explain plan for |
- | where ... | + | |
+ | @p3 | ||
</ | </ | ||
- | Voici les requêtes utilisées dans les exercices suivants : | ||
- | ^ Nom ^ Requête | ||
- | | R1 |<code sql> | ||
- | | AgeEgal | ||
- | | AgeInf | ||
- | | AgeSup | ||
- | | AgeEntre | ||
- | | CodeEgal | ||
- | | CodeInf | ||
- | | CodeSup | ||
- | | CodeEntre | ||
- | |Age et Code postal : | | | ||
- | | AgeEgalCodeEgal | ||
- | | AgeEgalCodeInf | ||
- | | AgeInfCodeEgal | ||
- | | AgeInfCodeInf | ||
- | | AgeInfCodeEntre | ||
- | | AgeEntreCodeInf | ||
- | |Age puis dénombrement : | | | ||
- | | AgeInfCompte | ||
- | **Rmq**: la dernière requête AgeInfCompte est une sélection suivie d'une agrégation (avec le count). | + | Pour BigAnnuaire, |
- | Comprendre | + | |
- | ===== Exercice 2. Plan sans index ===== | + | <code sql> |
+ | explain plan for | ||
+ | select distinct nom from BigAnnuaire; | ||
+ | @p3 | ||
+ | </ | ||
- | On s' | ||
- | Pour afficher la durée d'une requête, vous ajouterez toujours cette ligne juste avant la requête. | ||
<code sql> | <code sql> | ||
- | set autotrace trace explain | + | explain |
+ | | ||
+ | @p3 | ||
</ | </ | ||
- | La durée estimée s' | ||
+ | et ainsi de suite pour les attributs âge, cp, tel et profil. | ||
- | Question/ | + | <showif isloggedin> |
+ | <fc # | ||
+ | Le fichier // | ||
+ | </ | ||
+ | |||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ===== Exercice 1. Requête | ||
+ | Expliquer le plan des requêtes suivantes. Détailler chaque étape de l' | ||
+ | |||
+ | |||
+ | **Question a)** : | ||
<code sql> | <code sql> | ||
- | set autotrace trace explain | + | explain |
- | select | + | select |
- | </ | + | |
+ | where a.age = 18; | ||
+ | @p3 | ||
+ | </ | ||
+ | |||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
- | La durée de totale la requête est la valeur de la **première** ligne de la colonne **Time**. Les valeurs des lignes suivantes sont des sous-totaux. | ||
< | < | ||
- | --------------------------------------------------------------------------------------- | + | ----------------------------------------------------------- |
- | | Id | Operation | + | | Id | Operation |
- | --------------------------------------------------------------------------------------- | + | ----------------------------------------------------------- |
- | | 0 | SELECT STATEMENT | + | | 0 | SELECT STATEMENT |
- | | 1 | TABLE ACCESS | + | | 1 | TABLE ACCESS |
- | --------------------------------------------------------------------------------------- | + | |* 2 | INDEX RANGE SCAN |
+ | ----------------------------------------------------------- | ||
+ | Predicate Information | ||
+ | ---------------------- | ||
+ | 2 - access(A.AGE=18) | ||
</ | </ | ||
- | Réponse: la durée est de 11 minutes et 58 secondes. | + | * Evaluer l' |
- | + | * Puis le traitement se poursuit | |
- | Rmq, une autre façon de procéder serait de chronométrer la durée réelle d'exécution d'une requête, mais cela n'est pas " | + | |
- | === Accès aux données : parcours séquentiel=== | + | </showif> |
- | Aucun index n'est défini pour la table BigAnnuaireSimple. Donc la seule façon d' | + | |
- | **Questions** : Pour la requête nommée AgeEgal, observer le plan de la requête et repérer le parcours séquentiel de la table BigAnnuaireSimple. | ||
+ | **Question b)** : | ||
<code sql> | <code sql> | ||
- | set autotrace trace explain | + | explain |
- | select | + | select |
+ | | ||
+ | | ||
+ | @p3 | ||
</ | </ | ||
- | * Quelle est sa durée ? Est-ce la même durée que R1 qui n'a pas de sélection? Pourquoi ? | ||
- | * Expliquer ce que fait l' | ||
- | * Mêmes questions idem pour les autres requêtes sur la table **BigAnnuaireSimple** | ||
- | Remarque: | + | <showif isloggedin> |
+ | **<fc # | ||
- | ===== Exercice 3. Plan avec index ===== | + | < |
+ | ----------------------------------------------------------- | ||
+ | | Id | Operation | ||
+ | ----------------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | ||
+ | | 1 | TABLE ACCESS BY INDEX ROWID| BIGANNUAIRE | 24400 | | ||
+ | |* 2 | INDEX RANGE SCAN | INDEXAGE | ||
+ | ----------------------------------------------------------- | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 2 - access(A.AGE> | ||
- | On considère la table BigAnnuaire identique à BigAnnuaireSimple mais ayant deux index | + | Column Projection |
- | * IndexAge est un index sur BigAnnuaire(age) | + | ----------------- |
- | * IndexCp est un index sur BigAnnuaire(cp). | + | 1 - " |
+ | 2 - " | ||
+ | </ | ||
+ | </ | ||
- | L' | ||
- | Question/ | ||
+ | **Question c)** : | ||
<code sql> | <code sql> | ||
- | select | + | explain plan for |
+ | select | ||
+ | from BigAnnuaire | ||
+ | where a.age < 70 and (a.cp = 93000 or a.cp = 75000); | ||
+ | @p3 | ||
+ | </ | ||
+ | |||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | |||
+ | |||
+ | <fc # | ||
+ | INLIST ITERATOR: Le OR est reformulé en un IN (75000, 93000) | ||
+ | le INLIST évalue le prédicat IN: | ||
+ | |||
+ | Pour chaque cp dans la liste de codepostaux: | ||
+ | |||
+ | accès à l' | ||
+ | |||
+ | puis accès par rowid aux nuplets et filtrer age< | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | ------------------------------------------------------------ | ||
+ | | Id | Operation | ||
+ | ------------------------------------------------------------ | ||
+ | | 0 | SELECT STATEMENT | ||
+ | | 1 | INLIST ITERATOR | ||
+ | |* 2 | TABLE ACCESS BY INDEX ROWID| BIGANNUAIRE | 307 | | ||
+ | |* 3 | INDEX RANGE SCAN | INDEXCP | ||
+ | ------------------------------------------------------------ | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 2 - filter(" | ||
+ | 3 - access(" | ||
</ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | **Question d)** : | ||
+ | <code sql> | ||
+ | explain plan for | ||
+ | select a.nom, a.prenom | ||
+ | from BigAnnuaire a | ||
+ | where a.age = 20 and a.cp = 13000 and a.nom like ' | ||
+ | @p3 | ||
+ | </ | ||
+ | Remarque: Oracle utilise deux index pour l' | ||
+ | |||
+ | <showif isloggedin> | ||
+ | **<fc #008000> | ||
+ | REPONSE: | ||
+ | Ne PAS trop passer de temps à expliquer le détail de BITMAP conversion. | ||
+ | Dire que c'est simplement un intersection de ROWID. | ||
+ | </ | ||
< | < | ||
- | ------------------------------------------------------------------------------------------- | + | ---------------------------------------------------------------- |
- | | Id | Operation | + | | Id | Operation | Name | Rows | |
- | ------------------------------------------------------------------------------------------- | + | ---------------------------------------------------------------- |
- | | 0 | SELECT STATEMENT | + | | 0 | SELECT STATEMENT | | |
- | | | + | |* |
- | |* | + | | 2 | |
- | ------------------------------------------------------------------------------------------- | + | | |
+ | | 4 | | ||
+ | |* | ||
+ | | 6 | BITMAP CONVERSION FROM ROWIDS| | ||
+ | |* 7 | INDEX RANGE SCAN | INDEXAGE | ||
+ | ---------------------------------------------------------------- | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | </ | ||
- | Predicate Information (identified by operation id): | + | ===== Exercice 2. Sélection AVEC OU SANS index ===== |
- | --------------------------------------------------- | + | |
- | 2 - access(" | + | Dans les plans suivants, on affiche son coût (voir la colonne **Cost**) |
+ | |||
+ | **Question a)** : | ||
+ | Etudiez plusieurs variantes de la requête sélectionnant les personnes dont l'âge est inférieur à une valeur donnée. Pour cela, testez les prédicats de la forme '' | ||
+ | |||
+ | <code sql> | ||
+ | explain plan for | ||
+ | select a.nom, a.prenom | ||
+ | from BigAnnuaire a | ||
+ | where a.age <= 10; | ||
+ | @p4 | ||
+ | </ | ||
+ | |||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | |||
+ | < | ||
+ | -------------------------------------------------------------------------------- | ||
+ | | Id | Operation | ||
+ | -------------------------------------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | ||
+ | | 1 | TABLE ACCESS BY INDEX ROWID| BIGANNUAIRE | 20000 | 351K| 20046 | ||
+ | |* 2 | INDEX RANGE SCAN | INDEXAGE | ||
+ | -------------------------------------------------------------------------------- | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 2 - access(" | ||
</ | </ | ||
+ | </ | ||
- | Réponse: | ||
- | * Le traitement commence par l' | ||
- | * Puis le traitement se poursuit avec l' | ||
- | **Question a)** : Pour chacune des 6 requêtes suivantes: | + | Compléter le tableau en indiquant la cardinalité, le coût et si l' |
- | Quelle est la durée de la requête ? Est ce que l'accès par index est plus rapide qu'un parcours séquentiel ? | + | |
+ | ^ Prédicat ^ Rows ^ Index utilisé ^ Cout ^ | ||
+ | | age < = 10 | | ||
+ | | age < = 20 | | ||
+ | | age < = 30 | | ||
+ | | age < = 40 | | ||
+ | | age < = 60 | | ||
+ | | age < = 100 | | ||
- | **Question b)** : Etudier le plan de la requête AgeEgalCodeEgal. Combien d' | ||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | ^ Prédicat ^ Rows ^ Index utilisé ^ Cout ^ | ||
+ | | age < = 10 | 22 200 | | ||
+ | | age < = 20 | 44 400 | | ||
+ | | age < = 30 | 66 600 | | ||
+ | | age < = 40 | 88 800 | | ||
+ | | age < = 60 | 133K | | ||
+ | | age < = 100 | 220K | | ||
- | **Question c)** : Tester plusieurs variantes de la requête de AgeInf avec successivement les prédicats age<10 puis age<20 puis age<30. | ||
- | Est-ce que la durée augmente? La durée est-elle proportionnelle au nombre de nuplets du résultat (affiché dans la colonne //Rows//) ? Compléter le tableau : | ||
- | ^Prédicat ^ Rows : Nbre de nuplets | ||
- | |age< | ||
- | |age< | ||
- | |age< | ||
- | Si on suppose que la durée de AgeInf est proportionnelle au nombre de nuplets du résultat, quelle serait la durée pour // | ||
- | **Question d)** : Observer que pour '' | + | </showif> |
+ | **Question b)** : | ||
+ | Pour quel prédicat Oracle préfère-t-il évaluer la requête sans utiliser l' | ||
- | ===== Exercice 4. Coût de l' | + | <showif isloggedin> |
- | L' | + | **<fc # |
- | Oracle estime le coût de plusieurs possibilités d' | + | A partir |
- | Le **coût d'un accès séquentiel** est proportionnel au nombre de pages (i.e., de blocs) à lire. On a la formule | + | </ |
- | coût(lecture sequentielle d'une table) = C * page(table) avec C étant une constante. En TD on suppose pour simplifier que C=1 mais en TME on veut connaitre la valeur de C. | ||
- | Afficher le nombre de pages de la table Annuaire : | + | **Question c)** : |
+ | Proposer deux requêtes '' | ||
+ | * la première utilise l' | ||
+ | * la deuxième ne l' | ||
<code sql> | <code sql> | ||
- | set autotrace off | + | explain plan for |
- | column table_name format A20 | + | select |
- | select | + | |
+ | where a.cp BETWEEN 50000 AND ....; | ||
+ | @p4 | ||
</ | </ | ||
- | Afficher le coût d'une lecture séquentielle : | + | <showif isloggedin> |
+ | **<fc # | ||
<code sql> | <code sql> | ||
- | set autotrace trace explain | + | explain |
- | select | + | select |
+ | | ||
+ | where a.cp BETWEEN 50000 AND 80000; | ||
+ | @p4 | ||
</ | </ | ||
- | En déduire la valeur de C. | + | < |
+ | -------------------------------------------------------------------------------- | ||
+ | | Id | Operation | ||
+ | -------------------------------------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | ||
+ | | 1 | TABLE ACCESS BY INDEX ROWID| BIGANNUAIRE | 66506 | 1233K| 66663 | ||
+ | |* 2 | INDEX RANGE SCAN | INDEXCP | ||
+ | -------------------------------------------------------------------------------- | ||
+ | </ | ||
- | On veut vérifier que C est constante. Pour cela calculer C à partir à partir du coût d' | ||
- | Le nombre de pages de BigAnnuaire est : | ||
<code sql> | <code sql> | ||
- | set autotrace off | + | explain plan for |
- | select | + | select |
- | where table_name = ' | + | |
+ | where a.cp BETWEEN 50000 AND 90000; | ||
+ | @p4 | ||
</ | </ | ||
- | Le coût d'une lecture séquentielle est : | + | < |
- | < | + | ---------------------------------------------------------------------- |
- | set autotrace trace explain | + | | Id | Operation |
- | select | + | ---------------------------------------------------------------------- |
+ | | 0 | SELECT STATEMENT | ||
+ | |* 1 | TABLE ACCESS FULL| BIGANNUAIRE | 88528 | 1642K| 70893 | ||
+ | ---------------------------------------------------------------------- | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 1 - filter(" | ||
</ | </ | ||
- | Est-ce que vous obtenez la même valeur pour C? | ||
- | ===== Exercice 5. Coût d'une sélection pour le choix d'un index ===== | ||
- | On étudie la stratégie reposant sur l' | ||
- | On veut observer pour quel prédicat de sélection les index sont utilisés. | ||
- | Si un index est utilisé pour évaluer la sélection | ||
- | du plan affiché par Oracle contient une ligne <fc # | ||
- | Si, en revanche, la sélection AGE=18 était évaluée au vol, cette même rubrique contiendrait la ligne <fc # | ||
- | Il est ainsi très simple de repérer les prédicats pour lesquels un index est utilisé. | ||
- | **Question a)** Quelles sont les requêtes pour lesquelles aucun index n'est utilisé bien que des index soient définis sur les prédicats de la requête? | ||
- | **Question | + | </ |
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== Exercice 3. Comparaison de plans d' | ||
+ | |||
+ | Pour une requête donnée, on veut étudier plusieurs plans équivalents afin de comparer le coût de chacun d' | ||
+ | |||
+ | === Directive pour forcer/ | ||
+ | Énumérer les plans équivalents revient à considérer toutes les combinaisons entre utiliser des index ou pas en ajoutant les **directives** '' | ||
+ | |||
+ | **Question | ||
+ | |||
+ | * La directive '' | ||
<code sql> | <code sql> | ||
- | set autotrace trace explain | + | explain |
- | select * from BigAnnuaire where age < 25 and cp < 12000; | + | |
+ | from BigAnnuaire | ||
+ | @p4 | ||
</ | </ | ||
- | * Remplacer la valeur <fc # | + | * La directive |
- | * A partir de la requête initiale //age < <fc # | + | |
- | * Pour les valeurs X et Y que vous avez proposées, est-ce que la requête //age < X and cp < Y// utilise des index ? Observer les durées et expliquer. | + | |
- | |||
- | **Question c)** On étudie le cas d'une requête ayant un prédicat composé de plusieurs prédicats simples et peu sélectifs. | ||
- | On se pose la question: Est-ce que l' | ||
- | Soit la requête AgeInfCodeInf composée de deux prédicats de sélection sur age et sur cp: | ||
<code sql> | <code sql> | ||
- | select * from BigAnnuaire where age < X and cp < Y; | + | explain plan for |
+ | select | ||
+ | from BigAnnuaire | ||
+ | @p4 | ||
</ | </ | ||
- | Proposer des valeurs pour les nombres X et Y telles | + | Vérifier |
- | * la requête //select * from BigAnnuaire where age < X;// est évaluée sans index. | + | |
- | * La requête //select * from BigAnnuaire where code < Y;// est évaluée | + | |
- | * La requête AgeInfCodeInf utilise les deux index. | + | |
- | **Question | + | **Question |
- | Préciser la taille des index : | + | |
+ | **Question c)** : Pour la requête '' | ||
+ | |||
+ | Pour forcer l' | ||
<code sql> | <code sql> | ||
- | set autotrace off | + | explain plan for |
- | column table_name A10 | + | select |
- | column index_name A10 | + | from BigAnnuaire a where a.age = 18 and a.cp = 75000; |
- | select | + | @p4 |
- | avg_leaf_blocks_per_key, | + | |
- | from all_indexes | + | |
- | where table_name | + | |
- | set autotrace trace explain | + | |
</ | </ | ||
- | Expliquer (cf. le paragraphe Documentation ci-dessous) ce que représentent leaf_blocks et avg_leaf_blocks_per_key. | ||
- | Combien de pages faut-il lire pour obtenir les ROWID des Personnes ayant 18 ans ? Quel est le coût correspondant ? | ||
- | Combien de pages faut-il lire pour obtenir les nuplets des Personnes ayant 18 ans à partir de leur ROWID? Quel est le coût correspondant ? | ||
- | Mêmes questions pour les Personnes ayant '' | + | Remarque: Déclarer plusieurs directives '' |
- | ===== Exercice 6. Comparaison de plans d' | ||
- | Le but de cet exercice est de montrer que le plan choisi par l' | + | ===== Exercice 4 (facultatif). Requête |
- | Pour ce faire, il vous sera demandé d' | + | |
- | Énumérer les plans équivalents revient à considérer toutes les combinaisons entre utiliser des index ou pas. | + | |
- | Dans Oracle, cela revient à utiliser des directives qui sont expliquées ci-dessous. | + | |
- | === Directive | + | Il existe une table **Ville** (cp, ville, population) qui contient le nom de la ville pour chaque code postal cp. |
- | * La directive **index()** force l'usage d'un index. Exple pour AgeSup | + | |
+ | Décrire les plans suivants en rédigeant le pseudo-code correspondant au plan. Mettre en évidence les itérations '' | ||
+ | |||
+ | **Question a)** : | ||
<code sql> | <code sql> | ||
- | | + | explain plan for |
- | from BigAnnuaire where age > 18; | + | |
+ | from Annuaire a, Ville v | ||
+ | where a.cp = v.cp | ||
+ | and a.age=18; | ||
+ | @p3 | ||
+ | </code> | ||
+ | |||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | |||
+ | < | ||
+ | --------------------------------------------------------- | ||
+ | | Id | Operation | ||
+ | --------------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | ||
+ | |* | ||
+ | | | ||
+ | |* 3 | INDEX RANGE SCAN | INDEXAGE | 20 | | ||
+ | | 4 | TABLE ACCESS FULL | VILLE | | ||
+ | --------------------------------------------------------- | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 1 - access(" | ||
+ | 3 - access(" | ||
</ | </ | ||
+ | </ | ||
+ | |||
+ | |||
- | | + | **Question b)** : |
+ | Même requête mais en remplaçant Annuaire par BigAnnuaire. Pourquoi la table Ville est-elle lue avant l'annuaire alors que dans la requête a) c'est la table Annuaire qui est lue avant les villes ? | ||
<code sql> | <code sql> | ||
- | | + | explain plan for |
- | from BigAnnuaire where age = 18; | + | |
+ | from BigAnnuaire a, Ville v | ||
+ | where a.cp = v.cp | ||
+ | and a.age=18; | ||
+ | @p3 | ||
+ | </code> | ||
+ | |||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | |||
+ | La table Ville (2000 villes) est plus petite que la sélection des personnes qui ont 18 ans (2200 personnes). | ||
+ | Donc oracle préfère charger en mémoire les villes plutot que les personnes ayant 18 ans. | ||
+ | |||
+ | < | ||
+ | ------------------------------------------------------------ | ||
+ | | Id | Operation | ||
+ | ------------------------------------------------------------ | ||
+ | | 0 | SELECT STATEMENT | ||
+ | |* | ||
+ | | 2 | TABLE ACCESS FULL | VILLE | ||
+ | | 3 | TABLE ACCESS BY INDEX ROWID| BIGANNUAIRE | 2200 | | ||
+ | |* 4 | INDEX RANGE SCAN | INDEXAGE | ||
+ | ------------------------------------------------------------ | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 1 - access(" | ||
+ | 4 - access(" | ||
</ | </ | ||
+ | </ | ||
+ | |||
+ | |||
- | | + | **Question c)** : |
<code sql> | <code sql> | ||
- | | + | explain plan for |
- | | + | |
+ | from BigAnnuaire a, Ville v | ||
+ | where a.cp = v.cp | ||
+ | and v.population >= 985000; | ||
+ | @p3 | ||
+ | </code> | ||
+ | |||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | REPONSE | ||
+ | |||
+ | Il y a 16,57 villes avec une population supérieure à 985000 | ||
+ | |||
+ | On a 220 personnes par ville en moyenne | ||
+ | |||
+ | Donc 16,57 * 220 = 3647 | ||
+ | |||
+ | </fc>** | ||
+ | |||
+ | < | ||
+ | ------------------------------------------------------------ | ||
+ | | Id | Operation | ||
+ | ------------------------------------------------------------ | ||
+ | | 0 | SELECT STATEMENT | ||
+ | | 1 | NESTED LOOPS | ||
+ | | 2 | | ||
+ | |* 3 | TABLE ACCESS FULL | VILLE | ||
+ | |* 4 | INDEX RANGE SCAN | INDEXCP | ||
+ | | 5 | TABLE ACCESS BY INDEX ROWID| BIGANNUAIRE | 220 | | ||
+ | ------------------------------------------------------------ | ||
+ | Predicate Information: | ||
+ | ---------------------- | ||
+ | 3 - filter(V.POPULATION> | ||
+ | 4 - access(A.CP=V.CP) | ||
</ | </ | ||
- | * La directive **index_combine()** force à utiliser deux index: | + | </ |
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== Exercice 5: Autres requêtes ===== | ||
+ | Dans cet exercice on vous demande d' | ||
+ | |||
+ | a) Requêtes avec group by | ||
<code sql> | <code sql> | ||
- | | + | explain plan for |
- | from BigAnnuaire | + | |
+ | from BigAnnuaire | ||
+ | group by age; | ||
+ | @p3 | ||
+ | </ | ||
+ | |||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | |||
+ | pas d' | ||
+ | |||
+ | < | ||
+ | | Id | Operation | ||
+ | -------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | ||
+ | | 1 | HASH GROUP BY | | 100 | | ||
+ | | 2 | INDEX FAST FULL SCAN| INDEXAGE | | ||
+ | -------------------------------------------------- | ||
</ | </ | ||
+ | </ | ||
+ | b) Requêtes avec group by having | ||
+ | <code sql> | ||
+ | explain plan for | ||
+ | select age, count(*) | ||
+ | from BigAnnuaire a | ||
+ | group by age | ||
+ | having count(*) > 200; | ||
+ | @p3 | ||
+ | </ | ||
- | ** Rmq :** Spécifier plusieurs directives index() dans une même requête ne force **pas** à utiliser plusieurs index simultanément, | + | <showif isloggedin> |
+ | **<fc # | ||
- | **Question a)** : Pour la requête AgeEgalCodeEgal ci-dessus proposer 4 plans **équivalents** (i.e., IndexAge seul, IndexCP seul, aucun index, les deux index) | + | Operation nommée FILTER |
+ | < | ||
+ | | Id | Operation | ||
+ | --------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | ||
+ | |* 1 | FILTER | ||
+ | | 2 | HASH GROUP BY | | 5 | | ||
+ | | 3 | INDEX FAST FULL SCAN| INDEXAGE | 220K| | ||
+ | --------------------------------------------------- | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 1 - filter(COUNT(*)> | ||
+ | </ | ||
+ | </ | ||
- | **Question b)** : Mêmes questions pour les requêtes AgeInfCodeInf et AgeInfCodeEntre. | + | c) Requete min max |
+ | <code sql> | ||
+ | explain plan for | ||
+ | select min(cp), max(cp) | ||
+ | from BigAnnuaire a; | ||
+ | @p3 | ||
+ | </ | ||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | Sort aggregate: trier et ne garder que les valeurs min et max | ||
+ | < | ||
+ | | Id | Operation | ||
+ | ------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | ||
+ | | 1 | SORT AGGREGATE | ||
+ | | 2 | INDEX FAST FULL SCAN| INDEXCP | 220K| | ||
+ | ------------------------------------------------- | ||
+ | Column Projection | ||
+ | ----------------- | ||
+ | 2 - " | ||
+ | </ | ||
+ | </ | ||
+ | d) Requête avec **not in** | ||
- | ===== Exercice 7. Index couvrant une requête ===== | + | <code sql> |
- | Vérifier qu'il est possible d' | + | explain plan for |
- | * avec un distinct | + | select a.nom, a.prenom |
- | * avec un group by et une agrégation | + | from BigAnnuaire a |
- | * avec un order by | + | where a.prenom not in ( select b.prenom |
- | * avec les 2 attributs age et cp dans la clause select | + | from BigAnnuaire b |
+ | where b.age< | ||
+ | @p3 | ||
+ | </ | ||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | OPeration n°1: ANTI join: si la condition a.prenom=b.prenom est fausse alors le nuplet est dans le résultat. | ||
+ | < | ||
+ | | Id | Operation | ||
+ | ------------------------------------------------------------ | ||
+ | | 0 | SELECT STATEMENT | ||
+ | |* 1 | HASH JOIN RIGHT ANTI | ||
+ | | 2 | TABLE ACCESS BY INDEX ROWID| BIGANNUAIRE | 15533 | | ||
+ | |* 3 | INDEX RANGE SCAN | INDEXAGE | ||
+ | | 4 | TABLE ACCESS FULL | BIGANNUAIRE | 220K| | ||
+ | ------------------------------------------------------------ | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 1 - access(A.PRENOM=B.PRENOM) | ||
+ | 3 - access(B.AGE< | ||
+ | Column Projection | ||
+ | ----------------- | ||
+ | 1 - (#keys=1) A.PRENOM, A.NOM | ||
+ | 2 - B.PRENOM | ||
+ | 3 - B.ROWID | ||
+ | 4 - A.NOM, A.PRENOM | ||
+ | </ | ||
+ | </ | ||
- | ===== Exercice 8 (facultatif). Durée d'une requête ===== | + | e) Requête avec not exists |
- | On veut observer l' | + | |
- | '' | + | |
- | On définit la requête M1(A) en fonction d'une valeur A de l'âge : | ||
<code sql> | <code sql> | ||
- | | + | explain plan for |
- | from BigAnnuaire | + | |
- | where age <= A; | + | from BigAnnuaire |
+ | where not exists ( select * | ||
+ | from BigAnnuaire b | ||
+ | where b.prenom = a.prenom | ||
+ | and b.age < a.age); | ||
+ | @p3 | ||
+ | </ | ||
+ | |||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
+ | |||
+ | Opération n°2: Lire toute la table et indexer en mémoire les couples (prenom, | ||
+ | Puis relire toute la table: pour chaque personne p interroger l' | ||
+ | Si aucun couple trouvé: alors ajouter la personne p dans le résultat. | ||
+ | |||
+ | < | ||
+ | ---------------------------------------------------- | ||
+ | | Id | Operation | ||
+ | ---------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | ||
+ | |* 1 | HASH JOIN RIGHT ANTI| | ||
+ | | 2 | TABLE ACCESS FULL | BIGANNUAIRE | | ||
+ | | 3 | TABLE ACCESS FULL | BIGANNUAIRE | | ||
+ | ---------------------------------------------------- | ||
+ | Predicate | ||
+ | ---------- | ||
+ | 1 - access(B.PRENOM=A.PRENOM) filter(B.AGE < A.AGE) | ||
+ | Column Projection | ||
+ | ----------------- | ||
+ | 1 - A.PRENOM, A.NOM | ||
+ | 2 - B.PRENOM B.AGE | ||
+ | 3 - A.NOM, A.PRENOM, A.AGE | ||
+ | |||
</ | </ | ||
- | 1. Temps total écoulé. Pour différentes valeurs de l' | + | </ |
- | 2. Temps de calcul et temps de transfert du résultat. Mesurer la durée d' | + | f) Requête avec minus : les code spostaux des villes qui n'ont pas de centenaire. |
- | Lire le fichier // | + | |
- | - Lorsque la sélection est suivie d'une projection (pour évaluer la clause select), quelle est la durée | + | |
- | - Quelle est la durée pour transférer 1 nuplet de l' | + | |
+ | <code sql> | ||
+ | explain plan for | ||
+ | select cp | ||
+ | from BigAnnuaire a | ||
+ | minus | ||
+ | | ||
+ | from BigAnnuaire b | ||
+ | where b.age> | ||
+ | @p3 | ||
+ | </ | ||
+ | <showif isloggedin> | ||
+ | **<fc # | ||
- | ===== Questions fréquentes ===== | + | 2 index (indexage et indexcp) sont utilisés pour produire la liste des cp où résident des centenaires. |
- | - Emacs : avant d'exécuter une requête (avec Ctrl-C Ctrl-C) vérifier qu' | + | L'index indexcp |
- | - Directives d' | + | < |
+ | | Id | Operation | Name | Rows | | ||
+ | ------------------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | | 220K| | ||
+ | | 1 | MINUS | | | | ||
+ | | 2 | SORT UNIQUE | | 220K| | ||
+ | | 3 | INDEX FAST FULL SCAN | INDEXCP | ||
+ | | 4 | SORT UNIQUE | | 2200 | | ||
+ | |* 5 | VIEW | index$_join$_002 | 2200 | | ||
+ | |* 6 | HASH JOIN | | | | ||
+ | |* | ||
+ | | 8 | INDEX FAST FULL SCAN| INDEXCP | ||
+ | ------------------------------------------------------------- | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 5 - filter(B.AGE> | ||
+ | 6 - access(ROWID=ROWID) | ||
+ | 7 - access(B.AGE> | ||
+ | </code> | ||
+ | </showif> | ||
- | select /*+ directive */ * | + | g) requête avec where age >= ALL (...) |
- | from | + | |
- | | + | |
- | + | ||
- | S'il y a une erreur de syntaxe dans une directive Oracle ignore la directive **SANS** vous avertir. | + | |
+ | <code sql> | ||
+ | explain plan for | ||
+ | select a.nom, a.prenom | ||
+ | from BigAnnuaire a | ||
+ | where a.age >= ALL (select b.age | ||
+ | from BigAnnuaire b | ||
+ | where b.cp = 75000); | ||
+ | @p3 | ||
+ | </ | ||
- | ===== Documentation ===== | + | <showif isloggedin> |
+ | **<fc # | ||
+ | |||
+ | Opérations 3 et 2: trier les personnes par age croissant. | ||
+ | |||
+ | Opérations 6,5,4: accès aux personnes cp=75000 puis trier leur age. | ||
+ | |||
+ | Opération 1: Fusion (ANTI signifie que la condition de jointure access(a.age< | ||
+ | |||
+ | < | ||
+ | | Id | Operation | ||
+ | ------------------------------------------------------------- | ||
+ | | 0 | SELECT STATEMENT | ||
+ | | 1 | MERGE JOIN ANTI | | 2200 | | ||
+ | | 2 | SORT JOIN | ||
+ | | 3 | TABLE ACCESS FULL | BIGANNUAIRE | 220K| | ||
+ | |* 4 | SORT UNIQUE | ||
+ | | 5 | TABLE ACCESS BY INDEX ROWID| BIGANNUAIRE | 220 | | ||
+ | |* 6 | INDEX RANGE SCAN | INDEXCP | ||
+ | ------------------------------------------------------------- | ||
+ | Predicate Information | ||
+ | --------------------- | ||
+ | 4 - access(A.AGE< | ||
+ | 6 - access(B.CP=75000) | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | h) Requete avec UNION, avec UNION ALL, avec une division, ... | ||
+ | |||
+ | |||
+ | ===== Exercice 6: Documentation ===== | ||
Vous pouvez interroger le dictionnaire du SGBD pour obtenir des informations détaillées sur vos tables et vos index. | Vous pouvez interroger le dictionnaire du SGBD pour obtenir des informations détaillées sur vos tables et vos index. | ||
Description d'un index : profondeur de l' | Description d'un index : profondeur de l' | ||
- | <code sql> | + | <code sql> |
- | set autotrace off | + | |
- | | + | from user_indexes; |
</ | </ | ||
Description d'une table : cardinalité, | Description d'une table : cardinalité, | ||
- | <code sql> | + | <code sql> |
- | | + | |
- | select table_name, num_rows, blocks from user_tables; | + | select table_name |
+ | | ||
+ | |||
+ | select table_name as nom, num_rows as cardinalite, | ||
+ | from all_tables | ||
+ | where table_name = ' | ||
</ | </ | ||
- | + | ||
Description d'un attribut : valeur min, max, nb de valeurs disctinctes. | Description d'un attribut : valeur min, max, nb de valeurs disctinctes. | ||
- | <code sql> | + | <code sql> |
- | set autotrace off | + | |
column table_name format A20 | column table_name format A20 | ||
column column_name format A20 | column column_name format A20 | ||
Ligne 376: | Ligne 844: | ||
etc... de nombreuses autres informations sont disponibles tq par exemple l' | etc... de nombreuses autres informations sont disponibles tq par exemple l' | ||
+ | |||
+ | |||
+ | ===== Questions fréquentes ===== | ||
+ | |||
+ | - Emacs : avant d' | ||
+ | |||
+ | - Directives d' | ||
+ | |||
+ | <code sql> | ||
+ | select /*+ directive */ * | ||
+ | from | ||
+ | where | ||
+ | </ | ||
+ | |||
+ | S'il y a une erreur de syntaxe dans une directive Oracle ignore la directive **SANS** vous avertir. | ||
+ | |||
+ | |||
+ | - Error: **cannot fetch last explain plan** from plan_table : il y a une erreur de syntaxe dans votre requête. La requête n'a pas pu être analysée par oracle. Corriger la requête. | ||
+ | |||
+ | - La cardinalité estimée (ROWS) d'un opérateur semble être celle de l' | ||
Ligne 383: | Ligne 871: | ||
===== Divers ===== | ===== Divers ===== | ||
- | Aller vers [[site: | + | Aller vers [[site: |
site/enseignement/master/bdr/tmeindex.1485344696.txt.gz · Dernière modification : de hubert