Architectures, modèles et langages de donnéesagamache/pageperso/LivreBDPDF/Chapitre78.pdf · 7.1...
-
Upload
doankhuong -
Category
Documents
-
view
224 -
download
0
Transcript of Architectures, modèles et langages de donnéesagamache/pageperso/LivreBDPDF/Chapitre78.pdf · 7.1...
Volume I
André Gamache 2005
Fascicule 3
Architectures, modèles et langages de données
Hypercube
Ingénierie des bases de données
OLAP
c,d
Langage SQL, indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
Architectures, Modèles et Langages de Données
Volume 1 Fascicule 1
1- Introduction 1
2- Architecture fonctionnelle du SGBD 20
3- Modèle conceptuel des données 79
- Index
Fascicule 2 4- Modèle relationnel : théorie et contraintes d’intégrité 1 5- Algèbre relationnelle 57 6- Transposition du MCD au MRD 137 - Index
Volume 2 Fascicule 3
7- Langage de données SQL 1 8- Indexation, vue relationnelle et base réactive 123 - Index
Fascicule 4 9- Langage de programmation et SQL 1
10- Théorie de la normalisation relationnelle : les formes normales FN1 à FN5 45 11- Optimisation des requêtes relationnelles 117 Annexes : A- SQLoader B- Projet ALU-Nord : Script de chargement des données - Index
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
5
Chapitre 7
Langage de données SQL DDL, DML et les recherches Dans les sections précédentes, les éléments au langage SQL ont été présentés afin d’illustrer les formulations équivalentes aux opérateurs algébriques. La facette DDL du langage a aussi été présentée avec plusieurs détails afin de pouvoir créer et formuler correctement les contraintes des tables. Dans ce chapitre, nous approfondirons les clauses SQL‐DDL en examinant quelques paramètres qui ont trait aux structures physiques et nous poursuivrons l’étude du langage de requête SQL, en accentuant davantage les diverses fonctions disponibles dans ce langage sans égard à leur implémentation dans un SGBD particulier. Encore ici, de nombreux exemples sont formulés avec le SQL implémenté par Oracle, lequel correspond à peu près au niveau 2 de la norme SQL‐92. Nous étudierons en détails l’implémentation Oracle de certaines notions qui sont particulières à ce logiciel SGBD. Cependant, notre étude débordera les limites imposées par Oracle pour discuter aussi de plusieurs autres notions et implémentations propres à d’autres SGBD.
7.1 Langage SQL et l’exploitation de la base de données Le langage de données SQL créé dans les années 70 découle des travaux d’une équipe de chercheurs de la société IBM qui travaillait à la mise au point des langages de données SQUARE et SEQUEL dans le cadre du développement du système R, précurseur de plusieurs SGBD relationnels, notamment du système SGBD DB2. Lʹacronyme SQL signifie Structured Query Language. C’est un langage à mots clés réservés. Il est non procédural dans sa formulation, masquant assez bien le caractère algébrique des expressions syntaxe relativement simple, mais affiche une facette de langage naturel, soit l’anglais élémentaire. Le langage SQL a fait lʹobjet de plusieurs efforts de normalisation qui ont débuté de façon sérieuse en 1989 et qui se poursuivent toujours. Toute norme SQL publiée peut être implantée à divers niveaux, selon les choix faits par le développeur du SGBD. Trois niveaux sont possibles : minimal (1), intermédiaire (2) et complet (3). Ce dernier niveau doit respecter intégralement la norme SQL. Il y a plusieurs normes SQL, chacune enrichissant la version précédente : SQL‐89, SQL‐92 et SQL‐93. Lʹimplantation de SQL par la société Oracle est fortement similaire à la norme ISO‐SQL‐92 de niveau 2. Il en est de même aussi pour le système WATCOM SQL. Le langage SQL comporte trois facettes fortement intégrées peu importe le SGBD relationnel : a) DDL (Data Definition Language), pour la définition des tables, des contraintes diverses et des vues relationnelles stockées dans le dictionnaire du SGBD. b) DML, (Data Manipulation Language) pour la manipulation des tables et plus précisément les manipulations des tuples de relations. c) DCL, (Data Control Language) pour gérer la définition physique des accès (index), la spécification des fichiers physiques et la validation des opérations exécutées dans un contexte multiposte.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
6
SQL et les autres langages L3G Le langage SQL (Oracle) est utilisé de façon autonome et interactive (au moyen du module SQL*Plus interactif). Il est aussi utilisé dans les blocs de PL/SQL (SQL intégré à un langage procédural interactif de Oracle) et dans les applications mises au point avec les L3G grâce aux API (interfaces de programmation pour les applications). Finalement, le SQL dynamique permet la formulation de requêtes dont la spécification complète ne sera connue qu’au moment de lʹexécution.
SQL*Plus (rappel sur cette application) Les clauses du langage SQL de Oracle sont prises en charge par le module utilitaire SQL*Plus qui les réceptionne et les transmet à lʹinterpréteur de SQL qui est un service intégré du SGBD. Après le calcul de la réponse, le module SQL*Plus reçoit les tuples et les affiche selon des paramètres qui peuvent être fixés par lʹutilisateur au moyen de directives particulières. Il y a dʹautres modes dʹinteraction avec le SGBD, notamment par lʹentremise des applications développées en langage de troisième génération (L3G) et utilisant lʹinterface API appelée OCI chez Oracle. Le module SQL*Plus est donc lʹintermédiaire incontournable lorsquʹun développeur veut exécuter une clause SQL en mode interactif. Le texte de la dernière clause est placé dans un tampon prédéfini et nommé par un nom interne suffixé par SQL. Cet espace peut contenir seulement une clause SQL, excluant les directives ayant trait au formatage, à lʹenvironnement et à lʹaffichage de la réponse par le module SQL*Plus. Pour contourner cette restriction, il est possible de définir un autre tampon courant par la directive SET BUFFER dont la durée est celle de la session de travail.
Figure 7.1
Utilitaire SQLPlus et script d’exécution L’application utilitaire SQLPlus est incontournable pour la mise au point et l’exécution en ligne des clauses SQL. Cette utilitaire SQL*Plus comporte plusieurs directives :
SET BUFFER tampon1 -- un autre tampon courant est créé SAVE C:\tampon1.sql --sauvegarde du tampon dans un fichier sur C SET BUFFER SQL -- retour au tampon par défaut de SQL*Plus
BD
serveur Tampon SQL*Plus
Directives SQL*Plus
SGBD
SQL*Plus
client
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
7
En outre, il est possible dʹenregistrer une trace complète de lʹaffichage avec la directive SPOOL :
SPOOL C:\trace.4juillet -- trace dans un fichier SPOOL ON -- début de la trace SPOOL OFF -- pour terminer la trace; spool ON pour redémarrer
Un fichier de directives est très utile pour obtenir lʹenvironnement désiré et cela, dès le début dʹune session de travail. Voici un exemple dʹun tel fichier‐script qui est exécuté par la directive : START :environbd3.sql) Dans le fichier‐script, il faut insérer les commandes et/ou les directives suivantes :
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY', ALTER SESSION SET NLS_LANGUAGE_FORMAT = french COLUMN NOM FORMAT A25 .../*liste des formats requis pour l’affichage des colonnes*/
Le fichier est exécuté par START envirobd3.sql ou en le nommant init.sql ou login.sql, auquel cas il sera exécuté automatiquement avec le lancement du module SQL*Plus. Avant dʹaborder lʹétude du langage SQL, il est utile de survoler les différentes structures utilisées par la base de données afin de donner un sens physique aux paramètres des ordres SQL‐DDL. Ce survol est effectué en faisant référence au système Oracle dont lʹimplémentation du modèle relationnel et du langage SQL est reconnue comme étant de très bon niveau. Les directives du module SQL*Plus sont résumées dans le tableau ci‐dessous. APPEND* Ajout de texte à la fin de la ligne courante CHANGE Changement de texte dans une ligne CLEAR Supprimer le contenu du tampon courant DEL Supprimer la ligne courante DESCRIBE Liste du schéma d’une table GET Lecture d’un fichier dans le tampon HOST <commande> Exécution d’une commande de l’OS (hôte) LIST Afficher le contenu du tampon courant RUN *** Exécution de la commande ou du script rangé dans le
tampon courant de l’application SQL*Plus SAVE <fichier.sql> Écriture de tampon courant dans un fichier SPOOL <fichier.sql> Écriture de la trace de l’écran dans un fichier
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
8
* Les directives et les commandes peuvent être écrites indifféremment en majuscules ou minuscules.
Fonctions du dictionnaire de données Le dictionnaire de données (DD) dʹun système relationnel est implanté comme une base de données du même type. Les structures de données utilisées sont les mêmes que celles utilisées pour stocker les données transactionnelles. Le dictionnaire contient les métadonnées nécessaires pour définir notamment les tables de la base de données. Le dictionnaire de données joue un rôle central dans le fonctionnement du SGBD relationnel. Son contenu est rangé dès le démarrage dans la RAM, accessible en ligne par le noyau du SGBD. Les tables du dictionnaire servent à diverses fonctions: a) Stockage de la définition de la base de données : schéma, index, trigger, packages, fonctions, procédures, synonymes (alias) etc. Il est entièrement accessible à lʹadministrateur des données (DBA) et partiellement aux utilisateurs qui peuvent consulter les tables (et les autres objets) dont ils sont propriétaires. b) Passerelle incontournable entre le noyau du SGBD et lʹinterpréteur du SQL pour effectuer lʹanalyse et le traitement des requêtes SQL. Le dictionnaire est essentiel à lʹappariement (binding) dynamique avec les applications L3G, notamment en ce qui concerne la compatibilité entre le type interne dʹune donnée et le type externe disponible dans le langage de programmation hôte. c) Documentation des divers objets de la base de données : tables, index , attributs, déclencheurs, packages. d) Participation à la sécurité et à lʹintégrité de la base en stockant les droits dʹaccès des utilisateurs aux tables de base, aux vues et aux espaces de données. e) Répartition des données au moyen des liens entre les bases qui sont spécifiées dans le dictionnaire et leur localisation géographique.
Introduction à lʹorganisation des espaces physiques et logiques de la base de données Oracle La base de données Oracle est définie comme un ensemble de tables (relations) appartenant au compte créateur des tables qui a généralement la propriété du DBA. L’unicité du nom dʹune table est assurée par l’adjonction automatique du préfixe formé avec le numéro de compte de lʹutilisateur propriétaire. La répartition des données sur plusieurs serveurs est prise en considération par les métadonnées du dictionnaire sur la localisation, notamment les liens de placement. L’ensemble des définitions de table correspond au schéma de la base de données qui est exprimé en SQL‐DDL.
7.2 Objets Oracle Un objet ou plus précisément une entité de la base de données Oracle, est la notion utilisée pour représenter les éléments suivants : le schéma de relation, lʹextension d’une
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
9
relation (table), les déclencheurs (trigger), la fonction, la procédure interne, lʹespace de données, lʹindex, la vue, le trigger et le package. Tous ces objets sont rangés dans les tables du dictionnaire du SGBD, sauf l’extension de la relation qui est stockée dans un espace de table (tablespace). L’accès à chacun des objets est contrôlé par des droits qui sont accordés et, au besoin, révoqués par le DBA.
Figure 7.2
Espace de stockage Oracle Une relation du MRD est implémentée par une structure de table définie comme une liste de pages physiques appartenant à un tablespace défini et associé à un ou plusieurs fichiers gérés par le système d,exploitation du serveur. Chaque table ne contient généralement que des tuples de même schéma. Il y a une exception avec les tables stockées dans un cluster. Un extent est l’unité d’allocation de l’espace et les extents d’une même table forment un segment.
Structure physique de table : pages enchaînées Figure 7.2a
Chaque table dʹun schéma appartient au compte (Oracle) qui l’a créée. Par défaut, le nom complet dʹune table est formé du nom du compte suffixé par le nom de la table: compteChef. Tous les droits d’accès sur une table sont acquis automatiquement par le propriétaire de la table qui peut cependant les partager en propageant aux autres les droits dʹaccès appropriés (commande GRANT). Une table Oracle est accessible par diverses interfaces relationnelles, notamment via le module SQL*Plus, le langage procédural PL/SQL et lʹinterface de programmation des applications (API) utilisant un langage de troisième génération.
Compte5.r2
Compte6.r2
Dictionnaire de données, schémas, procédures, …
Compte4.r1
SGBD Instance1
BD
Piage i
Page i+1
extent1 extent2
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
10
Organisation logique des espaces de stockage pour les données et les index La structure de l’espace physique d’une base de données est définie par le DDL au moyen de plusieurs paramètres qui spécifient l’allocation initiale de l’espace pour une table, à même celui du tablespace auquel la table sera associée. Les données du système Oracle sont stockées via des espaces logiques, appelés espaces de table (tablespaces) (TS), chacun composé de segments, eux‐mêmes composés de lots homogènes et contigus d’octets appelés extents. Ces espaces sont utilisés pour stocker notamment les tables, les index, les clusters et les données des segments de recouvrement transactionnel (rollback).
Figure 7.2b Une extent est défini comme une unité dʹallocation de lʹespace physique. Les extents dʹun segment peuvent être dispersés et chaînés. Sur le plan de l’espace physique, un extent est formé de pages contiguës (blocs Oracle). Finalement, une page est constituée de plusieurs blocs du système d’exploitation hôte, blocs dont la taille est fixée lors de la création de la base de données. Lʹespace physique dʹune base de données peut être augmenté par le DBA, par lʹajout, en tout temps, dʹun ou de plusieurs fichiers dans un espace de table particulier.
Page Oracle Une page Oracle est une suite de blocs physiques constitués dʹoctets contigus (1 K à 16 K). La taille d’une page est fixée à la création de la base de données par le paramètre DB_BLOCK_SIZE initialisé dans le fichier INIT.ORA. Une page contient normalement des tuples dont la structure est homogène, i.e. appartenant à une seule table, sauf pour les pages de cluster. Ces pages ont une structure particulière permettant d’y ranger des tuples en provenance d’au moins tables différentes.
Espace de table (tablespace) Toute entité ou tout objet dans une base de données relationnelle est rangée dans une table qui est elle‐même placée dans un espace de table particulier appelé tablespace . Cet espace sur disque est associé à un fichier physique. Le compte Oracle SYS est, à la création de la base de données, le propriétaire initial des objets du dictionnaire et de
Espace de table AB
Segment 1 Segment 2
Extent 1 Extent 2 Extent 3 Extent 4 pages
Fichier AB
Fichier B
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
11
certaines autres tables utilisées en cours dʹexploitation pour stocker les métadonnées traitant de la performance et de lʹexploitation des tables. Le premier compte DBA créé lors de lʹinstallation du SGBD est nommé SYSTEM. L’espace de table SYSTEM est aussi créé à lʹinstallation et il appartient au compte de même nom. Les divers objets créés y sont placés par défaut, y compris ceux des utilisateurs, sauf si lʹutilisateur propriétaire de lʹobjet requiert de le faire autrement. Une meilleure performance est atteinte si les objets dʹindex et de tables des utilisateurs sont placés dans autant de TS (tablespace) distincts créés par le DBA. C’est le principe de base de l’architecture optimale appelée OFA (Optimal Flexible Architecture) et suggérée pour Oracle 1.
Figure 7.3
Structure générale de page Une page a une taille fixée au lancement de lʹinstance et cela, par lʹentremise du paramètre de système BLOCK_SIZE. Sa taille peut varier jusquʹà 16 Ko. Chaque page a une zone de gestion de son espace qui est appelée répertoire des tuples ou de lignes. L’espace d’une page est assortie d’une contrainte qui est la suivante :
PCTFREE + PCTUSED < 100 Dans ce répertoire, on trouve une entrée où est inscrit un déplacement (offset) dans la page qui est en quelque sorte un pointeur sur le début du tuple. Lors dʹune réorganisation de la page, les tuples sont déplacés vers le bas afin de récupérer lʹespace libre. Seuls les déplacements sont modifiés, tandis que le rid ( soit le TID pour Ingres et le ROWID pour Oracle) demeure constant, évitant ainsi de faire la mise à jour des index
1..*
1..*
BD
Schémas
Comptes
FichierPhysique EspaceDeTab
Extent
Page
Segment
Table1..*
1..1
1..1
1..1
1..1
1..1
1..* 1..1
1..1
1..*
1..*1..1 1..1 1..1
1..* 1..*
1..*
1..* 1..11..*
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
12
Figure 7.4 Lʹen‐tête de la page contient des données pour la gestion transactionnelle, notamment les adresses logiques (rid) des tuples verrouillés avec le numéro de la transaction propriétaire du verrou. Lors dʹune relecture de la page, le système vérifie si une transaction inscrite dans lʹen‐tête (qui a donc lu un tuple) est encore active et, dans le cas affirmatif, considère que le tuple est toujours verrouillé. Sinon, le verrou est périmé et le tuple est considéré disponible pour les autres transactions. L’adresse logique d’un tuple nommé rid ou rowid (row identifier) est souvent formée de la façon suivante :
rid ::= noFichier:noPage:noEntreedansRepertoire
Page Oracle Chaque page ou bloc Oracle est composé de blocs physiques d’octets contigus et est l’espace utilisé pour ranger les tuples d’une même table. La structure de la page comprend au moins trois parties : un en‐tête, une zone primaire pour ranger les tuples (PCTUSED) et une zone de débordement (PCTFREE) pour ranger les tuples dont la taille a augmenté suite à une mise à jour. Par défaut, le système Oracle fixe la zone primaire à 40% de la taille de la page et la zone de débordement à 10 %. Dans les cas où il y a peu de mises à jour et beaucoup d’ajouts, les valeurs par défaut (10/40) pour les paramètres PCTFREE et PCTUSED ne sont pas toujours adaptées aux besoins d’un tel environnement d’exploitation. Il faut les ajuster par exemple à 5/80 lorsquʹil y a peu de mises à jour. Le rôle du paramètre PCTFREE est de réduire le chaînage des parties de tuples dʹune page à lʹautre. Cette fragmentation dʹun tuple devrait se faire pour réaliser une mise à jour qui exige un espace plus grand non disponible à lʹadresse actuelle. À la création dʹune page, le paramètre PCTFREE indique le pourcentage de la page à garder en réserve pour y reloger les tuples mis à jour. Le paramètre PCTUSED permet de
En‐tête de page
Zone d’élargissement des tuples
Zone de tuples Répertoire des tuples
INITRANS : espace pour la gestion des accès à la page MAXTRANS : nombre maximum de transactions en accès simultané. PCTFREE : % de l’espace de page réservé à l’élargissement des tuples lors de la mise à jour. PCTUSED : % de la page réservé aux données et en dessous duquel la
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
13
minimiser les lectures et les écritures sur le disque en indiquant au serveur le pourcentage de lʹespace occupé dʹune page, qui doit être atteint avant que la page soit désignée à nouveau comme une page insérable. En effet, les insertions se poursuivent dans une page tant et aussi longtemps que le pourcentage du PCTFREE est respecté. À ce moment, la page est marquée non insérable et supprimée de la liste des pages disponibles (free list). Des insertions de tuples seront faites à nouveau dans cette page, lorsque les suppressions ramèneront le pourcentage dʹoccupation de lʹespace sous la barre du paramètre PCTUSED. Les tuples des tables et des index peuvent cohabiter ou non dans le même espace de table tout en étant rangés obligatoirement dans des segments différents et donc dans des extensions différentes. Dans une extension donnée, il n’y a donc que des pages de données de tables ou d’index et non les deux. Dans les deux cas, les paramètres PCTFREE et PCTUSED sont utilisés pour la gestion de lʹespace de la page. Avec lʹindex, le paramètre PCTFREE doit être assez grand pour une table indexée, surtout lorsque le taux de mise à jour est élevé. En effet, tout ajout de tuples dans une table indexée entraîne une mise à jour de lʹindex, donc une augmentation de la taille des entrées, notamment pour les index sur les attributs de type chaîne. Ceci entraîne rapidement un débordement dans la zone libre. Pour éviter le chaînage des tuples qui ralentit la lecture, il faut prévoir dans chaque page un espace libre un peu plus grand pour les pages destinées à stocker les index.
Page insérable Une page a le statut dʹinsérable si le SGBD est autorisé à y ajouter encore des tuples, sinon elle a le statut de page non disponible. Lorsque l’occupation dʹune page est telle que lʹespace restant correspond à lʹespace réservé par le PCTFREE, la page est enlevée de la liste des pages insérables (free list). Elle ne peut plus recevoir de nouveaux tuples, mais on peut toujours mettre à jour les tuples présents en utilisant la zone libre ou de débordement. Par contre, lorsque les suppressions et les modifications (changement de la valeur d’un attribut qui fait passer sa taille de 450 à 40 caractères ou la mise à jour d’un attribut null) ramènent l’espace occupé en dessous de la valeur PCTUSED pour cette page, celle‐ci est réinscrite dans la liste des pages insérables et de nouveaux tuples peuvent y être ajoutés. Lorsquʹun tuple est ajouté dans une page, il est possible que lʹespace occupé dépasse le PCTUSED, et cela, tant que la limite du PCTFREE est toujours respectée. Cette situation peut se produire si PCTUSED + PCTFREE < 100. Lorsque les paramètres PCTUSED et PCTFREE ne sont pas adaptés à lʹenvironnement d’exploitation de la base de données, cela conduit à des échanges de pages supplémentaires inutiles entre le disque et la ZMP (Zone de Mémoire Partagée). Pour une exploitation avec un faible taux de réorganisation et de mise à jour, le facteur PCTUSED devrait être de l’ordre de 80 et le PCTFREE de 10.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
14
Migration des tuples Lorsquʹun tuple est modifié et quʹil ne peut pas être replacé dans son espace dʹorigine, le système cherche à le ranger en entier dans lʹespace du PCTFREE (zone de débordement) et une adresse de suivi est placée dans lʹespace dʹorigine afin de garder lʹadressage initial fonctionnel pour les index.
Figure 7.5 Ainsi, les entrées dʹindex sur cette table pointent toujours au même rid (ou rowid) et dans la même page et cela, même si le tuple pointé a été déplacé. Le traitement des index incorpore celui du suivi dʹun tuple au fil de ses adresses de déplacement. Cʹest la migration des tuples. Pour lire un tel tuple, le système nʹa pas à effectuer un accès disque supplémentaire puisquʹil se trouve dans lʹespace de débordement de la même page. Après lʹinsertion du tuple dans lʹespace PCTFREE (débordement), lʹespace dʹorigine nʹest occupé que par une adresse de suivi et le reste de cet espace pourra être éventuellement récupéré pour y placer les données dʹun autre tuple. Si le tuple modifié ne peut pas être rangé dans lʹespace du PCTFREE, il est déplacé entièrement dans une nouvelle page insérable et la nouvelle adresse est aussi rangée dans son emplacement initial afin dʹen permettre le suivi à partir des index existants, qui pointent toujours sur lʹemplacement dʹorigine. Lʹinconvénient de cette gestion est que cela oblige à faire deux lectures au lieu dʹune seule pour accéder au tuple recherché.
Fragmentation des grands tuples et chaînage Une deuxième solution à la gestion des tuples plus encombrants (par exemple les tuples avec un BLOB) consiste à les diviser et à chaîner leurs parties. Lorsquʹun tuple modifié ne peut pas être rangé ni dans sa page dʹorigine ni dans une autre page de la base de données, le serveur divise le tuple en parties chaînées et chacune sera rangée dans une page ayant lʹespace suffisant.
Figure 7.6
12 :89
Tuple modifié et placé dans la zone de débordement
Place initiale du tuple de rid = 12 :34
tuple rid : 23:12
PCTFREE
PCTUSED 56:77
123:45
en‐tête
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
15
Cʹest le chaînage dʹun tuple qui nʹintervient que lorsque la migration est impossible. Le chaînage est aussi pénalisant parce quʹil faut faire plusieurs lectures sur disque pour retrouver la première partie et autant de lectures supplémentaires pour lire les autres.
Placement des données : regroupement La notion de regroupement des tuples (dans une structure de cluster) concerne généralement plusieurs tables qui partagent les pages dʹun même segment spécial appelé cluster.
Figure 7.6a Ces tuples cohabitent parce qu’ils ont en commun des valeurs d’attributs de regroupement (key cluster attributes) et quʹils sont souvent utilisés par les applications dans une opération de jointure. Étant logés dans les mêmes pages, la vérification de la condition de jointure sera faite plus rapidement parce quʹils seront accessibles sans nécessiter d’échanges de pages supplémentaires entre le noyau du SGBD et le disque des données. On peut concevoir la structure logique dʹun cluster comme ayant un répertoire dʹen‐tête construit avec les valeurs partagées par deux tuples de tables différentes. Chaque entrée du répertoire pointe sur les deux tuples au moyen de deux rid. Un cluster peut aussi être défini pour ne loger que les tuples dʹune même table, en les regroupant selon la valeur dʹun attribut particulier.
Rappel de quelques notions sur les arbres utilisés par les index Les index utilisés par Oracle2 ont généralement une structure en B*‐arbre. Ils sont rangés dans des tables, elles‐mêmes placées dans un segment dédié à un index et éventuellement dans un espace de données (tablespace) spécialisé, créé par le DBA. Cette structure est du type index hiérarchique : les feuilles pointent sur les adresses rids (ou encore ROWID) des tuples indexés par un attribut dont les valeurs indexées sont regroupées et ordonnées dans une cellule terminale (feuille). Chaque cellule terminale est indexée au niveau supérieur qui contient un pointeur de plus que le nombre de valeurs dans la cellule. Par exemple, la recherche du tuple indexé par la valeur 76 débute à la racine, par l’examen de la première et de la dernière entrée pour déterminer le sous‐arbre à explorer. Le premier examen indique qu’il faut explorer le sous‐arbre milieu et la
45 ::::23 :34 (45) ::::23 :67 45, ‘jacques’, 75 75, ‘patricia’, 45
Valeur de clé du cluster = En‐tête du
cluster
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
16
procédure se poursuit ainsi jusqu’à l’atteinte du dernier niveau. Une telle structure arborescente est gardée équilibrée par une procédure de mise à jour et la longueur moyenne du chemin d’accès à n’importe quel tuple est constante. Pour 106 tuples, il faut environ cinq accès pour retrouver un tuple. Dans le pire des cas, cinq lectures sur disque sont nécessaires pour obtenir autant de pages. Chaque clé primaire spécifiée dans un schéma de table génère soit un index interne (du type Système), soit une procédure interne pour vérifier la contrainte de clé. Toutefois, lʹindex système peut être désactivé (disable), auquel cas l’index nʹest pas utilisé pour renforcer la contrainte dʹentité. La contrainte de NULL continue de sʹappliquer aux attributs primaires. La création d’un index avec un attribut se fait par une commande SQL‐DCL et suppose un tri des valeurs de la clé. Lʹopération sera effectuée dans un espace de mémoire temporaire géré par le SGBD. Un index créé par le DBA peut être supprimé à volonté et refait plus tard. Dans certains cas, le DBA peut accorder à un utilisateur le droit de créer un index. Si une telle structure est intéressante pour la recherche, elle gêne souvent la mise à jour. En effet, l’ajout d’un nouveau tuple indexé par la valeur 9 implique une insertion de la clé dʹindexation dans la cellule référencée par le pointeur droit de la clé 3 (voir Figure 7.8). Or, cette cellule étant déjà saturée, la cellule ciblée sera divisée et la valeur médiane insérée au niveau supérieur par la même procédure. Ces opérations exigent des accès aux disques qui ralentissent les opérations dʹaccès à la base de données. Le maintien de l’équilibre de l’arbre est toutefois assuré et réparti entre les différentes opérations sur la base de données.
7.3 B‐arbre Le B‐arbre dʹorigine est construit à partir de la racine et il est géré pour demeurer toujours équilibré, i.e. avec une hauteur à peu près constante (Figure 7.7).
Figure 7.7
81 21
tuple
12 3 5235 91 85
2 1 1913 3937
90 87
9995
2623 10 5 6255
84 82
Pointeur sur clés supérieures à celles de gauche et inférieures à celles de droite.
Pointeur sur tuple L’absence de la flèche indique l’absence de pointeur.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
17
Les cellules feuilles sont aussi reliées par un chaînage latéral afin de faciliter le balayage séquentiel partiel ou complet des tuples d’une table. Les caractéristiques de cette structure sont les suivantes : Une cellule contient p pointeurs aux sous‐arbres toujours équilibrés et un maximum de (p‐1) clés. Lʹarbre est dit dʹordre p. Le pointeur à la gauche dʹune valeur de clé référence un sous‐arbre dont les clés sont inférieures ou égales à la clé du nœud; le pointeur à la droite fait référence un sous‐arbre dont les clés sont supérieures. Le pointeur sur un tuple est constitué du rid, cʹest‐à‐dire lʹadresse de la page. Ce pointeur est inséré dans chaque nœud et non seulement dans les feuilles. Avec l’arbre d’ordre 3 de la Figure 7.7 ( 3 pointeurs et 2 valeurs de clé), la clé 9 est ajoutée pour fournir l’arbre suivant représenté à la figure 7.8. Le rééquilibrage est généralement immédiat, ou légèrement différé, cʹest‐à‐dire quʹil est fait pratiquement à chaque ajout ou modification dans le B‐arbre. En insertion, le tuple est stocké dans une page et sa clé insérée dans la cellule appropriée de lʹindex avec les divisions et les migrations des tuples quʹimposent les règles dʹéquilibre. La principale caractéristique de cet arbre est que les nœuds internes contiennent des pointeurs aux tuples et que les clés ne sont pas redondantes dans les feuilles.
Procédure algorithmique dʹinsertion La clé dʹun tuple est insérée en trouvant la cellule où elle devrait être normalement placée. Sʹil y a lieu, la division de la cellule est faite et la clé à la droite de la médiane est déplacée au le niveau supérieur en utilisant la procédure dʹinsertion. Lʹopération est récursive.
Procédure de suppression La suppression dʹun tuple dans une cellule conserve cette dernière intacte si le nombre de pointeurs résiduel est supérieur à p/2, où p est lʹordre de lʹarbre.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
18
Figure 7.8
Calcul de la hauteur dʹun arbre dʹordre 3 (avec p = 3 pointeurs par cellule) La hauteur dʹun B‐arbre peut être estimée par un simple raisonnement inductif : niveau h=1: 1 cellule, 3 pointeurs et 2 clés : 30 p0 cellule et p0 x (p‐1) clés niveau h=2: 3 cellules, 9 pointeurs et 6 clés : 31 p1 cellules et p1 x (p‐1) clés niveau h=3 9 cellules, 27 pointeurs et 18 clés : 32 p2 cellules et p2 x (p‐1) clés donc au niveau h : p(h‐1) (p‐1) >= n (où n est le nombre de tuples) (h‐1)log p + log(p‐1) >= log n h>= 1 + (log n ‐ log(p‐1)) avec log dans la base p (soit l’ordre de l’arbre). Donc le nombre moyen de pages lues dans un accès est environ logp n (dans la base p). Pour un arbre d’ordre plus élevé, par exemple 50, les gains en terme d’accès sont encore plus significatifs : avec n = 500 000 tuples et p = 50 on a h ≈ 4. Soit quatre lectures nécessaires pour atteindre la feuille recherchée dans un B‐arbre. Dans le cas courant où la cellule est une page, lʹadresse se réfère à celle‐ci, la recherche dʹune clé en mémoire se fait très rapidement. En effet, la taille d’une page (8Ko) pour la cellule confère un ordre relativement élevé à l’arbre pour ainsi en diminuer sensiblement la hauteur à une constante généralement inférieure à 8. La base p du logarithme joue un rôle critique. En effet, plus p est grand, plus la valeur du log dans la base p est petite.
Avantages de la structure en B‐arbre Le B‐arbre offre plusieurs avantages lorsquʹil est utilisé comme index : ‐ Le parcours moyen pour trouver un tuple est inférieur à la hauteur de lʹarbre.
21 12
tuple
10 3 5235 91 85
2 1 1913 3937
9087
9995
2623 9 5 6255
8482
81
tuple
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
19
‐ Il nʹy a pas de redondance des clés à la suite dʹune duplication dans les feuilles. ‐ La hauteur dʹun arbre pour indexer un nombre n de dossiers est la même quel que soit
le chemin parcouru. Pour 2 millions de tuples, il est de lʹordre de 5 accès au disque.
Inconvénients du B‐arbre Lʹajout et la suppression sont des opérations complexes en raison de la réorganisation des nœuds internes. Périodiquement, le DBA aura à supprimer lʹindex et à le recréer pour compacter les entrées. Les pointeurs aux tuples (8 octets) occupent lʹespace des cellules autrement occupé par des clés. Finalement, le parcours séquentiel des tuples est parfois difficile, voire impossible ou impraticable, en cas dʹabsence de chaînage latéral à tous les niveaux et en raison de la dispersion des pages qui exige plusieurs lectures sur le disque.
B*‐arbre La structure du B*‐arbre est similaire à celle du B‐arbre, mais les clés des cellules intermédiaires sont aussi insérées dans les feuilles avec le pointeur aux tuples, i.e. aux données. Les nœuds intermédiaires ne pointent pas sur les tuples. Il y a donc une certaine réduction de la redondance, augmentant ainsi la densité des tuples dans les pages de données. Le parcours séquentiel des feuilles est possible au moyen du pointeur de chaînage latéral.
Variantes du B‐arbre Deux variantes existent pour le B‐arbre : ‐ B+‐arbre : il correspond au B‐arbre classique avec des pointeurs aux tuples placés uniquement dans les feuilles et un remplissage des cellules qui ne dépasse pas la moitié de leur capacité. ‐ B*‐arbre : il est identique au B+‐arbre, mais avec la garantie dʹun remplissage de chaque cellule ne dépassant pas les deux tiers de sa capacité. Le nombre de réorganisations des cellules peut devenir important, surtout à lʹapproche de la saturation des feuilles de lʹarbre. Dans la pratique, lorsque cet état est atteint au cours d’une mise à jour, les procédures de gestion de l’arbre amorcent immédiatement sa réorganisation en divisant le contenu de la cellule saturée en deux parties et en réallouant l’espace pour une ou plusieurs nouvelles cellules du B‐arbre. L’effort de rééquilibrage est consenti au fur et à mesure, par quanta répartis, afin d’éviter l’accaparement du noyau du SGBD aux temps d’une demande forte et durant lesquelles le travail de réajustement ralentirait les opérations dans la base de données.
Mécanisme de verrouillage des tuples Normalement, un utilisateur du système Oracle exploitant directement les données par lʹentremise de SQL*Plus, soit par une application n’a pas à se soucier du verrouillage des données. La gestion des verrous est automatique et assumée par le SGBD du serveur. Un premier utilisateur qui accède à une table acquiert aussi un verrou dʹexclusivité TX (ou plus simplement X) sur chaque tuple modifié ou inséré et un verrou RX sur la table. Le verrou RX (RS en premier se transformant en RX) sur une table autorise la transaction à faire dʹautres actions sur les tuples de la table qui ne sont pas encore verrouillés par une
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
20
autre transaction. À la fin de la mise à jour marquée par un COMMIT transactionnel, les verrous sont automatiquement libérés par le moteur dʹOracle. Le système pose un verrou au niveau des tuples, sauf si une application le demande explicitement par la commande explicite LOCK TABLE. Le verrou contrôle alors toute la table. L’atomicité de chaque ordre SQL est assurée par le SGBD en ce sens quʹun ordre est complété ou en cas d’erreur, défait entièrement, et cela, sans intervention directe de lʹapplication.
7.4 Association des espaces de table avec les fichiers physiques Les espaces de table nommés tablespaces (TS) sont associés à des fichiers physiques dʹun OS lors de leur création par le DBA. Les tables et les index sont créés et associés explicitement à un TS particulier par une commande particulière utilisée seulement par le DBA.
CREATE TABLESPACE ESPACE_USER3 --commande pour le DBA DATAFILE '/usr/users/fichier1.dbf' SIZE 25M DEFAULT STORAGE INITIAL 50K NEXT 30K PCTINCREASE 0 MINEXTNENTS 1 MAXEXTENTS 100 ONLINE;
Une table peut être rangée explicitement dans ce tablespace déjà créé par la commande DDL suivante :
CREATE TABLE Usines( no number(2) constraint cp_usines primary key, libelle varchar2(45), ville varchar2(60), capacite number, -- par défaut un entier de précision 32 classe char(1)) TABLESPACE ESPACE_USER3;
Lʹespace de table est nommé et son espace maximal spécifié dans une commande antérieure est de 50 Mo. Toutefois, lʹespace initial est de 50K et augmente, selon les besoins, par quanta de 30 Ko. Toute table stockée dans cet espace de table aura les caractéristiques de stockage par défaut. Normalement, lʹespace de table est créé par le DBA et non par les développeurs. La création d’une table se fit dans un espace prédéfini par le DBA et alloué au compte Oracle par défaut. N.B. Si lʹespace de table nʹest pas fourni dans la commande CREATE TABLE ou prédéfini par défaut, les objets sont rangés dans lʹespace de table SYSTEM. Les segments ROLLBACK et les INDEX sont aussi créés explicitement par le DBA :
CREATE INDEX Usines_nom_idx On Usine(nom); CREATE [PUBLIC] ROLLBACK SEGMENT segt25 TABLESPACE TS_table10;
La création d’un segment temporaire est effectuée automatiquement par le SGBD lors de son installation. Rappelons aussi que les segments de rollback sont nécessaires pour
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
21
effectuer le UNDO ou le ROLLBACK dʹune seule transaction sans pour autant recourir au journal interne qui est global à toutes les transactions.
Bases de données de référence Les exemples utilisés pour illustrer les premières clauses SQL du présent chapitre se réfèrent à quelques bases de données dont le schéma et la sémantique sont présentés ci‐dessous. Les versions récentes des SGBD utilisant lʹUNICODE devraient pouvoir éventuellement accepter les signes orthographiques nationaux dans les clauses SQL. Pour simplifier la base, on convient dʹutiliser de préférence les minuscules non accentuées, mais en tolérant les exceptions pour des raisons de meilleure lisibilité.
BD1 : Ventes au détail Cette extension de la base de données valide la contrainte référentielle qui est implémentée et active lors des transactions sur la base. Elle sera utilisée par la suite comme base de référence pour de plusieurs exercices SQL. La base est composée des tables Ventes et Inventaire.
Ventes : nom* article* qte* (table enfant) Serge a1 3 Ted a2 5 Andrée a1 2 Serge a2 4 Erik a1 2 Serge a3 4 Valérie a7 3 Lucie a7 2 Serge a5 3 Serge a7 5 Christine a3 6 Serge a1 7
Cette contrainte référentielle est un cas particulier d’une contrainte d’inclusion plus générale qui spécifierait que tout article de la relation Ventes doit être inclus dans la projection de la table Inventaire sur l’attribut article. Une transaction sur la base ne concerne qu’un article en inventaire au moment de la vente, tandis que chaque article en inventaire peut faire l’objet de plusieurs transactions de vente. Dans la table Inventaire, les attributs prixVente et cout partagent le même domaine de valeurs, mais avec une sémantique distincte marquée par un libellé respectif différent. Tous les articles vendus sont inscrits dans lʹinventaire. Au moins un article disponible en inventaire n’a pas encore été vendu. Pour satisfaire l’hypothèse de la relation universelle qui impose l’unicité des libellés dans un schéma, les articles déjà vendus et ceux actuellement en inventaire peuvent être libellés respectivement Ventes.article et
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
22
Inventaire.article. Une transaction de vente est inscrite en notant le nom de l’acheteur, l’article et la quantité. La clé de la table Ventes est rendue volontairement complexe par l’absence d’un numéro de transaction. Dans ce schéma, elle est formée de tous les attributs.
Inventaire : article* prixVente cout (table parent) a5 17 20 a1 10 5 a2 20 0 a7 15 15 a3 25 null a16 46 34 + nouvel article
Figure 7.9 La table Inventaire fournit les articles en inventaire. L’absence de coût signifie que cet article n’est plus disponible. La contrainte référentielle est spécifiée par une clé étrangère mono‐attribut dans la table Ventes (soit article) portant le même libellé que la clé primaire dans la table parent Inventaire. La simplicité de la relation Inventaire et le choix de sa clé sous‐tendent que certains faits ne peuvent pas être représentés dans cette base. Par exemple, deux articles identiques achetés à des coûts différents ne pourraient pas être représentés dans cet inventaire.
BD2 Gestion des stocks de pièces pour les transporteurs aériens Cette deuxième base représente les stocks de pièces des transporteurs aériens répartis ici et là dans le monde pour la réparation de leurs avions.
Transporteur : mat* nom volPass villeSS permis f1 Flyer 200 Londres 235 f2 Inter 100 Paris 160 f3 Airqc 300 Québec null f4 Boac 200 Londres 190 f5 Delta 300 Boston 120
Un transporteur a un permis d’opération pour chaque avion q’il possède, une matricule, un siège social dans une ville donnée et un nom de société. Le permis d’un transporteur peut être révoqué temporairement auquel cas l’indicateur null est utilisé pour signifier l’absence de permis. Chaque transporteur peut déplacer un nombre maximum de passagers par jour (volPass). Dans le cas de cette base BD2, l’hypothèse de la relation universelle est aussi vérifiée, car un libellé d’attribut n’a qu’une seule sémantique, peu importe la table où il apparaît. Les attributs proprio et mat partagent le même domaine sémantique. En outre, l’attribut proprio peut être null comme cela est signalé par un null dans l’extension ci‐dessus.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
23
Pieces : noP* description km (K) poids villeP proprio
p1 aileron 8500 12 Londres f3 p2 moteur 9900 50 Paris f5 p3 porte 6000 17 Rome f1 p4 radar 8000 14 Londres null p5 train 5000 78 Paris f2 p6 télémètre 9000 36 Londres f2
Figure 7.10 La contrainte dʹinclusion (qui est dénommée DIN) est du genre référentiel et elle doit être valide à tout moment. De plus, le permis de vol international est unique et identifie le transporteur, mais il est possible d’avoir un transporteur qui ne l’a pas ou qui est en instance de l’obtenir (unicité de l’attribut avec valeur nulle autorisée). Lʹattribut permis ne peut donc pas être une clé, car il peut être nul. Les clés sont respectivement mat et noP. Les attributs proprio et mat ont le même domaine sémantique et font donc référence au même concept. Si la colonne proprio était ignorée, la table n’aurait pas de contrainte référentielle et les deux tables seraient sans association sémantique. La ville du siège social du transporteur est nommée différemment (villeSS) de celle du dépôt de pièces (villeP). L’hypothèse de la relation universelle est donc vérifiée de facto. Il y aurait une ambiguïté si, dans les deux relations, il y avait un même attribut ville ayant deux interprétations différentes. Avec la colonne proprio (comme clé étrangère) dont le domaine sémantique est partagé avec celui de mat, un lien est établi entre les deux tables et les jointures deviennent possibles. Avec ces deux tables, il serait aussi possible de définir un cluster indexé de placement des tuples pour que ceux qui partagent la même valeur de la clé du cluster (par exemple, la clé primaire et de la clé étrangère) soient rangés dans la même page.
BD3 Dotation des ressources humaines Cette base implante une association (n‐m) entre deux classes au moyen d’une troisième relation ou table.
Postes : noPoste* description j12 gérant j34 électricien j98 null j21 mécano1 j55 contrôleur j75 mécano2
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
24
Empl : noEmpl* nom prenom tauxH P346 Audy Rudy 13.00 P456 Gagnon André 17.00 P345 Audy Michel 12.50 P651 Tremblay Robert 12.50 P762 Dussault Marc 13.25 P337 Bérube null 12.50 P450 Dion Fabrice 13.25 P535
P800 Joncas Vanjulier
Sylvie Julie
14.50 19.50
Assignations : noPoste* noEmpl* debut quart j12 P456 21‐jan‐1992 soir j34 P651 22‐mar‐1991 jour j21 P762 23‐dec‐1990 nuit j34 P345 24‐sep‐1994 jour j75 P346 12‐oct‐1997 null j55 P456 25‐jan‐1994 jour j34 P337 21‐jan‐1992 nuit j75
j34 P450 P800
27‐jan‐1994 10‐nov‐1995
null soir
* Indique un attribut primaire (i.e. un attribut inclus dans une clé) Figure 7.11
Le lien d’association complexe entre les deux tables est assuré par une troisième table Assignations. La troisième table utilisée pour implémenter lʹassociation a une clé primaire composée (noPoste, noEmpl); les deux clés étrangères distinctes dans cette table sont respectivement noPoste et noEmpl.
7.5 Schéma de la base de données Le modèle conceptuel est transposé en un modèle logique (MLD) propre au SGBD par lʹentremise de règles de passage particulières. Le schéma du modèle logique est spécifié par les clauses SQL‐DDL qui définissent les tables de la base de données et leurs caractéristiques. Les contraintes dʹattribut, la clé primaire et la ou les clés étrangères dʹune relation sont aussi définies dans le schéma.
Création dʹune table et le typage des attributs La création d’une table consiste à définir son schéma et à l’associer à un nom unique composé du compte propriétaire et du libellé de la table. Définir une table sous‐tend aussi la spécification de ses attributs typés, des contraintes d’intégrité associées aux attributs et, éventuellement, de son espace de table. Les types internes (type avec lequel une donnée est stockée dans la base de données) disponibles dans Oracle sont similaires à ceux spécifiés par le SQL‐92 avec cependant quelques différences. Par exemple, le type booléen n’est pas toujours implémenté ; il est cependant présent dans la version 9i de Oracle et de DB2.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
25
Types internes dʹOracle pour les données Les SGBD comme Oracle, DB2 et Informix sont capables de représenter les données avec une gamme assez étendue de types. Toutefois leurs types ne sont pas toujours aussi élaborés que ceux des langages de programmation de 3e génération. Une conversion s’imposer alors, opération qui devra être prise en charge par l’application! Avec Oracle, les types suivants sont disponibles : VARCHAR2(w), où w est la longueur de la chaîne, max. 2Ko, et 4Ko avec la version 8. NUMBER(p, s), p est le nombre total de chiffres avec s chiffres après le point (scale). LONG, données en caractères jusqu’à deux Go (image). DATE (1), ce type comprend les éléments suivants : le siècle, l’année, le mois, le jour, l’heure, la minute, la seconde(voir (1)). RAW (w), données en binaire, pour une longueur maximale de 255 octets; LONG RAW, données en binaire, jusqu’à 2 Go. ROWID, caractères hexadécimaux représentant l’adresse du tuple. CHAR (w), où w est la longueur fixe de la chaîne avec un maximum de 255 o. MLSLABEL, étiquette de système (pour la version Trusted Oracle). FLOAT (b), pour les réels avec précision b. CLOB, données de longueur variable codées sur 1 octet/caractère, mx 4Go. BLOB, chaîne de bits, max. 4Go. NCHAR, chaîne fixe de caractères NLS (alphabet national). L’implémentation des types peut varier d’un manufacturier à l’autre avec la limitation classique pour le Float dans sa représentation des entiers et des réels. (1) Le type DATE a un format externe bien connu, mais son format interne est bien différent. Le passage de l’un à l’autre se fait par des fonctions spécialisées : TO_DATE(), TO_CHAR() et TRUNC(). La fonction TO_CHAR(), avec un gabarit approprié, permet dʹafficher la date dans le format externe souhaité et de supprimer le temps lors de la transposition de la date. Cela permet ainsi de faire des comparaisons fondées uniquement sur le jour, le mois et lʹannée: TO_CHAR(dateContrat, ʹYYYY‐MM‐DDʹ) ‐‐> ʹ1999‐11‐24ʹ.
Conventions dʹécriture des contraintes de clé Les tables sont créées avec une clé primaire (dite aussi principale) et éventuellement une ou plusieurs clés étrangères. Le nom d’une contrainte spécifiée lors de la création dʹune table doit être unique et peut être formé par simple convention d’écriture avec le préfixe cp_ ou pk_ pour la contrainte de clé primaire suivi du nom de la table. Pour une clé étrangère, le nom de la contrainte est préfixé par fk_, suivi du nom de la table enfant et au besoin, celui de la table parent. Exemple, fk_ouvrier_atelier, pour dénoter la contrainte référentielle entre la table Atelier et Ouvrier. Ainsi, le nom de la contrainte permet de l’associer au couple table enfant‐parent.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
26
Voici la formulation du schéma de la table Ventes de la BD3 : CREATE TABLE Ventes(
nom VARCHAR2(15) NOT NULL, article VARCHAR2(10) NOT NULL, qte NUMBER(3), CONSTRAINT pk_Ventes PRIMARY KEY (nom,article, qte), CONSTRAINT fk_Ventes_inventaire FOREIGN KEY(article) REFERENCES Inventaire (article) ) [Tablespace TS_user99 Storage initial 500M next 75M minextents 5 maxextents 20] ;
N.B. Les tuples de la table Ventes sont rangés dans lʹespace table TS_user_99 jusquʹà hauteur de 10 Go. Le reste de lʹespace sera occupé par dʹautres tables. La spécification de l’espace de données (tablespace) n’est pas nécessaire lorsque le rangement des tuples est fait automatiquement dans l’espace de données associé par défaut au compte de l’utilisateur. Lorsque la clé primaire est simple, elle peut être aussi définie au niveau de lʹattribut.
CREATE TABLE Inventaire ( article VARCHAR2(10) CONSTRAINT cp_inventaire PRIMARY KEY, prixVente NUMBER(5,2) NOT NULL, cout NUMBER(5,2) null) PCTFREE 20 PCTUSED 75 [TABLESPACE TS_donnees1]; --table stockée dans TS_donnees1
La table est créée et les données seront stockées dans un espace de table nommé TS_donnees1. Si lʹespace de table nʹest pas identifié, lʹextension sera créée et placée automatiquement dans lʹespace de table SYSTEM. Ce dernier est créé par le SGBD pour y ranger les tables du dictionnaire. Il faut donc de préférence ne pas engorger cet espace en plaçant les données dans des espaces de table séparés stockés au besoin sur un disque différent.
CREATE TABLE Transporteur( mat CHAR(3) NOT NULL, nom VARCHAR2(15) NOT NULL, volPass NUMBER(3), villeSS VARCHAR2(15), permis CHAR(3) NULL, CONSTRAINT pk_transporteur PRIMARY KEY (mat)) [TABLESPACE TS_donnees_user]; **** -- [] signifie non nécessaire selon le cas. Les données sont rangées dans le tablespace TS_donnees_user
CREATE TABLE Pieces (
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
27
noP CHAR(2) NOT NULL PRIMARY KEY, description VARCHAR2(15) NOT NULL, km NUMBER(4)NOT NULL, poids NUMBER(2)NOT NULL, VILLEP VARCHAR2(15) NOT NULL, proprio CHAR(3) NULL, CONSTRAINT fk_transporteur_pieces FOREIGN KEY (proprio) REFERENCES Transporteurs(mat)) [TABLESPACE TS_donnees_user];
CREATE TABLE Empl(
NOEmpl CHAR(4) NOT NULL PRIMARY KEY, -- si clé simple nom VARCHAR2(35) NOT NULL, prenom VARCHAR2(25) NULL, TAUXH NUMBER(5,2) NOT NULL check(tauxH > 10.00)) TABLESPACE TS_donnees_user;
CREATE TABLE Postes( NOPoste CHAR(3) NOT NULL PRIMARY KEY, description VARCHAR2(45) null) [TABLESPACE TS_donnees_user];
CREATE TABLE Assignations ( NOPoste CHAR(3) NOT NULL, NOEmpl CHAR(4)NOT NULL, debut DATE NOT NULL, quart VARCHAR2(6) NULL, CONSTRAINT cp_Assignations PRIMARY KEY (noPoste, noEmpl), CONSTRAINT fk_Assignations_empl FOREIGN KEY(NOEMPL) REFERENCES EMPL (noEmpl), CONSTRAINT fk_Assignations_postes FOREIGN KEY (noPoste) REFERENCES Postes (noPoste)) TABLESPACE TS_donnees_user;
Le schéma de cette dernière table comprend une clé primaire et deux clés étrangères. Par la suite, le DBA ou le propriétaire de la base de données peut toujours modifier certains paramètres d’un schéma par la commande ALTER TABLE :
ALTER TABLE Postes ADD (description varchar2); ALTER TABLE Postes DROP description; --avec la version 8
Les clés primaires sont qualifiées NOT NULL de sorte que si le chargement initial de la base de données est fait avec une clé primaire désactivée, les nulls seront quand même interdites de stockage. À la fin de lʹopération, la clé primaire est activée de nouveau afin dʹamorcer la construction ou la mise à jour de lʹindex‐système sous‐tendu par la clé
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
28
primaire. Cette façon de faire est plus rapide, puisquʹil nʹy a pas de mise à jour de lʹindex en cours de chargement, mais exige lʹabsence de clés dupliquées. Avec le NOT NULL, le chargement refusera tout tuple dont les attributs primaires ne sont pas valués, puisque ces données ne constituent pas une clé valide.
Les clés : primaire, étrangère et attribut UNIQUE Une clé primaire limite la nature des tuples qui peuvent être rangés dans la base de données en interdisant les doublets. En effet, il ne peut pas y avoir deux tuples ayant la même valeur pour les attributs de la clé primaire. Cette contrainte est généralement placée à la fin de la spécification du schéma d’une table et est nommée de manière à la distinguer dans le dictionnaire. La clé primaire peut être simple ou composée. Voici un exemple dʹune clé composée :
CREATE table Assignation ( noEmpl number(2) not null, noPoste number (2) not null, … , CONSTRAINT cp_EmplPoste PRIMARY KEY (noCours, section);
Lorsque la clé primaire est composée, elle peut être formulée pour avoir des attributs nuls, pourvu quʹun des attributs primaires soit non nul. Dans ce cas, la contrainte de clé primaire est implémentée par la clause UNIQUE(), laquelle permet de contraindre les valeurs pour quʹelles soient toutes différentes ou nulles pour un ou plusieurs attributs.
CREATE TABLE Employe ( nom varchar2(50) NOT NULL, noEquipe number(4) NULL, . . . ) Constraint U_NoBinome UNIQUE(nom, noEquipe) );
La paire dʹattributs (nom, noEquipe) doit avoir au moins une valeur pour l’attribut nom qui ne peut pas être nul. Toutefois, lʹattribut noEquipe peut être nul comme cela est autorisé par les UNIQUE et NOT NULL. Une contrainte étrangère peut être spécifiée en se référant a un attribut UNIQUE, donc à des attributs qui ne sont pas une clé primaire. La clé étrangère simple ou complexe exprime une contrainte pour les tuples qui peuvent être ajoutés dans la table enfant et une autre contrainte sur la suppression des tuples dans la table parent. Soit la table ChoixCours avec une contrainte référentielle par rapport à la table parent CoursHoraire.
CoursHoraire(noCours*, section*, professeur) ChoixCours(matricule*, noCours*, section)
Create table ChoixCours(
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
29
matricule char(3) NOT NULL, noCours char(6), section number(1), constraint cp_choix_cours primary key (matricule, noCours), CONSTRAINT fk_choix_cours_cours_horaire FOREIGN KEY(noCours, section)
REFERENCES CoursHoraire(noCours, section); L’ajout d’un choix de cours particulier sous‐tend une référence à un cours‐horaire qui doit exister dans la base. La clé étrangère dans un tuple est soit entièrement composée de nulls, soit partiellement ou entièrement valuée, i.e. qu’au moins un des attributs a une valeur.
7.6 Implémentation des contraintes structurelles avec le langage SQL‐92 Les définitions ci‐dessus sont partiellement muettes en ce qui concerne les contraintes structurelles du MCD. Ceci est particulièrement important pour les tables avec des clés étrangères. En effet, la suppression dʹun tuple dans une table parent nʹest pas neutre par rapport à la table enfant. Quʹarrive‐t‐il aux tuples dépendants lors de la suppression ou de la mise à jour de la clé du tuple parent ? Reprenons lʹexemple du modèle simple ci‐dessous dont le schéma correspondant est constitué de deux relations : Ville et Province. Les contraintes du modèle imposent une suppression automatique des villes associées lorsque la province est supprimée.
Figure 7.12 Le modèle équivalent UML est le suivant :
Province(nomP*, populationP) Ville(nomV*, populationV, dateCreationV, nomP)
Figure 7.13
EstDansVille
nomV* populationV dateCreationV
Province nomP* populationP
(1,1) (1,n)
Ville nomV* populationV dateCreationV
Province nomP* populationP
11..* EstDans
UML
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
30
Actions référentielles en SQL Le sort réservé aux tuples dʹune table enfant lors dʹune mise à jour ou dʹune suppression dans la table parent est variable selon les spécifications de la table enfant. Ces actions dites référentielles dans la proposition SQL‐92 sont formulées en lien avec une action de mise à jour dans la table parent et de ses effets au niveau de la table enfant. Elles ne sont pas toutes l’objet d’une implémentation dans Oracle. CASCADE : Une modification (UPDATE ou DELETE) de tuples dans la table parent se propage aux autres tables. Ainsi, une suppression dʹun tuple parent entraîne la suppression des tuples correspondants dans la table enfant. Une modification de la clé dʹun tuple de la table parent entraîne la suppression des tuples préalablement associés à lʹancienne clé. NO ACTION : Si une modification (DELETE ou UPDATE) viole la contrainte référentielle, la modification est refusée ou défaite (action par défaut); SET NULL : Lorsque le tuple parent est supprimé (DELETE), lʹattribut de la clé étrangère devient NULL dans la table enfant. Il en est de même pour une modification (UPDATE) de la clé dans la table parent. SET DEFAULT : Le tuple de la table parent est supprimé (DELETE) et la clé étrangère des tuples de la table enfant est mise à une valeur définie par défaut lors de la création de la table enfant. Avec lʹexemple ci‐dessous, les tables sont créées en SQL‐92 de façon à respecter les contraintes structurelles :
CREATE TABLE Province( nomP char varying(45) NOT NULL PRIMARY KEY, populationP number(8) NOT NULL);
N.B. Le type char varying() est le pendant SQL‐92 de varchar() et varchar2() deux types couramment implémentés dans les SGBD Oracle, DB2 et Informix.
CREATE TABLE Ville( nomV char varying(50) NOT NULL PRIMARY KEY, populationV number(8), dateEntree DATE, nomP char varying(45) NOT NULL, CONSTRAINT fk_ville_province FOREIGN KEY(nomP) REFERENCES Province(nomP) ON DELETE CASCADE [ON UPDATE CASCADE] [ON DELETE SET NULL] );
Si une province doit être supprimée de la table, tous les tuples de Ville qui le référencent sont aussi supprimés. De même, si une ville disparaît de la carte administrative dʹune
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
31
province, elle ne peut pas rester dans la base avec une clé composée de l’indicateur null. Les autres actions entre crochets ne sont pas toujours implémentées : si une province change de nom, tous les tuples dans Ville qui font référence à cette province par la clé étrangère sont modifiés pour refléter le changement de nomP. La troisième option permet de supprimer un tuple référencé ; dans ce cas la clé étrangère des tuples de la table enfant sont mise à null.
Autres langages de données Il existe quelques implémentations plus conviviales du langage algébrique (et du calcul relationnel) qui permettent la fouille d’une base de données et la transmission des résultats à l’utilisateur. Voici quelques langages de requête implémentés : ‐ QUEL : langage convivial implémenté à partir du calcul de tuple (Berkeley University); ‐ QBE : langage basé sur le calcul de domaine (IBM‐IMF, crd et FORMS‐Oracle); ‐ RENDEZ‐VOUS : langage quasi naturel (IBM); ‐ CUPID : algèbre relationnelle utilisée par le biais de pictogrammes (IBM). Depuis leur création, quelques‐uns de ces langages sont tombés dans l’oubli, parfois en raison de lʹabsence dʹune technologie appropriée ‐ CUPID, LAGRIF et RENDEZ‐VOUS. La puissance de la technologie actuelle permettra éventuellement à de nouveaux langages graphiques ou de type langage naturel de voir le jour et de trouver un créneau d’applications.
7.7 Requête SQL Une requête SQL3, 4, 5 est un énoncé de recherche, composé de clauses identifiées par le mot clé Select et dont lʹordre logique dʹexécution est indiqué ci‐dessous par lʹentier placé entre parenthèses. La dernière opération est le tri des tuples de la réponse. Concrètement, l’ordre d’exécution sera celui correspondant au plan d’exécution optimisé et généré par le moteur SGBD. Voici la structure d’une clause SELECT complète : (5) SELECT sélection des attributs (alias) de projection. (1) FROM pour nommer la ou les relations en jeu dans le calcul de la réponse. Ces relations sont des vues, des relations de base ou une expression de table. (2) [WHERE] prédicat (formulé au besoin avec une sous‐requête) de sélection ou de jointure. (3) [GROUP BY ] pour grouper les tuples selon des attributs. (4) [Having] pour filtrer les groupes à retenir sur la base dʹun prédicat sur le groupe. (6) [ORDER BY] pour trier les tuples de la réponse sur les attributs (incluant les alias) présents dans la réponse calculée. Le format général de la requête comprend deux parties obligatoires : SELECT et FROM . Les crochets encadrant les autres indiquent qu’elles sont facultatives. L’ordre
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
32
d’exécution d’une requête sous‐tend celui de l’exécution de l’expression algébrique correspondante. Le début est marqué par l’étape (1), pour se terminer par l’étape (6), soit le tri du résultat. Les opérations pour la mise à jour de lʹextension d’une relation sont implémentées sous forme dʹordres DML dont la syntaxe est relativement simple :
INSERT INTO <relation> VALUES (... ) ; UPDATE <relation> SET <attribut> = valeur WHERE <prédicat de sélection> ; DELETE <relation> WHERE <prédicat de sélection>;
Les commandes de mise à jour (UPDATE et DELETE) sous‐tendent au préalable une recherche avec le prédicat formulé dans le WHERE, suivie de la modification des tuples trouvés. Les clauses DDL et DCL sont couramment ajoutés aux clauses du DML pour former un langage de données complet au sens de la norme ISO.
Modèle dʹexécution d’une requête Lʹexécution logique de la requête débute par le calcul des sous‐requêtes qui sont définies par la clause FROM et/ou WHERE, et cela pour obtenir une table ou un scalaire. Dans le cas d’une sous‐requête corrélée, i.e. qui contient un prédicat faisant référence à un attribut de la requête parent, elle devra être recalculée pour chaque tuple sélectionné dans la table parent, soit celle formulée par la clause FROM. Lʹexécution de lʹexpression de table annoncée dans le FROM est effectuée en filtrant immédiatement chaque tuple de la table par le prédicat . En ce faisant, on évite le calcul inutile dʹau moins une table intermédiaire qui peut devenir très volumineuse. Ce calcul peut faire référence à plusieurs tables de base si le From sous‐tend une jointure. S’il y a lieu, les tuples de la table intermédiaire sont regroupés par lʹexpression du GROUP BY et les fonctions dʹagrégation sont exécutées sur chaque groupe formé. Les colonnes du SELECT sont retenues dans la réponse transmise par lʹexécution dʹune projection. Un tri éventuel est effectué (ORDER BY) à la fin des calculs et juste avant de transmettre la réponse au client. N.B. Le prédicat annoncé par le WHERE peut avoir plusieurs formes : condition simple, un prédicat d’inclusion avec une ou des sous‐requêtes emboîtées, ou un prédicat vide ou absent toujours évalué à VRAI. Toute la puissance discriminante de la requête s’exprime par ce prédicat de sélection formulé dans le WHERE. Lʹordre logique dʹexécution des clauses est tel que les alias des relations et des attributs ne sont pas connus ou visibles quʹaprès les étapes où ils sont définis dans l’exécution de la requête. Ainsi, un alias d’attribut dans la clause SELECT (étape 5) peut être utilisé dans la clause ORDER BY (étape 6) pour ordonner les tuples de la réponse, parce qu’il a
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
33
été défini avant. Il est donc visible ou connu par lʹexécuteur au moment du tri. Toutefois, le même alias ne peut pas être utilisé dans le WHERE, car il est encore inconnu à lʹétape 2 de lʹexécution de la requête. Il sʹagit dʹun ordre logique dʹexécution dont le plan sera définitivement établi par lʹoptimiseur. Il est possible que les opérations soient formulées différemment dans un plan optimisé, mais le résultat devra être le même.
Grammaire du SELECT (SQL‐92) La grammaire SQL‐92 (englobant SQL‐89) de la clause SELECT est représentée sous forme dʹun graphe. Les conventions utilisées dans un tel graphe sont les suivantes : Les mots clés et les symboles du langage sont de préférence en lettres majuscules
SELECT liste-non-vide-attributs-ou-fonctions FROM liste-non-vide-de-relations WHERE prédicat simple-complexe-ou-sous-requête;
Le souligné dʹun mot clé indique la valeur par défaut: ORDER BY ASC . Le libellé en minuscules et avec une majuscule comme première lettre utilisé dans le FROM représente le nom du schéma de la vue, de la relation ou de lʹattribut. Graphe de la grammaire du SELECT
FROM
schéma vue
(sous‐requête)
snapshot
table
WHERE
@lien
FROM
(1)
(2)
condition
CONNECT BY condition
STARTWITH condition
GROUP BY expressionHaving condition
(2) (3)
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
34
Figure 7.14
Expression SQL Une expression est constituée dʹune combinaison d’attributs, de constantes, de fonctions, d’opérateurs et dʹopérandes qui sont évaluables pour donner une table ou un scalaire. Le type du résultat de lʹexpression est défini. Le résultat peut être aussi un null lorsquʹun de ses opérandes est un nul. Par exemple, les expressions suivantes sont correctes :
Expression Type de la réponse CONCAT(ʹMme ʹ, nom) Chaîne
age_actuel = 35 + age Entier ou null si âge est null total = AVG(prix) * 3.6 Réel total = AVG(prix) * 3.6 Réel salaire = (salaire * .15) + salaire Réel res = (67* 5) /2 Réel
SELECTUNION
UNION
INTERSECT
MINUS
(4)(3)
,
,
ORDER
FOR UPDATE
OF
NOWAIT
DESC
ASCposition
expression
colonne
table
vue
(4)
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
35
Dans ce dernier exemple, si le salaire dans un tuple est null, alors le résultat de lʹévaluation de l’expression sera aussi l’indicateur null. Une expression peut être utilisée partout où peut apparaître un attribut ou une constante.
Condition Une condition est un prédicat dont lʹévaluation fournit une valeur booléenne : TRUE ou FALSE. Avec SQL‐92 lʹévaluation peut fournir une troisième valeur, soit UNKNOWN. Elle est formée dʹune formule logique ou dʹun prédicat dʹinclusion dans un ensemble qui peut être énuméré ou calculé par une sous‐requête. Exemples de conditions placées dans la clause WHERE :
SELECT ville FROM Employe WHERE nom in ('Jacques', 'Laurence', 'Marie-Claude')
--énumération L’ensemble énuméré est calculé une seule fois par la sous‐requête.
SELECT ville FROM Employe WHERE nom IN (SELECT nom FROM Etudiant WHERE age > 25); -- prédicat d’inclusion avec calcul par sous-requête)
Sélection (ou restriction de lʹextension) Les sélections ci‐dessous utilisent la base de données BD1. A chaque fois quʹun même attribut apparaît dans plus dʹune relation, il faut le préfixer par le nom de la relation et cela, pour éviter toute ambiguïté pour lʹinterpréteur. Pour quelques clauses SQL, lʹexpression algébrique correspondante sera formulée pour illustrer le passage de l’algèbre au langage SQL.
7.8 Expressions algébriques et requêtes SQL Toute requête SQL correspond à une expression algébrique et vice versa. Lister les articles vendus jusqu’à ce jour. L’expression algébrique correspondante est une projection d’une table de base.
Πarticle (Ventes)
En SQL, cette projection se traduit par la liste des attributs de la projection placée dans la clause SELECT.
SELECT article FROM Ventes;
La projection supprime les doublets de la réponse, tandis que la cluse SQL SELECT ci‐dessus ne le fait pas à moins dʹy ajouter le mot clé DISTINCT.
SELECT DISTINCT article FROM Ventes;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
36
b) Lister les ventes (avec tous leurs attributs) effectuées jusqu’à ce jour . Une simple projection fournit la réponse demandée.
Πnom,article, qte (Ventes)
La projection de tous les attributs peut s’exprimer par la liste des attributs ou l’astérisque placé dans la clause SELECT.
SELET * FROM Ventes;
Lister le nom des acheteurs de l’article nommé ‘a2’ :
Πnom(σVentes.article = ‘a2’(Ventes))
SELECT nom FROM Ventes WHERE Ventes.article ='a2';
/*(présume un type varchar2 pour article)*/
Ventes : nom* article* qte* Serge a1 3 Ted a2 5 André a1 2 Serge a2 4 Jocelyn a1 2 Serge a3 4 Valérie a7 3
Lucie a7 2 Serge a5 3 Serge a7 5 Christine a3 6 Serge a1 7
Figure 7.15 d) Lister le nom des acheteurs de l’article ‘a2’ avec une quantité supérieure ou égale à 3 . Lʹexpression algébrique pour calculer cette réponse comprend une sélection exécutée en premier, suivie dʹune projection pour ne conserver que les attributs demandés.
Πnom(σVentes.article =‘a2’and qte >= 3(Ventes))
Cette expression est convertie en SQL très facilement : le prédicat de sélection apparaît dans le WHERE et la liste des attributs de projection est ajoutée au SELECT.
SELECT DISTINCT Ventes.nom FROM Ventes WHERE article = 'a2' and qte >= 3;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
37
La condition peut être formulée avec plusieurs opérateurs de comparaison : =, !=, > =, <=, <, > et des prédicats du langage comme les suivants : BETWEEN x b1 AND b2, IN, LIKE, IS NULL, IS NOT NULL. De même, lʹexpression du prédicat peut être formulée avec plusieurs attributs. Exemple d’un prédicat conjonctif : … WHERE nom = ʹliseʹ and age = 25 Ce prédicat est équivalent au suivant : … WHERE (nom, age) IN (ʹliseʹ, 25) e) Lister le nom des acheteurs qui ont acheté entre 3 et 7 unités dʹun article.
Πnom(σqte between 3 And 7(Ventes))
SELECT nom FROM Ventes WHERE qte BETWEEN 3 AND 7; --les bornes sont incluses
f) Lister les acheteurs dont le nom commence par la lettre ʹSʹ au moyen de l’opérateur d’appariement LIKE .
SELECT nom FROM Ventes WHERE nom like 'S%';
NB : Le caractère générique % représente une chaîne composée de 0 ou n caractères qui vient après le S. Autres formes possibles : %S, %S%S, etc. Ce caractère spécial est très utile si la chaîne spécifiée dans le schéma est de longueur constante et si la chaîne recherchée est plus petite. Les caractères spéciaux peuvent être aussi combinés. Il est aussi possible de prendre en compte les caractères spéciaux comme des littéraux intégrés à la chaîne par lʹentremise du caractère dʹéchappement annoncé par le mot clé ESCAPE.
SELECT nom FROM Ventes WHERE nom LIKE ‘Sans\_Chagrin’ ESCAPE '\' ;
N.B. Le caractère ʹ\ʹ précède un caractère spécial (soit le _) pour indiquer au système quʹil doit être lu comme un littéral. Finalement, dans la version 10g et les suivantes les expressions régulières sont incluses et traitées lors de la recherche par Oracle. g) Lister les articles achetés par les personnes dont le nom est Deschesnes, Descheynes, Deschaines et Descheines.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
38
SELECT article FROM Ventes WHERE nom LIKE ‘Desch__nes’;
** Notez que le caractère _ (souligné) représente un caractère quelconque dans une position fixe de la chaîne. Voici une autre formulation du prédicat :
SELECT ARTICLE FROM VENTES WHERE NOM LIKE ‘Desch’||‘%nes%’;
Le deuxième % est nécessaire si l’attribut nom est déclaré CHAR(w) et non de longueur variable comme c’est le cas avec le type VARCHAR2(w). h) Lister les articles en inventaire et dont le prix de vente est entre 10 et 20 $ inclusivement :
SELECT article FROM Inventaire WHERE prixVente Between 10 and 20; ‐‐ Bornes incluses
À noter que la négation du prédicat se formule simplement par le NOT BETWEEN et permet de tester lʹexclusion de la valeur de lʹattribut de chaque valeur de lʹintervalle. Pour que le prédicat soit vrai il faut que la borne inférieure soit plus petite que la borne supérieure. Voici dʹautres formulations correctes, mais qui seront transformées par lʹoptimiseur du SGBD : ‐ avec le prédicat OR :
prixVente = 10 or prixVente = 11 Or ...prixVente = 20 Après transformation : prixVente BETWEEN 10 And 20 ‐ avec la négation :
NOT (prixVente = 10 or prixVente = 11 or ...prixVente = 20)
Après transformation : prixVente NOT BETWEEN 10 and 20 i) Lister les articles achetés par les clients Serge, Valérie et Ted :
SELECT article FROM Ventes WHERE nom IN ('Serge','Valérie','Ted');
Le même prédicat peut être formulé sous la forme d’une disjonction d’égalités :
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
39
(nom = 'Serge' or nom = 'Valérie' or nom = 'Ted') Le NOT permet aussi de tester lʹexclusion ou lʹabsence de nom parmi ceux de la liste énumérée. Avec la présence du NULL dans la liste énumérée, le prédicat est évalué correctement avec les constantes énumérées, mais ne sélectionne pas les tuples dont le nom est renseigné par l’indicateur null.
SELECT article FROM Ventes WHERE nom IN ('Serge', 'Valérie', null);
La recherche des ventes à un client inconnu est faite inutilement, car le nom est un attribut primaire qui ne peut pas être un null lorsque la clé primaire est spécifiée dans le schéma de la table. Suppression des doublets dans une réponse Les tuples identiques de la réponse peuvent être supprimés par une variante de la sélection soit le SELECT DISTINCT. L’ordre dʹaffichage des tuples sélectionnés peut être aussi spécifié par la clause ORDER BY. j) Lister les acheteurs ayant des noms différents.
SELECT DISTINCT nom FROM Ventes;
Quel est le résultat de la clause ci‐dessous?
SELECT DISTINCT nom, article FROM Ventes;
Les doublets sur les attributs nom et article (en tant que tuple de la réponse) sont supprimés. k) Lister en ordre alphabétique croissant de nom, les transactions de vente concernant les articles achetés en quantité plus grande que trois.
SELECT article, nom, qte FROM Ventes WHERE qte > 3 ORDER BY nom ASC;
Par défaut, la relation de la réponse constitue un seul ensemble de tuples trié en fin de calcul selon l’ordre croissant de nom (option ASC). L’option DESC permet un tri descendant sur le ou les attributs mentionnés dans la clause ORDER BY. L’attribut
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
40
utilisé comme critère de tri doit être obligatoirement dans la table obtenue par la projection (exprimée par la clause SELECT).
7.9 L’indicateur null et lʹévaluation dʹune condition Un attribut peut avoir un indicateur de l’absence d’une valeur, le null (6) si la spécification du schéma de la table l’autorise explicitement (NULL et NOT NULL). Cet indicateur peut être aussi défini par la clause DEFAULT IS NULL lequel est attribué à un attribut. Comme le traitement des indicateurs nulls est plus complexe, il est préférable de privilégier, lorsque cela est possible au regard de la sémantique, la contrainte NOT NULL pour un attribut. La norme ISO définit le traitement suivant pour le NULL dans les expressions de comparaison : a) Un NULL n’est jamais égal à une valeur quelconque y compris à lui‐même (0, blanc ou NULL) puisque le NULL n’est pas une valeur du domaine. Par exemple :
SELECT * FROM Employe WHERE salaire = null ; ‐‐ FALSE y compris lorsque salaire est inconnu
Les employés dont le salaire est inconnu ou absent ne sont par retrouvés avec cette formulation. En effet, l’indicateur null n’est pas une valeur du domaine, donc ne peut pas être l’objet d’une comparaison. b) Un NULL n’est jamais différent d’une autre valeur. En effet, parce que le NULL n’est pas une valeur du domaine.
SELECT * FROM Employe WHERE salaire <> 25000; --FALSE pour les tuples avec salaire NULL)
c) Un NULL n’est pas plus petit ou plus grand ni égal à une valeur.
SELECT * FROM Employe ; WHERE salaire < 19 000 ‐ ‐ (FALSE pour les tuples dont le salaire est NULL)
d) Le résultat des fonctions dʹagrégation (sauf exception) n’est pas modifié par les nulls des tuples, pour autant que certains non nulls existent dans le lot. L’exception est la fonction de comptage Count() qui peut ou pas prendre en compte les valeurs nulles. Ainsi, Count(*) compte les tuples et donc considère le comptage des lignes (tuples) sans égard à la présence des nulls. Par contre, le Count(age) ne compte que les valeurs non nulles pour cet attribut. Voici dʹautres exemples singuliers utilisant le null dans le prédicat. Ici, il y aura impression de la constante si le prédicat est évalué à VRAI.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
41
SELECT 'F' FROM DUAL WHERE 'Y' = NULL; -- aucun résultat !
SELECT 'F' FROM DUAL WHERE NULL = NULL; -- aucun résultat !
SELECT 'F' FROM DUAL WHERE NULL IN ('A','B',NULL); -- aucun résultat !
SELECT 'F' FROM DUAL WHERE'a' IN ('b', null); -- aucun résultat
Figure 7.16 Par contre, l’expression avec le prédicat IS NULL ou IS NOT NULL permet de traiter les tuples ayant l’indicateur de valeur nulle.
SELECT nom FROM Client WHERE credit IS NULL;
Cette expression donnera le nom des clients dont le crédit nʹest pas encore déterminé ou inexistant.
Exceptions dans le traitement du null Il y a certaines exceptions dans le traitement de l’indicateur d’absence de valeur, le NULL : a) Fonction DECODE : DECODE(note,'A','excellent','B','tres_bien','C','bien',NULL,'échec') Les tuples dont la note est ʹAʹ sont lʹobjet dʹun remplacement de cette note par la chaîne ʹexcellentʹ et si la note est NULL , elle est remplacée par la constante de chaîne ʹéchecʹ. b) Dans le GROUP BY
SELECT matricule, age FROM Employe GROUP BY matricule, age; -- null dans certains tuples.
Le groupement tiendra compte de la valeur NULL pour les attributs du GROUP BY. c) Avec le qualificatif DISTINCT :
SELECT DISTINCT qte --les nulls sont distincts; FROM Ventes;
d) Avec les opérateurs UNION, MINUS et INTERSECT qui tiennent compte des nulls.
Prédicat IS NULL et IS NOT NULL de SQL‐92 Ce prédicat est proposé dans la norme SQL‐86 afin de tester si une expression, qui est très souvent formée dʹun seul attribut, est évaluée à NULL lors de son instanciation par un tuple.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
42
SELECT * FROM Ouvrier WHERE age IS NULL ;
Tous les tuples de Ouvrier dans lequel lʹâge est inconnu (ou null) seront sélectionnés. La norme SQL‐92 a étendu cette notion pour y inclure le test de plusieurs attributs au moyen du constructeur de tuple. Ce dernier est de degré 1 sʹil est constitué dʹun seul attribut, tandis quʹil a un degré supérieur lorsquʹil y a plusieurs attributs.
SELECT * FROM Ouvrier WHERE(noAtelier, age) IS NULL; --expression de degré 2
Lorsque lʹexpression du prédicat contient deux attributs, elle est de degré 2. Elle est TRUE si les deux attributs ont une valeur NULL dans un même tuple.
Degré 1 E IS NULL E IS NOT NULL NOT E IS NULL NOT E IS NOT NULL Expression ‐‐> (E) si E = NULL TRUE FALSE FALSE TRUE AUCUN NULL dans E FALSE TRUE TRUE FALSE
Degré > = 2 E IS NULL E IS NOT NULL NOT E IS NULL NOT E IS NOT NULL Tous NULL dans E TRUE FALSE FALSE TRUE Quelques NULL FALSE FALSE TRUE TRUE Aucun NULL FALSE TRUE TRUE FALSE
Figure 7.16a Par contre, si lʹon veut sélectionner les ouvriers dont lʹun des deux attributs de l’expression est inconnu, il suffira de faire appel à la négation du prédicat.
SELECT * FROM Ouvrier WHERE NOT (noAtelier, age) IS NULL;
Cette clause est équivalente à la suivante :
SELECT * FROM Ouvrier WHERE noAtelier IS NULL or age IS NOT NULL;
La norme SQL‐92 introduit aussi de nouveaux prédicats du genre IS TRUE, IS UNKNOWN et IS FALSE. Ils permettent de tester la valeur de vérité dʹune condition pour chaque tuple dʹune table.
SELECT * FROM Usine WHERE (production >200 And ville = 'Québec') IS UNKNOWN;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
43
Les usines dont le volume de production ou la ville sont inconnus seront sélectionnées. En effet, si un des attributs est NULL, la condition est donc évaluée aussi à NULL et le prédicat qui vérifie la valeur de vérité UNKNOWN est donc TRUE.
Plus de détails concernant la notion dʹexpression SQL Une expression fournit, après évaluation, une valeur (et éventuellement le NULL) dʹun type donné. Une expression, notamment en SQL‐92, est formée de divers éléments ‐ constantes, variables, fonctions ‐ qui peuvent apparaître dans les clauses SELECT et WHERE. Une expression peut être aussi combinée avec dʹautres au moyen des opérateurs arithmétiques, logiques et de chaîne. Considérons maintenant la base de données suivante composée de quatre tables normalisées : Atelier (noAtelier*, chef, site, debit, volume, classe, noUsine,specialite, dateOuverture) Specialite (specialite*, classe, description) Usine (noUsine*, libelle, adresse, ville, codePostal, production) Ouvrier (nas*, nom, age, dateEmb, noAtelier) 1‐ Expression incluant un littéral Une expression peut être formulée avec une constante numérique, une chaîne, une date, un attribut, un temps et une estampille. Voici quelques exemples de constantes de types divers : entiers : ‐97, 45, +67 nombre décimal : 45.6, ‐34.567 virgule flottante : 1.56E2 chaîne : ʹprogrammation à objetsʹ chaîne UNICODE : ʹGʹæÕÕʹ nombre hexadécimal : XʹFFFFʹ date : ʹ1998‐12‐25ʹ temps : ʹ13:45ʹ ou 13.45 ou ʹ1:45 PMʹ estampille : ʹ2000‐01‐06‐13:45:00:00000’
Figure 7.16b SELECT volume * 1.5 FROM Atelier WHERE debit <= 500 * .6;
L’expression arithmétique est évaluée une fois au début du traitement de la requête. 2‐ Expression incluant un nom dʹattribut avec ou sans opérateur La plus simple des expressions est un attribut seul, tandis que la transformation d’une valeur ou d’un attribut par les fonctions et les opérateurs fournit aussi une expression qui est plus complexe. Exemples d’expression avec les attributs :
volume nom || ʹ ʹ || prenom Usine.production volume * prixUnitaire
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
44
SELECT volume * 1.5 FROM Atelier Where debit > volume * .2 ;
Une expression complexe est construite en combinant plusieurs expressions au moyen des opérateurs suivants : ‐ Opérateurs arithmétiques: +, ‐, *, /; Exemple évaluée pour chaque tuple testé : (tarif * heures * 52) / (1.8 * bonus) - Opérateur de concaténation : || Exemple : nom || '--' || adresse /* insertion de deux tirets 3‐ Expression incluant un appel de fonction interne et externe Tout appel de fonction est une expression dont lʹévaluation sous‐tend lʹexécution du corps de la fonction. Une fonction peut être aussi combinée avec les opérateurs arithmétiques et de chaîne pour retourner une valeur typée.
LENGTH(adresse || ville) SUBSTR(code_postal, 1,3) Trunc(salaire * 1.2, 0) Trunc(date_in, 'YYYY' )
Round(salaire,0)--arrondi
SELECT Concat(libelle||adresse) FROM Usine WHERE SUBSTR(codePostal, 1, 3) = 'G1K';
Une expression peut comprendre une fonction dont un argument peut être aussi un appel de fonction. Exemple dʹemboîtement de fonctions :
SELECT U.libelle, U.adresse, U.noUsine FROM Atelier A, Usine U WHERE A.noUsine = U.noUsine and LENGTH(SUBSTR(site,1,(INSTR(site,'ing')))) = 5;<--
4‐ Expression avec une constante de type Date Une expression dans laquelle une constante entière est ajoutée à une date fournit une autre date. Souvent, l’unité par défaut pour un entier ajouté à une date est le jour (DAYS). Les autres unités ne sont pas toujours implémentées. Par exemple, Oracle offre que l’unité DAYS. Exemples d’unités de date :
DAYS MONTHS HOURS MINUTES
SELECT DateE + 3 DAYS /* ajout des jours à une date */
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
45
FROM Ouvrier WHERE TRUNC(DATE_E,'YY')BETWEEN TRUNC(1992,'YY') And TRUNC(1998,'YY');
La fonction TRUNC() permet de tronquer la date conformément au masque et enlève toutes les autres parties incluses dans la date. Dans l’exemple suivant, les embauches entre 92 et 98 sont affichées avec une date augmentée de 30 mois.
SELECT DateE + 30 MONTHS /* date augmentée de 30 mois */ FROM Ouvrier WHERE To_Number(To_Char(DATE_E,'YY')) Between 92 And 98;
5‐ Expression incluant une variable hôte Une variable hôte (dont la valeur est fournie par une variable du langage de programmation) dans une clause SQL est déclarée dans le contexte d’un programme de type L3G (ex.: C, C++, Java et elle est précédée du caractère deux points ( : ) comme délimiteur de variable hôte. Exemple (:x) :
SELECT volume + :x FROM Atelier;
Le caractère deux points dans :x indique à lʹinterprète SQL quʹil sʹagit dʹune variable hôte et que la valeur de cette variable est accessible en lecture et en écriture. 6‐ Expression incluant une variable dʹenvironnement ou une fonction particulière sans argument. Voici quelques fonctions sans argument : CURRENT DATE; CURRENT SERVER USER, USER implémentées dans certains SGBD :
SELECT A.volume * 1.5 FROM Atelier A, Ouvrier O WHERE A.noAtelier = O.noAtelier and O.dateEmb = CURRENT DATE;
7‐ Expression incluant une structure CASE (prévu dans SQL‐92) Cette clause CASE est une expression qui fournit après évaluation, une constante de type chaîne de caractères ou numérique.
CASE classe WHEN SUBSTR(classe, 1,1) = 'A' THEN 'excellent' WHEN SUBSTR(classe, 1,1) = 'B' THEN 'très bon' WHEN SUBSTR(classe, 1,1) = 'C' THEN 'bon' END;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
46
Exemple : SELECT noSpecialite,
CASE noSpecialite WHEN noSpecialite = 1 THEN 'fraisage'||'aluminium' WHEN noSpecialite = 2 THEN 'perçage du fer' WHEN noSpecialite = 3 THEN 'tournage acier' ELSE 'autres opérations'
END, volume FROM Atelier WHERE site = 'Québec';
Sous‐requête à valeur unique (scalaire) Lʹévaluation dʹune sous‐requête qui donne une seule valeur typée est aussi une expression. Dans lʹexemple ci‐dessous, la sous‐requête est évaluée dès le départ et sa réponse, composée obligatoirement dʹun seul numéro d’atelier, est un scalaire qui est substituée à la sous‐requête avant de poursuivre lʹévaluation.
SELECT nom, dateEmb, noAtelier FROM Ouvrier WHERE noAtelier = (SELECT noAtelier FROM Atelier WHERE site = 'Québec');
Tri des données du résultat La clause ORDER BY est exécutée à la toute fin du calcul et permet dʹordonner les tuples de la réponse selon un ou plusieurs attributs ou alias utilisés dans le SELECT.
SELECT nom, ville FROM Empl WHERE age >20 ORDER BY nom, ville ASC ;
Tri avec un alias :
SELECT noVente, montant *.15 [as] taxe FROM Ventes WHERE mois = 'juin' ORDER BY taxe DESC; <-- tri décroissant par alias
7.10 Conversion de type dans SQL : CAST (SQL‐92) Lʹopérateur CAST() sous‐tend une opération de transposition du type dʹune valeur ou celui d’un résultat ou dʹune expression (y compris le NULL) en celui dʹun autre type. Comme SQL est un langage typé, lʹopérateur CAST() permet plus de souplesse dans la formulation des expressions. Par exemple, lʹajout dʹun attribut numérique à un autre de type chaîne est autorisé à la seule condition que la chaîne soit au préalable l’objet d’une conversion de type. Il n’est possible de faire une conversion que pour changer les caractéristiques dʹun même type. Exemples : CAST (classe as INTEGER) -- le type chaîne de l’attribut classe est converti en entier.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
47
Exemple : Select classe,
CASE classe WHEN CAST(classe as INTEGER) = 1 Then . . . ;
La transposition suivante permet de modifier les caractéristiques du type de l’attribut :
CAST (nom as char(45)) pour modifier la longueur de la chaîne; CAST (volume as decimal(2,4)--modifie nb chiffres après point décimal
SELECT U.production, cast(A.debit as integer) * 3 FROM Usine U, Atelier A WHERE Cast(ville as varchar2(25))='Québec'And U.noUsine = A.noUsine;
En général, les conversions automatiques sont possibles pour plusieurs changements de type , notamment dʹun type numérique vers tout autre type numérique. Toutefois, la conversion signale une erreur lorsquʹune valeur numérique cible a une taille insuffisante pour exprimer totalement la valeur de la source. Cependant, si lʹespace ne permet de loger que les chiffres décimaux avant le point décimal, il y aura alors un arrondi du nombre converti. Les conversions possibles pour SQL‐DB2‐V2 sont présentées sommairement dans le tableau ci‐dessous. Des conversions similaires sont autorisées avec Oracle et les autres systèmes.
type de la source type de la cible char, varchar, clob, long varchar char, varchar, clob, long varchar char, varchar char, varchar date, time, timestamp char, varchar date date time time blob blob timestamp date, time, timestamp graphic, vargraphic, long vargraphic graphic, vargraphic, long vargraphic smallint, integer, decimal, double smallint, integer, decimal, double
Figure 7.16c La conversion est possible selon le tableau en passant par le chemin date ‐> char. Le même résultat peut être obtenu par la fonction TO_CHAR() fournie par les diverses implémentations de SQL, notamment celle dʹOracle. Par exemple : la conversion de lʹannée dont le type est Date : la conversion date vers char est possible avec la clause : CAST (annee as char(8)).
SELECT Cast(nom as char(15)) FROM Ouvrier;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
48
Supposons que la date dʹouverture de lʹatelier est de type Date et que celle de lʹembauche dʹun ouvrier est de type chaîne de caractères. Pour faire une jointure entre les deux tables Atelier et Ouvrier, il faut effectuer une conversion de type avec lʹattribut de jointure dateEmb.
SELECT O.nom, A.noAtelier FROM Atelier A, Ouvrier O WHERE A.dateOuverture = CAST(O.dateEmb as Date); SELECT CAST(nom as varchar(50) [as] nomFamille FROM Ouvrier WHERE age < 30;
Cas spécial : Compatibilité de deux structures de table dans une opération binaire telle que lʹunion. Certaines implémentations de SQL ne permettent pas dʹexécuter une requête pour obtenir la liste combinée des ouvriers qui travaillent à lʹusine et ceux qui travaillent sur les chantiers, si la chaîne est déclarée avec des longueurs différentes.
OuvrierUsine (nas* : number(4,0) , usine : varchar2(2)) OuvrierChantier (nas* : number(4,0),chantier : varchar2(4))
OuvrierUsine : nas* usine OuvrierChantier : nas* chantier
234 u1 873 C500 456 u2 La réponse recherchée est une table de 3 colonnes, puisque les attributs usine et chantier ne sont par entièrement compatibles.
nas* usine chantier 234 u1 null 456 u2 null 873 null C500
Figure 7.16g Pour effectuer une telle union et obtenir la liste demandée, il faut avoir une compatibilité des schémas. Il y aura donc augmentation du schéma de OuvrierUsine en insérant une colonne d’indicateurs de nulls typés comme une chaîne varchar(4). Il en sera de même avec OuvrierChantier. La requête suivante devient donc calculable :
SELECT nas, usine, CAST(NULL as varchar(4)) FROM OuvrierUsine UNION SELECT nas, CAST( NULL as varchar(2)), chantier FROM OuvrierChantier;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
49
Voici une autre requête qui donne essentiellement les mêmes données avec un arrangement différent dans lequel il nʹest plus possible de distinguer les chantiers des usines, sauf si la valeur même de la donnée le permet.
SELECT nas, CAST(usine as varchar(4)) [as] lieu_de_travail FROM OuvrierUsine UNION SELECT nas, chantier as varchar(4) [as] lieu_travail FROM Ouvrier_Chantier;
réponse : nas lieu_travail
234 u1 456 u2 873 500 <‐ chantier
7.11 Emboîtement des requêtes (jointure) Une requête emboîtée7 (nested query) est composée d’une requête principale dite de niveau ou bloc 1 avec au moins une sous‐requête de niveau ou bloc 2 dont lʹévaluation est préalable à celle de la requête supérieure de niveau 1. Il peut y avoir jusquʹà 16 niveaux dʹemboîtement. Il y a aussi une requête emboîtée et corrélée. Dans une telle requête corrélée, une des variables (attribut) de la sous‐requête est globale, c’est‐à‐dire qu’elle prend sa valeur dans le tuple courant obtenu par la requête principale (de niveau 1). Avec une telle requête corrélée, la sous‐requête est évaluée à chaque changement de la valeur globale, i.e. à chaque lecture d’un tuple dans la table de la requête principale. La réponse à une requête emboîtée n’est formée qu’avec les attributs de la requête principale ; ceux de la sous‐requête n’étant pas visibles au premier niveau. Voici quelques exemples de requêtes comprenant une sous‐requête mais sans corrélation. a) Lister le nom des personnes qui ont acheté des articles qui ne sont plus en inventaire. Pour calculer cette réponse, il y a d’abord présélection des articles encore en inventaire (dont le coût nʹest pas nul), formulée par une sous‐requête. L’affichage est fait avec juste les attributs de la relation du bloc 1. Les articles vendus sont sélectionnés que s’ils ne sont pas inclus parmi ceux encore en inventaire.
SELECT nom FROM Ventes WHERE article NOT IN <-- prédicat d’inclusion (SELECT article FROM Inventaire WHERE cout is NOT NULL);
Le calcul de la sous‐requête (de niveau 2) est entièrement effectué (matérialisé) avant celui de la requête principale. En effet, la sous‐requête ne renferme aucun attribut global et son calcul fournit une table dont le contenu est constant pour cette requête. Ce résultat
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
50
intermédiaire est constitué de tuples qui constituent un ensemble transitoire stocké dans une table temporaire utilisée pour lʹévaluation de la requête principale. La même requête peut être formulée avec une jointure et des alias de relation:
SELECT nom FROM Ventes [as] V, Inventaire [as] I WHERE V.article = I.article and I.cout is NOT NULL;
Lʹalias est défini avec ou sans le mot clé AS selon la version de la norme SQL implémentée par le SGBD. Le calcul de la jointure se fait par un balayage des deux tables avec vérification de la condition de jointure formulée avec les deux attributs partageant le même domaine. Au besoin, ces deux attributs sont libellés différemment, mais doivent avoir un type ou un domaine compatible. Une jointure peut donc être exprimée par une requête utilisant un prédicat dʹinclusion. Très souvent, la jointure exploite le lien d’association entre les tables Inventaire et Ventes par l’entremise de la clé primaire et de la clé étrangère. La jointure n’est cependant pas limitée à ce cas. Ventes : nom* article* qte* Inventaire : article* prixVente cout Serge a1 3 a5 12 8 Ted a2 5 a1 10 5 André a1 2 a2 20 15 Serge a2 4 a7 15 15 Jocelyn a1 2 a3 25 null Serge a3 4 a16 45 34 Serge a5 3 Serge a7 5 Christine a3 6 Serge a1 7
Figure 7.17 Voici un autre exemple de la formulation de la jointure en SQL‐92. Le mot clé JOIN permet d’exprimer explicitement la notion de jointure :
SELECT V.nom FROM Ventes as V JOIN Inventaire as I ON V.article = I.article and I.cout is NOT NULL;
La condition de jointure peut être incluse dans la clause From avec le mot clé ON :
SELECT V.nom FROM Ventes as V JOIN Inventaire as I ON V.article = I.article WHERE I.cout is NOT NULL;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
51
Le ON spécifie la condition de jointure, tandis que le WHERE exprime un filtrage avec les autres attributs. b) Lister le nom des clients qui ont acquis des articles en rupture de stock. Cet état de l’inventaire est marqué par l’absence de coût (indicateur null) pour un article en inventaire.
SELECT nom From Ventes WHERE article IN (SELECT article FROM Inventaire
WHERE cout is NULL );
Ventes : nom* article* qte* Inventaire : article* prixVente cout Serge a1 3 a5 12 8 Ted a2 5 a1 10 5 André a1 2 a2 20 15 Serge a2 4 a7 15 15 Jocelyn a1 2 a3 25 null Serge a3 4 a16 45 34 Valérie a7 3 Lucie a7 2 Serge a5 3 Serge a7 5 Christine a3 6 Serge a1 7
Figure 7.17a Une telle requête fait appel à un test dʹinclusion par lʹentremise du prédicat IN afin de ne sélectionner que les articles qui ne sont pas dans lʹinventaire courant. Pour chaque tuple de la table Ventes, il y a rétention de ce tuple dans la réponse si, et seulement si, le prédicat est TRUE. Si le prédicat est évalué à UNKNOWN, le tuple de Ventes nʹest pas retenu. De plus, l’information affichée ne doit provenir que de la relation de premier niveau.
Expression de table Parmi les exemples précédents, il y a ceux dont la sous‐requête est évaluée dès le début pour retourner une valeur scalaire. Il est aussi prévu dans la norme SQL‐92 quʹune sous‐requête retourne une table temporaire qui peut être nommée. Une telle expression peut être similaire à une vue relationnelle. Exemple : SELECT ArtProfit.article, ArtProfit.marge*1.15 AS margeplus FROM (SELECT article, (prixVente ‐ cout) as marge FROM Inventaire WHERE prixVente > cout) AS ArtProfit WHERE ArtProfit.article = ʹa1ʹ
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
52
ORDER BY margeplus; Dans cette clause, lʹexpression de table donne une table dont le nom est ArtProfit et son schéma est global à la clause. Il peut donc être réutilisé dans la clause From ou dans les opérations subséquentes conduisant à la réponse. Finalement, lʹalias margeplus est connu au moment du tri et peut donc être un critère de celui‐ci.
Voici une autre expression de table qui permet de grouper les tuples par la marge, elle‐même calculée par lʹexpression (prixVente ‐ cout). Comme le GROUP BY nʹaccepte pas les expressions, il faut définir et travailler avec un alias.
SELECT ArtProfit.marge, Count(ArtProfit.article) FROM (SELECT article, round((prixVente - cout),0) AS marge FROM Inventaire WHERE prix_vente > cout) as ArtProfit GROUP BY ArtProfit.marge;
ALL et ANY et un ensemble de valeurs Le mot clé ALL signifie pour chaque valeur ou pour chacune des valeurs et ANY, avec le sens de SOME, signifie pour au moins une valeur sélectionnée par la sous‐requête. Si la sous‐requête identifie au moins un tuple, elle est évaluée à TRUE, sinon elle est évaluée à FALSE. Cet opérateur indique comment les valeurs retournées par une sous‐requête sont prises en considération par le WHERE de la requête. Exemples : a) Lister les articles déjà vendus en quantité inférieure ou égale à celles des ventes faites pour l’article ʹa2ʹ : SELECT distinct article FROM Ventes WHERE qte < all(SELECT qte FROM Ventes WHERE article = 'a2');
nom* article* qte* tableT : qte Serge a1 3 5 Ted a2 5 4 André a1 2 Serge a2 4 Jocelyn a1 2 réponse: article Serge a3 4 a1 Valérie a7 3 a7 Lucie a7 2 a5 Serge a5 3 Serge a7 5 Christine a3 6 Serge a1 7
Figure 7.18
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
53
Remarque : La quantité sera plus petite que le plus petit nombre d’articles ʹa2ʹ vendus jusqu’à présent par toute transaction inscrite dans l’extension courante. La fonction Min() permet aussi une autre formulation avec une sous‐requête.
SELECT article FROM Ventes WHERE QTE < ( SELECT Min(qte) FROM VENTES WHERE article = 'a2');
La complexité de cette requête tient au fait que le prédicat de sélection réfère à une quantité qui doit être calculée d’abord et cela, avant que le premier tuple de lʹextension de Ventes ne soit testé. La fonction Min() a un ensemble de valeurs comme argument et retourne une seule valeur, soit la plus petite parmi celles de l’ensemble‐argument. b) Lister les articles vendus en quantité supérieure à celles des ventes de l’article ʹa7ʹ :
SELECT article FROM Ventes WHERE qte > all (SELECT qte -- plusieurs valeurs FROM Ventes WHERE article = ‘a7’);
Dans cet exemple, la plus grande valeur de l’attribut qte est utilisée pour la sélection. Voici une autre formulation de la même requête. Elle est possible en raison de la relation dʹordre implicite aux valeurs entières du sous‐ensemble calculé.
SELECT article FROM Ventes WHERE qte > (SELECT max(qte)
FROM Ventes WHERE Ventes.article = 'a7');
La sous‐requête est matérialisée et la table temporaire est utilisée pour la recherche de la plus grande valeur de l’attribut qte de la relation Ventes. c) Lister les articles vendus en quantité plus grande que celle de nʹimporte quel article vendu.
SELECT article FROM Ventes WHERE qte > any (SELECT qte FROM Ventes);
Autre formulation :
SELECT article FROM Ventes WHERE qte > (SELECT Min(qte)FROM Ventes);
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
54
Les opérateurs relationnels peuvent être combinés pour former diverses conditions exotiques de comparaison : < = any, <= all, != any, != all, < any, < all.
7.12 Requête corrélée Un requête corrélée est une requête emboîtée avec au moins un attribut global. La réponse à une requête corrélée est composée de tuples sélectionnés dans une table de niveau 1 en fonction de la présence d’un autre tuple dans une autre table de niveau 2. C’est une requête dite de corrélation qui se formule au moyen d’une sous‐requête utilisant une variable‐attribut globale. L’information qui peut être affichée provient uniquement de la première relation (externe à la requête ou de premier niveau). Ce type de requête implémente le quantificateur existentiel dont la formulation correspond aussi à une jointure. Exemple : Lister les transactions de vente (donc tous les attributs de cette table) pour les articles encore en inventaire. Un article est en encore en inventaire si l’attribut cout a une valeur autre que l’indicateur NULL.
SELECT * FROM Ventes [as] V WHERE Exists (SELECT article FROM Inventaire [as] I
WHERE V.article = I.article And cout is NOT NULL);
où V.article est la variable‐attribut globale qui est instanciée pour chaque tuple de la table Ventes. Le préfixe avec l’alias est nécessaire pour éviter toute confusion dans la corrélation avec la sous‐requête. Le prédicat WHERE est évalué pour chaque tuple de la relation Ventes. S’il est vrai pour un tuple, celui‐ci est retenu dans la réponse. La clause EXISTS est vraie si au moins un tuple est trouvé par la sous‐requête. Elle permet aussi de formuler une jointure avec la négation du prédicat.
nom* article* qte* Inventaire : article* prixVente cout Serge a1 3 a5 12 8 Ted a2 5 a1 10 5
André a1 2 a2 20 15 Serge a2 4 a7 15 15 Jocelyn a1 2 a3 25 null Serge a3 4 a16 45 34 Valérie a7 3
Lucie a7 2 Serge a5 3 Serge a7 5 Christine a3 6
Serge a1 7 Figure 7.19
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
55
Par exemple, une requête corrélée négative est utile pour lister les pièces déjà vendues, mais qui ne sont plus en inventaire.
SELECT nom, article, qte FROM Ventes [as] V WHERE not exists
(SELECT article FROM Inventaire [as] I WHERE V.article = I.article and cout is null); L’attribut V.article est global et prend sa valeur à l’extérieur de la sous‐requête. Avec un tel attribut, la sous‐requête n’est pas évaluée totalement au départ. Lʹévaluation est faite pour chaque instanciation de l’attribut article avec la valeur fournie par un tuple de la relation Ventes. Dans une telle requête, les seuls attributs pouvant être inclus dans la réponse sont ceux de la table de premier niveau. Ainsi, la requête suivante nʹest pas exécutable parce que l’attribut coût en est un de la table référencée par la sous‐requête.
SELECT V.nom, V.article, V.qte, I.cout FROM Ventes V WHERE not exists (SELECT I.article FROM Inventaire I WHERE I.article = V.article);
Autres exemples avec BD3 : Dotation des ressources humaines Reprenons le schéma de la BD3 libellé DOTATION et composé des trois relations ci‐dessous.
Empl (noEmpl*, nom, tauxH, noPoste) Assignations (noPoste*, noEmpl*, debut, quart) Postes (noPoste*, titre)
Deux contraintes référentielles sont aussi définies. a) Lister le nom et le taux horaire des employés qui ont au moins une assignation de travail :
SELECT nom, tauxH FROM Empl [as] E WHERE EXISTS (SELECT * FROM Assignations [as] A WHERE E.noEmpl = A.noEmpl);
Le prédicat de la sous‐requête utilise un attribut global préfixé par E. La valeur d’un tel attribut est fixée à l’extérieur de la sous‐requête pour chaque tuple lu lors du balayage de la table. Notez aussi que les attributs de la réponse ne sont pas obligatoirement préfixés parce que seuls les attributs de Empl peuvent être inclus dans la réponse. b) Lister le nom et le taux horaire des employés qui n’ont pas d’assignation de travail.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
56
SELECT nom, tauxH FROM Empl [as] E WHERE NOT EXISTS (SELECT *FROM Assignations [as]
WHERE E.noEmpl = A.noEmpl); Un alias de relation peut être utilisé pour remplacer le nom d’une table de base de données dans une clause WHERE. Dans ce cas, l’alias préfixe un attribut global. b)Lister le nom et le titre du poste des employés qui ont une assignation de travail. Dans ce cas il faut afficher les attributs provenant de deux relations et cela en fonction de l’existence d’un tuple dans une troisième relation.
SELECT nom, titre FROM Empl [as] X, Postes [as] Y WHERE X.noPoste = Y.noPoste and EXISTS (SELECT * FROM Assignations WHERE X.noEmpl = Assignations.noEmpl);
Les tables Empl et Postes sont en premier l’objet d’une jointure pour former la relation intermédiaire interne (connue seulement par le système) dont le schéma est le suivant :
Rtemp (noEmpl, nom, tauxH, noPoste, noPoste, titre) Chaque tuple de cette relation intermédiaire est prise en compte successivement pour vérifier la clause EXISTS. Lorsque le prédicat EXISTS est vérifié et si la réponse de la sous‐requête est non vide, alors le tuple de premier niveau est retenu dans la réponse. La variable de tuple X est globale et prend sa valeur à lʹextérieur de la sous‐requête.
Expression de table partagée (DB2) Avec certaines clauses il est utile de référer à une vue dans une sous‐requête même si la requête principale est très occasionnelle. Par exemple, pour trouver les ateliers dont lʹâge moyen des ouvriers dépasse celui de lʹensemble des ouvriers, il est possible dʹutiliser une vue relationnelle par une clause CREATE VIEW. Atelier (noAtelier*, chef, site, debit, volume, classe, noUsine, noSpecialite) Specialite (noSpecialite*, classe, description) Usine (noUsine*, libelle, adresse, ville, codePostal, production) Ouvrier (nas*, nom , age, dateEmb, noAtelier)
CREATE VIEW Ancien (noAtelier, age_moy_at) AS SELECT noAtelier, AVG(age) [as] age_moy_at FROM Ouvrier GROUP BY noAtelier;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
57
La requête suivante permet dʹafficher le numéro des ateliers où lʹâge moyen des ouvriers est supérieur à lʹâge moyen des ouvriers peu importe lʹatelier.
SELECT noAtelier FROM Ancien -- référence à la vue relationnelle WHERE age_moy_at >(SELECT AVG(age_moy_at) FROM Ancien);
Le calcul de la réponse sous‐tend la création dʹune vue correspondant à une table temporaire. Lors de sa formation, il y a accès au catalogue du système et calcul de la vue (matérialisation) pour chaque référence à celle‐ci dans une clause SQL. Dans lʹexemple ci‐dessus, il y a deux références à la vue relationnelle Ancien, qui entraînent normalement deux matérialisations de la vue. De plus, entre les deux matérialisations, une mise à jour peut être faite sur la base de données par une autre application changeant lʹextension de Ancien. Pour éviter ce double calcul et lʹincohérence de lecture, le système DB2‐V2 exploite la notion dʹexpression de table partagée. Il sʹagit de la factorisation de la vue dans une clause qui débute avec le mot clé WITH placé en tout début de la requête :
WITH Ancien (noAtelier, age_moy_at) AS (SELECT A.noAtelier, AVG(O.age)FROM Atelier A, Ouvrier O WHERE A.noAtelier = O,noAtelier GROUP BY A.noAtelier SELECT noAtelier FROM Ancien WHERE age_moy_at > SELECT (AVG(age)FROM Ancien);
Cette clause effectue un seul calcul de lʹextension de lʹexpression de table identifiée par le WITH et du même coup supprime tout danger dʹincohérence lors du calcul de la réponse suite aux actions d’une autre application.
7.13 Fouille hiérarchique de lʹextension dʹune relation en Oracle Les données organisées avec une structure arborescente et stockées dans une relation de base ou obtenues par calcul peuvent être extraites par une expression SQL (Oracle) caractérisée par un CONNECT BY. On a vu que lʹalgèbre relationnelle ne permet par dʹexploiter directement les structures arborescentes, parce quʹelle nʹest pas récursive.
parent : nas* pren nase* prene 50 Paul 55 Lise 55 Lise 65 Jacques 65 Jacques 75 Patricia 65 Jacques 95 Sylvie 95 Sylvie 185 André 55 Lise 100 Luce 100 Luce 200 Renée
Figure 7.20
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
58
Les structures arborescentes peuvent représenter différentes situations comme la généalogie des personnes, lʹinclusion des parties dans un tout, la composition dʹun équipement formé avec des composants mécaniques ou la hiérarchie des postes dans une organisation. Voici un exemple dʹune table représentant la hiérarchie parentale. Lʹexemple de la Figure 7.20 concerne la représentation des personnes qui sont un parent dʹune ou de plusieurs autres personnes, toutes représentées dans la même relation de base. Toute personne peut être successivement parent et enfant selon le niveau de représentation dans l’arbre. Chaque personne est identifiée par son nas et son prénom. La personne qui est parent de lʹautre apparaît en première position dans le tuple. Cette relation représente donc les personnes ayant un rang de parenté du type parent‐enfant entre elles. Ainsi, Jacques, dont le nas est 65, est parent de Patricia et de Sylvie, dont le nas est respectivement 75 et 95. Pour trouver toutes les personnes ayant une filiation donnée, le système exécute une requête initiale pour trouver tous les tuples de la relation, considérés comme formant lʹensemble de départ D (identifié par un START WITH). A partir de cet ensemble de départ, le système exécute une requête pour trouver en premier toutes les personnes qui sont juste en dessous et ces tuples sont notés par le système comme étant de niveau 1. Ils sont ajoutés à lʹensemble de départ. Les tuples ajoutés ont un lien parental direct avec une personne de lʹensemble D. A chaque fois quʹun tuple de lʹensemble de départ D est traité par la recherche, il est marqué comme traité. Le résultat de la recherche est ajouté pour obtenir un ensemble de départ augmenté D*. La deuxième opération se poursuit avec chaque tuple de niveau 1 pour trouver ceux de niveau 2 et ainsi de suite. Lʹarrêt est signalé lorsque toutes les personnes de lʹensemble de départ augmenté D* ne sont plus parent ou encore lorsquʹils ont tous été testés (condition dʹarrêt).
Sommaire de lʹalgorithme Les tuples de lʹensemble de départ sont trouvés avec la clause START WITH et sont regroupés dans lʹensemble setD. Pour chaque tuple parent (en commençant avec les parents des tuples de lʹensemble de départ, soit setD), les tuples enfants sont trouvés par
50, Paul
55, Lise
65, Jacques100, Luce
95, Sylvie200, Renée 75, Patricia
185, André
Enfant de
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
59
le prédicat CONNECT BY et ne sont retenus dans la réponse que sʹils vérifient le WHERE. Lʹopération recommence pour trouver les enfants de ces tuples, et ainsi de suite. La condition dʹarrêt est vérifiée lorsquʹil nʹy a plus de tuples parentaux dans setD*, cʹest‐à‐dire quʹaucun tuple de setD* a un enfant.
Algorithme de calcul dʹune requête hiérarchique Dans cet algorithme, il y a deux métafonctions : f_enf(t) et f_start(p) ainsi qu’une variable de tuple t. f_enf(t): fournit les tuples enfants (par le CONNECT BY) du tuple t. f_start(p): fournit les tuples de D qui vérifient le prédicat p dans R; début; SetD = vide; SetD = SetD + f_start(p); /*tuples du start with*/ SetD* = SetD; For each t non marqué in SetD* [WHERE p1];/*filtre avec WHERE*/ Marquer le tuple t courant du SetD*; SetD* = SetD* + f_enf(t); /*tuples identifiés par Connect By */ end_for; Affiche SetD*;‐‐(au besoin avec LEVEL); Fin.
Figure 7.22 Cet algorithme est celui du SGBD Oracle pour implémenter lʹexploration itérative dʹune arborescence. L’opérateur unaire PRIOR est associé au CONNECT BY qui précise au moyen dʹune condition la relation entre le tuple parent et les tuples enfants et cela, au moyen dʹun attribut qui concrétise le lien. Clause SQL (Oracle) pour fouiller une arborescence :
SELECT LEVEL, nas, LPAD(' ',2*(LEVEL-1))||TO_CHAR(nase) [as] NASEENF FROM Parent START WITH nas = 55 --calcul de D CONNECT BY PRIOR nase = nas --PRIOR attribut du parent ORDER BY LEVEL, nas;
LEVEL NAS NASEENF (alias de NASEE)
1 55 65 1 55 100 2 65 75 2 65 95 2 100 200 3 95 185
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
60
Figure 7.23 Les requêtes récursives ou hiérarchiques sont prévues dans SQL‐93. Toutefois, Oracle, DB2 et WATCOM SQL ont déjà implémenté leur propre version de la recherche hiérarchique. Le système DB2‐V2 a une implémentation plus poussée de cet algorithme afin de permettre aussi les requêtes récursives. Prenons un autre cas, celui de la table Produit qui représente les pièces finies obtenues par lʹassemblage de pièces fabriquées en sous‐traitance. Ces dernières pouvant être aussi composées avec des pièces elles‐mêmes fabriquées en sous‐traitance. Le produit p1 comprend 2 composantes p22 et 3 composantes p23. Le produit p20 est composé lui‐même de 6 composantes p26 et 4 composantes p34. Le produit p23 comprend 2 composantes p20. Comment formuler la requête pour afficher les composantes et les sous‐composantes des produits p1?
Produit : noProd* composante* qte p1 p22 2 p1 p23 3 p20 p26 6 p20 p34 4 p2 p33 3 p23 p20 2
Figure 7.23a Une requête d’exploration pseudo récursive est toute désignée pour effectuer cette recherche.
SELECT LEVEL, noProd, composante FROM Produit Start with noProd = ‘p1’ Order by Level;
La réponse est constituée des pièces : {p1, p22, p23, p20, p26, p34}.
7.14 Récursivité avec le SGBD DB2‐V2 Une expression de table en DB2‐V2 a une autre propriété très importante, soit la récursivité dont elle peut être lʹobjet dans son traitement. En effet, une telle expression peut être définie en se référant à elle‐même. Par exemple, si dans la relation Ouvrier on ajoute lʹattribut nasChef, il devient possible de demander quels sont les ouvriers qui sont sous la supervision directe ou indirecte du chef ʹPatronetʹ dont le nas est ʹn1ʹ. En effet ʹPatronetʹ peut superviser directement lʹemployé ʹGuyʹ qui supervise lʹemployée ʹPatriciaʹ qui à son tour supervise lʹemployé ʹJulienʹ. La réponse à la première question est donc lʹensemble suivant {‘Guy’, ‘Patricia’, ‘Julien’}
Ouvrier (nas*, nom , age, dateEmb, nasChef, noAtelier)
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
61
Pour calculer cette réponse, il est possible de définir une expression de table qui est calculée une première fois pour former lʹensemble setD, et ensuite recalculée aussi longtemps que de nouveaux tuples sont ajoutés à lʹensemble précédent pour former setD*. Lʹalgorithme utilisé est similaire à celui de Oracle. Il consiste à calculer dès le départ lʹensemble setD, i.e. les personnes supervisées directement par le chef ʹPatronʹ dont le nas est ʹn1ʹ. Cʹest la sous‐requête initiale. Ensuite, il faut calculer récursivement les Ouvriers supervisés par chaque ouvrier de lʹensemble initial. Les employés doivent être reliés aux tuples existants dans lʹensemble précédent. La syntaxe en DB2 est la suivante :
WITH Supervises (nas) AS <-- ensemble de départ (SELECT nas -- requête initiale FROM Ouvrier O WHERE O.nasChef = 'Patronet') UNION ALL
(SELECT O.nas <-- requête récursive FROM Supervises [as] S, Ouvrier [as] O WHERE O.nas = S.nas);
SELECT nas <-- requête finale FROM Supervises;
Lʹopérateur UNION ALL est similaire à lʹUNION, incluant cependant dans le résultat tous les tuples, y compris ceux qui sont dupliqués.
7.15 Fonctions SQL Les fonctions du langage8 sont divisées en deux groupes selon qu’elles s’appliquent à un tuple (fonction scalaire) ou à un ensemble de tuples (fonction d’agrégation). 7.15.1 Fonctions appliquées à chaque tuple sélectionné Les fonctions scalaires sont classées selon la nature des arguments et de leur traitement particulier : ‐ Fonctions numériques : opérateurs mathématiques, arrondi, troncature, valeur absolue, ‐ Fonctions caractères (chaînes) : recherche d’une sous‐chaîne, longueur, transformation, ‐ Fonctions avec la date : transformation en format date et inversement ... Fonction de conversion : conversion numérique, de monnaie, de calendrier, ... Les opérateurs arithmétiques et les fonctions incorporés dans une expression du langage peuvent être utilisés dans la clause SELECT et/ou WHERE à la place dʹun attribut. Une fonction ne diffère d’un opérateur que par la forme externe de ses arguments. La fonction a zéro ou plusieurs arguments spécifiés dans sa liste. L’opérateur a au moins un argument et, s’il en a deux, ils sont spécifiés en notation arithmétique usuelle. La fonction à plusieurs arguments sʹécrit sous la forme bien connue avec la liste des
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
62
arguments entre parenthèses. Finalement, la librairie des fonctions peut être augmentée à volonté, tandis que les opérateurs sont figés par la grammaire du langage de données.
Fonctions et opérateurs numériques La gamme des fonctions peut varier dʹun système à lʹautre. Par exemple, certains SGBD offrent les services dʹun générateur de nombres aléatoires, la fonction RAND(), tandis que cette même fonction doit être implémentée par l’utilisateur au moyen de la fonction modulo.
Fonction numérique Description ABS (n) Valeur absolue du nombre n CEIL (n) Plafond d’une valeur réelle n MOD (m, n) Le modulo : le reste de m/ n GREATEST (v, w) La plus grande des deux valeurs v et w LEAST(v, w) La plus petite des deux valeurs v et w
Fonction numérique Description
POWER (m, n) Exponentiation : mn ROUND (m, n) Arrondi du nombre m avec n chiffres après le point. SQRT (m) Racine carrée de m TRUNC (m, n)** Troncature du nombre m à n chiffres décimaux; ** si m
est une date, alors pour n = 0, suppression de la partie du temps: hh:mm:ss
Figure 7.24 ** La fonction TRUNC() joue un rôle particulier dans le traitement des dates. En effet, un attribut déclaré de type date est codé à lʹinterne avec un format particulier qui inclut aussi le temps correspondant à la création du tuple dans la base. Ce temps est composé de lʹheure, la minute et la seconde : hh:mi:ss . A chaque insertion ou modification dʹune date, lʹheure peut être ajoutée, sinon elle est annexée automatiquement par Oracle selon une valeur par défaut généralement définie dans un fichier système d’initialisation comme étant 00:00:00. Pour supprimer le temps dans la date, il est aussi possible dʹutiliser le masque de la fonction TO_CHAR() ou de faire appel à la fonction TRUNC(). Dans ce dernier cas, la troncature sʹopère en enlevant la partie temps de la date. Exemples :
TRUNC (SYSDATE) supprime le temps de la date du jour. TO_CHAR(dateIn, 'DD-MM-YYYY')
—chaîne date retournée sans le temps SELECT To_Char(trunc(SYSDATE), ‘DD-MM-YYYY’) FROM DUAL;
Rép : 24‐11‐2000 ‐‐ le temps est absent de la réponse, car il a été supprimé par la fonction Trunc().
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
63
Voici quelques exemples qui illustrent lʹusage des opérateurs binaires et des fonctions scalaires : a) Calculer la racine carrée de la marge bénéficiaire sur les articles en inventaire dont le coût d’achat est plus grand que 10 $ .
SELECT article, SQRT(prixVente - cout) [as] marge FROM Inventaire WHERE cout > 10.00;
Remarque : La marge bénéficiaire est égale à la différence entre le prix de vente et le coût de l’article. Cette différence est calculée pour chaque tuple sélectionné par le prédicat de la clause WHERE. b) Afficher le nom des articles en inventaire dont le carré du bénéfice est compris entre 5$ et 15 $ .
SELECT article FROM Inventaire WHERE POWER((prixVente - cout), 2) between 5 and 15;
c) Afficher le montant total de la vente de 23 articles au prix de 45 $ l’unité. Pour obtenir la réponse, il faut effectuer une simple multiplication en utilisant la table DUAL :
SELECT 23 * 45.00 -- simple multiplication avec affichage FROM DUAL;--table fictive ayant le type table par SQL
Le calcul est possible dans un SELECT ou dans un WHERE à condition de référer à une table de la base de données. En effet, la grammaire exige la présence d’une table pour que l’analyse syntaxique accepte la clause SQL. La table DUAL est en quelque sorte virtuelle et permet de satisfaire aux exigences de la grammaire SQL. d)Lister les articles en inventaire dont le prix de vente est inférieur à 90 % du montant le plus élevé entre le prix de vente et le coût.
SELECT article FROM Inventaire WHERE prixVente < .90 * GREATEST(cout, prixVente);
Lʹappel de la fonction et lʹévaluation de lʹexpression sont faits pour chaque tuple de la table Inventaire. e) Lister les articles en inventaire et le montant le plsu élévé entre le prix de vente et le coût:
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
64
SELECT article, Greatest (prixVente,cout) FROM Inventaire ;
Fonctions SQL pour les chaînes Un seul opérateur de chaîne est défini dans le langage, soit celui de la concaténation (||). Cʹest un opérateur binaire dont les opérandes peuvent être une expression simple ou complexe, dont lʹévaluation fournit une chaîne, i.e. une constante de chaîne. Cependant, plusieurs fonctions de chaîne sont implémentées dans tous les langages de données. Les arguments des fonctions sont des constantes, des attributs ou des expressions de chaîne. La valeur retournée est typée. Cependant, une fonction dont un des arguments est NULL retourne une valeur qui est un null, sauf pour la fonction REPLACE(). Cette dernière fonction accepte le NULL et permet son remplacement par un autre caractère. Les fonctions ont un comportement général prédéterminé lorsque tous les arguments de son prototype sont fournis. Elles ont aussi un comportement particulier lorsque certains arguments sont absents ou réduits à leur plus simple expression.
Fonctions de chaîne Description LPAD(ch1,n[,ch2]) Remplissage par la gauche avec les car. ch2 jusquʹà ce que la
longueur totale de ch1 soit n. Si ch2 est absent, le caractère de remplissage est le blanc.
LTRIM(ch1[, setcar]) Suppression des car. par la gauche dans ch1 jusquʹau 1er car. qui nʹest pas dans setcar. Si cet argument est absent, tous les blancs sont supprimés par la gauche.
RPAD(ch1, n [, ch2]) Remplissage droit avec ch2 jusqu’à l’atteinte de la longueur n pour la chaîne ch1.
RTRIM(ch1[, setcar]) Suppression des car. par la droite dans ch1 jusquʹau 1er car. qui nʹest pas dans setcar.
CHR(n) Caractère ASCII équivalent à lʹentier n. INITCAP (ch1) Retourne ch1 avec une majuscule pour le premier caractère
de chaque mot de ch1. Deux mots sont séparés par un blanc. CONCAT (ch1, ch2) Concaténation de ch1 et ch2 dans l’ordre des arguments. LOWER (ch1) Retourne ch1 en minuscules. UPPER (ch1) Retourne ch1 en majuscules. SOUNDEX (ch1) Code pseudo phonétique de ch1 (signature phonétique de
Oracle) SUBSTR (ch1, m, n) Extraction dʹune sous‐chaîne à partir de la position m (incl.)
et de longueur n. TRANSLATE(ch1, s, c) Transcodage de ch1: chaque car. de ch1 retrouvé en position
i dans la chaîne s est remplacé par celui de même position dans la chaîne c. Le caractère de remplacement dans c ne peut pas être un null, car la fonction retournerait alors une valeur nulle. Si la position i est absente dans c, alors le caractère est supprimé dans ch1.
INSTR(ch1,ch2,n,[i]) Retourne la position de la i ème occurrence de ch2 dans la chaîne ch1 et cela à partir de la position n. Si i est absent, sa valeur par défaut est 1.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
65
LENGTH (ch1) Longueur de la chaîne ch1. Le caractère NULL n’occupant aucun espace pour la fonction LENGTH.
REPLACE (ch1, si, par) Retourne ch1 dans laquelle toute occurrence de si est remplacée par celle de par (N.B. par peut être un null).
TO_NUMBER(ch1[,fmt[,nls]]) Transforme la chaîne ch1 représentant un nombre dont le format peut être donné par fmt.
Figure 7.25
Légende : ch1, ch2, par : pour chaîne de caractères; n pour un entier; setvar = jeu de caractères (constante de chaîne) pos : position dans la chaîne fmt = format de la chaîne nls : paramètre de langue nationale Voici quelques exemples avec BD3 pour illustrer lʹusage des fonctions de chaîne, notamment lʹemboîtement des fonctions. a) Afficher le nom et le numéro des employés dont le nom dépasse 40 caractères
SELECT noEmpl, nom FROM Empl WHERE LENGTH(nom) > 40;
b) Extraire une sous‐chaîne du nom des employés dont le numéro est supérieur à 400 à partir de la position où se trouve la première occurrence de la lettre ʹiʹ et cela jusquʹà la position où se trouve la première occurrence du premier blanc.
SELECT SUBSTR(nom, INSTR (nom, 'i'), INSTR (nom, ' ' )) FROM Empl WHERE noEmpl > 400;
c) Affichez le nom des ouvriers dont les voyelles sont supprimées sauf une qui est remplacée par le caractère souligné ʹ_ʹ.
SELECT TRANSLATE(nom, 'aeiouy', '_') FROM Empl;
d) Afficher en minuscules le nom des employés dont la première lettre est un T et compléter par la gauche avec des tirets de manière à afficher au total une chaîne de 30 caractères. La fonction RTRIM() permet de supprimer par la droite les caractères jusquʹau premier absent de la liste fournie comme deuxième argument.
SELECT LPAD(nom,30,' ')), FROM Empl WHERE SUBSTR (nom, 1,1 ) = 'T';
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
66
e) Afficher le numéro et le nom des employés dont le nom a une prononciation similaire à Durand et dont la longueur est inférieure à 25 caractères. SELECT noEmpl, nom FROM Empl WHERE SOUNDEX(nom) = SOUNDEX('durand') and LENGTH (nom) < = 25;
À propos du code Soundex Lʹalgorithme graphico‐phonétique Soundex a été proposé il y a quelques décennies par Margaret K. et Robert C. Odell (1918) 9 dans le but de faciliter la recherche des auteurs dans les catalogues de bibliothèques lorsque lʹorthographe du nom de famille était imprécise. Il consiste à générer une signature Soundex pour un nom et ses graphies similaires. Lʹobjectif était de pouvoir rechercher un nom dont l’orthographe imprécise fournissait une phonétique similaire à la graphie correcte du nom. La prononciation de Smith et Smithes ou de Durant et Durand est similaire, mais la graphie est différente. Son efficacité est variable et dépend de la nature et de lʹemplacement des modifications ou des omissions qui peuvent être au début ou à la fin du nom. Plus le nom est long, plus la signature regroupe correctement les noms erronés dont les différences se situent à la fin de la chaîne. Il est efficace pour la langue anglaise, notamment les versions avancées du Soundex, et un peu moins pour le français poour lequel des algorithmes modifiés ont été proposés récemment. Il peut être utilisé avec certaines langues non romanisées à la condition de redéfinir les familles phonétiques. Plusieurs versions améliorées et adaptées ont été proposées par Russell10, et plus récemment par Baird de Georgia Tech11 et par Philips12. Le principe général repose sur le fait que les consonnes susceptibles dʹengendrer des confusions sont celles qui appartiennent à la même famille phonétique. En anglais, il y a 6 familles phonétiques : bilabiale, labiodentale, dentale, alvéolaire, nasale et laryngale. Il suffirait en principe donc de remplacer les consonnes dʹune même famille par un caractère unique pour résoudre plusieurs cas dʹambiguïté. Finalement, comme les erreurs dans la graphie sont plus probables avec un nom long qu’avec un court et que les erreurs se situent très souvent à la fin du nom, le code Soundex ne retient que les premiers caractères du code généré.
Algorithme Soundex (version Odell) 1‐ Le nom initial est placé dans la variable Code_S; 2‐ Supprimer tous les caractères non alphabétiques de Code_S. La chaîne restante est affectée à la variableCode_C initial; 3‐ Transformer les lettres restantes dans Code_C en lettres majuscules; 4‐ Le premier caractère du Code_C est gardé tel quel; 5‐ Les autres caractères du Code_C sont traités et transformés ainsi : 5.1 Les voyelles et les caractères H et W sont supprimés dans Code_C; 5.2 Les caractères B, F, P, et V du Code_C sont remplacés par le chiffre‐caractère 1; 5.3 Les caractères C, G, J, K, Q, S, X, Z du Code _C sont remplacés par le chiffre‐caractère 2; 5.4 Les caractères D et T du Code _C sont remplacés par le chiffre‐caractère 3;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
67
5.5 Le caractère L du Code _C est remplacé par le chiffre‐caractère 4; 5.6 Les caractères M et N du Code _C sont remplacés par le chiffre‐caractère 5; 5.7 Le caractère R du Code _C est remplacé par le chiffre‐caractère 6; 6‐ Toute paire de caractères Soundex identiques dans Code_C est remplacée par un seul caractère; 7‐ Les caractères représentant les voyelles encore présentes dans Code_C sont supprimées; 8‐ Le premier caractère et les trois chiffres suivants sont conservés comme code Soundex; si Code_C nʹa pas suffisamment de chiffres, la chaîne est complétée avec le caractère‐chiffre 0.
Exemple : Langue Code_S Code_C anglais Rogerson R262 Rogerson R262 Rogers R262 <‐‐ Roger R260 <‐‐‐ Rodger R326 <‐‐ français Durant D653 Durand D653 <‐‐‐ Duran D650 <‐‐‐
Figure 7.25a Le calcul de la signature est en quelque sorte un Hashing avec cette différence que le nombre de collisions est sensiblement important, pour peu que les racines des noms de la source soient similaires. Lʹefficacité de la signature du Soundex peut être mesurée par le nombre de signatures similaires générées par lʹalgorithme :
E = 1 - (nb_signatures_identiques / nb_de_noms_différents). Pour un ensemble sans conflit de signatures, E = 1 ; avec une seule signature pour un ensemble de noms, E = 0.
7.15.3 Fonctions SQL pour le traitement des dates Les dates13 sont des données importantes dans une base de données et leur traitement est plus complexe qu’on l’imagine à priori. Lʹhistoire du calendrier remonte au grec Meton (400 B.C.) qui découvrit le cycle lunaire de 19 ans et son effet sur la façon de reconnaître correctement les saisons. À cette époque la connaissance du cycle des saisons était une question importante pour l’agriculture et de survie! Il sʹen est suivi à travers les siècles une série de repères pour du temps au moyen de la notion de date intégrée dans divers calendriers, chacun dénommé par le nom de son auteur. Ainsi pour les musulmans, c’est la fuite de Mahomet de la Mecque vers Médine en 622 qui marque le point de départ du calendrier. Pour le monde hébreu le calendrier débute avec la création du monde fixée à 3760 ans avant Jésus‐Christ. Pour les chrétiens, c’est la
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
68
naissance de Jésus fixée qui détermine l’année 0. C’est donc dire que l’an 2000 en Occident correspond à l’an 5760 pour les hébreux et à l’année 1377 pour les musulmans! Sur le plan planétaire, une date est une convention dʹécriture dʹune réalité temporelle fortement influencée par le système des planètes. Plus concrètement, un système informatique moderne doit tenir compte de plusieurs éléments pour représenter une date de calendrier, notamment du caractère conventionnel de son origine qui varie selon le système politico‐religio‐culturel de référence. Dans un SGBD, chaque date est stockée dans un format interne complexe particulier à chaque système SGBD. Elle comprend, pour un calendrier Julien qui utilise la notion d’écart avec un point temporel fixe, outre le jour, le mois et lʹannée, le siècle, l’heure, la minute et la seconde.
Figure 7.25b Les dates en format interne obéissent à une relation dʹordre; elles peuvent être l’objet d’une comparaison ou dʹun calcul par les opérateurs de date, comme une addition ou une soustraction. Le format des dates doit être valide au‐delà du siècle courant afin dʹéviter les problèmes du passage du millénaire débusqués dans les anciennes applications (legacy applications). Passage bi‐directionnel dʹune date en format interne au format externe piloté par un gabarit (masque). Les fonctions To_Date() et To_Char() sont importantes pour la manipulation dʹune date. Ces fonctions comportent plusieurs arguments qui seront expliqués un peu plus loin. Plusieurs autres fonctions utilisent un argument du type date pour faire des opérations sur celles‐ci. Fonctions pour les dates Description SYSDATE Date du système (du jour); fonction sans argument. LAST_DAY (d) Date du dernier jour du mois comprenant la date d MONTHS_BETWEEN(d1, d2) Nombre de mois entre deux dates d1 et d2 ADD_MONTHS(d, n) Retourne la date en y ajoutant n mois à la date d. Le n peut
être positif ou négatif. NEXT_DAY(d, j) Retourne la date du prochain jour j qui suit la date spécifiée.
Le dimanche est codé avec J=1 ou ‘SUNDAY’ Figure 7.26
xx.ee.sss.uuu.urruu.ttt
21 :24‐Jan‐2000 16 :45 :32
To_Date ( )
Date dans un des formats externes disponiblesTo_Char( )
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
69
La valeur retournée peut être un entier ou une date. Par exemple, si on ajoute un entier à une date, la réponse est une date. Par contre, si on soustrait deux dates, la valeur obtenue est un entier. Exemples : a) Afficher la date du jour
SELECT SYSDATE FROM DUAL ; /*DUAL est une table virtuelle nécessaire dans une clause pour satisfaire la grammaire de SLQ */
Lors de lʹaffichage piloté par SQL*Plus, le temps est enlevé et seulement les éléments de la date, formée du jour, du mois et de lʹannée sont affichés par cette clause. Cet affichage peut être aussi bien contrôlé par un format explicite au moyen de la fonction TO_CHAR() qui comporte un masque.
SELECT TO_CHAR (debut, 'DD-MON-YYYY') FROM Empl WHERE quart = 'soir' ;
Pour afficher le mois en français lorsqu’il a été enregistré en anglais, il faut utiliser le masque de la fonction To_Char().
SELECT TO_CHAR(dateEmbauche,'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = french')
FROM Employe; La date affichée aura le format suivant: 23‐DEC‐1998 et le mois sera exprimé dans la langue spécifiée par le paramètre NLS. b) Afficher le dernier jour du mois :
SELECT LAST_DAY(SYSDATE) "dernier jour du mois" <-libellé FROM DUAL;
La chaîne devient lʹintitulé de la colonne de la table réponse. Les guillemets doubles sont utilisés parce que le libellé contient des espaces. c) Afficher l’arrondi de la date du jour :
SELECT ROUND(SYSDATE) FROM DUAL;
d) Afficher la date du prochain lundi après le 31 décembre 1999.
SELECT NEXT_DAY('31-dec-1999', 'MONDAY') FROM DUAL;
Date par défaut
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
70
Le système SGBD a normalement un format de date par défaut défini dans un fichier de paramètres pris en compte au lancement de lʹinstance. Avec Oracle, ce format peut être DD‐MON‐YYYY; dans ce cas le gabarit ne spécifie pas le temps qui est par défaut 00:00:00, soit minuit. Ce paramètre peut être spécifié ou redéfini dans le fichier système INIT.ORA. Il est aussi possible dʹinclure le temps réel en spécifiant le paramètre NLS_DATE_FORMAT comme étant ʹDD‐MM‐YYYY HH:MI:SSʹ. Ainsi, lʹinscription dʹune date par la fonction SYSDATE inclut aussi le temps courant. Ce paramètre peut être temporairement changé pour la durée dʹune session avec la commande ALTER SESSION. Il est possible de connaître la valeur courante de ce paramètre défini dans lʹinstance en consultant la table GV$NLS_PARAMETERS. ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS' ; INSERT INTO Ouvrier ('200056345','Vézina,'Claude','M', '33.8',SYSDATE,'u12', 'ps75'); -- temps est inséré avec SYSDATE --25-DEC-1999 23:54:49 (à l'interne) ; SELECT TO_CHAR(dateP,'DD/MM/YYYY HH:MI:SS') FROM Tplan_Mois; réponse --> 22/02/1998 23:11:56 --le temps est affiché Avec la suppression du temps :
ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY' ; À partir de cet instant, les insertions de tuple avec une date peuvent se faire avec le nouveau format de date. À lʹinterne, le temps sera celui défini par défaut, soit par exemple 00:00:00 : INSERT INTO TABLE1 Tplan_Mois
('machine2376',To_Date('24-01-1998','DD-MM-YYYY')); Il est cependant toujours possible dʹinsérer le temps courant en utilisant le gabarit approprié. INSERT INTO TABLE1 Tplan_Mois ('machine2376',To_Date('24-01-1998','DD-MM-YYYY HH:MI:SS')); Il est aussi possible de changer temporairement ce format par défaut en utilisant le paramètre NLS_DATE_FORMAT.
ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS';
SELECT TO_CHAR(datep, 'dd-mm-YYYY hh:mi:ss') FROM Tplan_Mois;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
71
réponse ‐‐> 12‐DEC‐1998 23:25:56
Fonctions SQL de conversion des types Quelques fonctions jouent un rôle particulier dans la conversion du type interne vers un format externe compatible avec lʹenvironnement. Le paramètre NLSP permet de spécifier le mois dans une langue naturelle particulière. Par exemple, pour obtenir une date avec le mois en français à partir de la date stockée en format américain, il suffit dʹutiliser le troisième paramètre de la fonction TO_CHAR() :
Fonctions conversions Transformation TO_CHAR(d, masque[, nlsp])) Retourne une chaîne pour la date d conformément au masque
fourni. TO_DATE(ch1,masque[,nlsp])) Conversion de ch1 au format interne date en interprétant la
chaîne selon le masque fourni. TO_NUMBER(ch1,masque[,nlsp]) Fournit la valeur numérique de la chaîne ch1 conformément
au masque du nombre ROWIDTOCHAR (ROWID) Version chaîne du ROWID CHARTOROWID (ch1) Conversion de la chaîne en ROWID de tuple
Figure 7.27 SELECT TO_CHAR(date_nais,'YYYY-MON-DD',
'NLS_LANGUAGE_FORMAT = french') FROM Employe WHERE TO_CHAR(date_nais, ‘YYYY’) = ‘1984’;
La date n’est pas stockée comme une constante, mais avec un format interne particulier à Oracle qui inclut les éléments suivants : siècle, année, mois, jour, heure, minute et seconde. La date représentée avec un tel format ne peut donc pas être utilisée directement dans un prédicat comme peut l’être une constante de chaîne telle que ‘JAN‐25‐94’. Elle doit être transformée pour obtenir une chaîne qui peut être comparée à une autre chaîne. La transformation inverse de la date en une date de format interne est effectuée par la fonction To_Date(). L’importance des dates justifie l’implémentation d’une arithmétique simplifiée avec les dates (avec les opérateurs arithmétiques + et ‐). Dans certains systèmes SGBD, la constante ajoutée ou retranchée peut être assortie d’une unité : DAYS ou MONTHS. Chez Oracle, l’unité est exclusivement et implicitement le jour (DAY). Les exemples suivants illustrent cette arithmétique : a)SYSDATE + 1, pour obtenir la date du lendemain (DAY est l’unité par défaut de Oracle); b)SYSDATE ‐ 7 DAYS, donne la date du même jour de la semaine précédente (Oracle); c) SYSDATE ‐ EMBAUCHE, l’évaluation donne le nombre de jours de travail. d)SYSDATE+10 MONTHS donne la date du jour, 10 mois plus tard [pas toujours implémentée];
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
72
La date peut être exprimée de la façon traditionnelle avec lʹannée, le mois et le jour, ou selon le calendrier Julien (depuis le 1er janvier 4712 BC) ou, autrement, en mois ou en jours depuis le début de l’année. Les fonctions de conversion utilisent des masques (synonymes : gabarit, modèle, patterns) pour spécifier le résultat. Un masque de date est une composition avec les éléments syntaxiques ci‐dessous.
Éléments Exemples de masque Exemple de sortie YYYY ʹYYYY‐MMʹ 1994‐12 MM ʹMMʹ 12 (mois de décembre) DD ʹYYYY/MM/DDʹ 1994/12/22 MON ʹMON DD YYYYʹ DEC 22 1994 MONTH ʹMONTH**DDʹ FEVRIER**24
Figure 7.28 Où les caractères ‐ , . / et \ sont des caractères littéraux du masque reproduits intégralement dans le résultat de la fonction. Exemple : TO_DATE(dateIn, ʹMM//YYYYʹ) ‐‐ La fonction retourne : ʹ11//1998ʹ
Transformation dʹune date externe en format interne Une date en format externe est stockée dans la base de données avec un format interne très particulier. Il faut donc convertir le format lors de lʹécriture et de la lecture dʹun attribut du type date. La fonction To_Date() transforme une date de format externe en une date codée en format interne :
To_Date(chaîne, masque [,nlsp]) Le masque (ou le gabarit) de la fonction précise comment doit être interprétée la chaîne de caractères lors de la transformation par la fonction To_Date(). Les paramètres : chaîne : est une chaîne de caractères représentant une date; masque : ou gabarit sous forme dʹune constante de chaîne incluant des littéraux dʹaffichage nlsp : constante pour spécifier la langue nationale : 'NLS_DATE_LANGUAGE =
'french' . Exemple : INSERT INTO Ouvrier ('200056345', 'Vézina,'Claude','M','33.8',
To_Date(‘24-Jan-2000’,’DD-MON-YYYY’, ’NLS_DATE_LANGUAGE = french’),'u12', 'ps75');
Suppression du temps dans la date Une date insérée dans la base de données Oracle par la fonction SYSDATE est suffixée automatiquement par le temps composé de lʹheure, la minute et la seconde (le siècle est
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
73
aussi ajouté et rendu accessible). Cet ajout est rendu possible si le format par défaut permet lʹadjonction du temps réel. La fonction TRUNC() joue un rôle particulier dans le traitement des dates en supprimant le temps annexé à toute date créée et stockée dans la base. Si le temps est explicitement exclu par le masque, le système lʹenregistre comme étant 00:00:00 (minuit). Pour supprimer le temps dans la date, il est possible dʹutiliser le masque de la fonction TO_CHAR() ou de faire appel à la fonction TRUNC(). Dans ce dernier cas, la troncature sʹopère en enlevant la partie temps de la date. Exemples: TRUNC(SYSDATE) /*supprime le temps dans la date du jour retournée par la fonction SYSDATE*/ TRUNC(dateP) /*retourne seulement la partie date de lʹattribut datep. Le temps est supprimé de la date*/
Transformation dʹune date du format interne vers une chaîne de caractères Cette transformation consiste à passer du format interne de la date en un format externe utilisé par une application.
TO_CHAR (attribut, masque [, nlsp]) où
attribut : attribut du type DATE dans une relation masque : format de date pour la chaîne d sortie nlsp : spécification de la langue nationale (american, french, english,....) pour lʹexpression du mois.
La fonction TO_CHAR peut supprimer le temps inclus dans le format dʹun attribut de type date. Exemples : a) Afficher le numéro des employés embauchés après le 23 décembre 90 :
SELECT noEmpl FROM Assignations WHERE TO_CHAR(debut,'DD/MON/YYYY) >'23/DEC/1990';--supp.du temps
b) Afficher le nom, le numéro de poste et le rowid ( ou rid) de l’employé qui a débuté son travail le 24 septembre 1994 :
SELECT nom, noPoste, ROWIDTOCHAR(ROWID) FROM Empl E, Assignations A WHERE E.noPoste= A.noPoste and
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
74
To_Date('24/sept/1994','DD/MM/YYYY',NLS_DATE_LANGUAGE = french') = Trunc(debut);
Cette adresse de tuple (rid) peut varier au cours du temps, car elle ne correspond pas à une identité d’objet immuable. Le ROWID sera plus utile avec le SQL intégré parce qu’une variable hôte peut l’utiliser dans un autre SELECT et accéder directement aux tuples. Le temps du format interne de lʹattribut embauche est supprimé par la fonction Trunc (debut, ʹDDʹ). La fonction de troncature modifie la date pour conserver que le jour. c) Afficher le mois en anglais du début (typé Date) de contrat et le numéro des employés embauchés après le 23 février 1990 : SELECT TO_CHAR(debut, 'MONTH', 'NLS_DATE_LANGUAGE= american'), noEmpl FROM ASSIGNATIONS WHERE debut>TO_DATE('23-FEV-1990','DD-MON-YYYY,'NLS_DATE_LANGUAGE = french'); La comparaison est faite selon lʹordre lexicographique des chaînes. La fonction MIN() retournera donc la plus petite date dans lʹensemble des dates, tandis que la fonction MAX(), la plus grande.
SELECT MAX(debut) FROM Employe WHERE age between 18 and 45;
Cette clause fournit la date de début la plus récente pour les employés de 18 à 45 ans.
7.16 Fonction définie par l’utilisateur Il est possible de définir une fonction dʹutilisateur qui est stockée dans le dictionnaire de la base de données sous forme dʹun objet (persistant et sans méthode) et que toute application peut utiliser dans une expression SQL, et cela partout où il peut y avoir un attribut. Cet accès est possible sous réserve que lʹutilisateur ait reçu les droits appropriés par le propriétaire de la fonction. La définition de nouvelles fonctions enrichit la librairie des fonctions SQL et permet d’adapter le système aux besoins de traitement.
CREATE FUNCTION Profit (prixVente IN real, cout IN real) return real is diff real; BEGIN diff := prixVente - cout; return (diff); END Profit;
. ‐‐ Notez bien le point ‘’.’’ qui arrête lʹéditeur PL/SQL du module Oracle SQL*Plus]
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
75
Il faut noter que chaque paramètre est fourni avec son type qui ne comprend pas les éléments de longueur pour la chaîne et ceux de précision et d’échelle pour les nombres. Cette fonction de lʹutilisateur peut être appelée dans une requête SQL comme toute autre fonction interne de SQL .
SELECT article, Profit(prixVente, cout) FROM Article_v WHERE prixVente > cout;
Cette fonction est stockée dans le dictionnaire comme un objet. Ces objets sont identifiés en consultant la table USER_OBJECTS. De plus, le texte de la fonction est aussi disponible dans la table USER_SOURCE.
SELECT OBJECT_NAME, OBJECT_TYPE FROM USER_OBJECTS; SELECT TEXT FROM USER_SOURCE WHERE NAME = 'Profit';
Le corps de la fonction est un bloc de PL/SQL pouvant incorporer, selon les besoins, une ou plusieurs clauses SQL ou des appels de fonction. Voici une autre fonction dʹutilisateur Longc pour compter le nombre de consonnes dans une valeur dʹattribut dont le type correspond à celui dʹun attribut du dictionnaire. La fonction TRANSLATE() ne peut pas être utilisée avec un NULL comme 3e argument, car la valeur retournée est alors aussi null. CREATE FUNCTION LONGC(nom IN varchar2) return number
IS nom_fam Ouvrier.nom%type; /* type de nom_fam équivalent à celui défini dans le dictionnaire pour nom */ lon number :=0; BEGIN nom_fam := REPLACE(TRANSLATE(nom,'aeiouy','2'),'2', null); lon := LENGTH(nom_fam); return(lon); END;
La fonction est stockée dans le dictionnaire de la base de données sous la forme dʹun objet appelé LONGC. Cette fonction est appelée dans une clause SQL standard :
SELECT nom, LONGC(nom) FROM Ouvrier;
La réponse est le nombre de consonnes présentes dans le nom de chaque ouvrier inscrit dans la base.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
76
Récursivité des fonctions Une fonction dʹutilisateur ou un bloc PL/SQL peut être récursif. Par exemple, la fonction factorielle est définie en exploitant la récursivité de la fonction.
CREATE FUNCTION Fact (n Integer) return Integer is begin if n = 1 THEN return 1; else return (n* fact(n-1)); END IF; END FACT; /
La fonction Fact est appelée et exécutée pour calculer par exemple le factoriel des nombres inférieurs à 20.
SELECT Fact(nombre) FROM TableNombres WHERE nombre < 20;
La définition de nouvelles fonctions permet au développeur de réutiliser le code déjà testé et dʹaccélérer le développement des applications.
7.17 Fonctions SQL diverses
Fonction NVL() La fonction NVL() permet de simuler l’alternative et de traiter la valeur nulle.
NVL(expr1,expr2) Fonction NVL :
Si la valeur de expr1 est non nulle alors retourner la valeur de expr1; sinon retourner la valeur de expr2;
Le type de la valeur retournée est celui de expr1. Exemple : Afficher le numéro des employés et leur quart de travail. S’il n’y a pas de quart de travail spécifié (null), afficher le message suivant : ‘quart_libre’.
SELECT noEmpl, NVL(quart,'quart_libre') FROM Assignations;
Fonction DECODE() La fonction DECODE() du système Oracle implémente une alternative multiple. Elle correspond à la clause CASE de la norme SQL‐92. DECODE (source,expr1,expr1.1,expr2,expr2.1,expr3,expr3.1,val_defaut)
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
77
Fonction algorithmique du Decode() : Si source = expr1 THEN return(expr1.1) si source = expr2 THEN return(expr2.1) si source = expr3 THEN return (expr3.1) else return(val_defaut); ‐‐ si aucun match nʹest trouvé La fonction DECODE() est de type tuple et compare la valeur de lʹexpression source avec le résultat de l’évaluation de l’expression expr1 et, si la comparaison est vérifiée la fonction retourne expr1.1. Dans le cas contraire la comparaison se poursuit avec l’expression espr2 et ainsi de suite. Si aucun test n’est positif, la valeur ou l’expression par défaut est évaluée et le résultat retourné par DECODE. La fonction DECODE peut être utilisée avec un nombre variable dʹarguments.
Employe(matricule*, nom, age, sexe, salaire, noDep) SELECT matricule,CONCAT(decode(sexe,’F’,’Mme’,’M ’,’Mr ’,’?‘),nom) FROM Employe WHERE (salaire /12) > 3450.00);
Fonction SIGN() La fonction Sign(n) évalue le nombre n. Si n est positif, elle retourne +1; si n est 0, elle renvoie 0; si la valeur est négative, la fonction retourne la valeur ‐1 : Voici une requête plus complexe qui utilise la fonction SING(n) pour augmenter de moitié le salaire des employés de moins de 45 ans dont le salaire actuel est supérieur à 15 000 $ et pour doubler le salaire de ceux qui gagnent moins de 15 000 $. SELECT noDep, Sum(Decode(Sign(salaire-15000),+1, salaire*1.5,-1, salaire *2, salaire)) FROM Employe WHERE age < 45;
COALESCE() Cette fonction a un nombre variable de paramètres typés; elle retourne la valeur du premier paramètre parmi ceux de la liste dont la valeur est non nulle. Par contre, elle retourne la valeur NULL si tous les paramètres sont nuls après leur évaluation. Soit A, B, C dont les valeurs sont respectivement 5, 3, 4. L’exécution de la fonction COALESCE (A, B, C) retourne 5 avec un type correspondant à celui du paramètre typé ou à celui obtenu après conversion. Ainsi si A est typé Float(2, 2), B est typé Float(5, 2) et C est typé entier, le résultat de la fonction sera 5, et la valeur typée Float(5, 2). SELECT article, COALESCE (prixVente, cout)
FROM Inventaire;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
78
Par exemple, la figure ci‐dessous donne les règles de transformation pour les types implémentés dans DB2‐V2. La longueur, la précision et lʹéchelle sont fournies ou imposées par le type cible résultant de la transformation. Pour bien comprendre ce résultat, il faut se rappeler que lʹinterprète SQL traite une fonction par une recherche de son nom dans le schéma spécifié. Par la suite, il vérifie si le type de chaque paramètre est identique ou, dans le cas contraire, il cherche un type commun à tous les paramètres en les transformant selon des règles propres à chaque SGBD, voir le tableau ci‐dessus).
smallint ‐‐> integer ‐‐> decimal ‐‐> double char varchar ‐‐> long varchar ‐‐> cblob graphic ‐‐> vargrahic ‐‐> long vargraphic ‐‐> dbclob blob ‐‐> blob cible idem date ‐‐> date cible idem time ‐‐> time cible idem timestamp ‐‐> timestamp cible idem
Exemple avec DB2‐V2 Soit le schéma de la relation UsineProd comprenant les attributs typés ci‐dessous : UsineProd(no_us char(2), production integer, cMateriau decimal(6,1), coutTot decimal (4,2))
UsineProd : noUs production cMateriau coutTot u2 25 234.2 456.78 u1 null 1764.8 null u3 null null 876.41 u5 ‐1 ‐1 ‐1
N.B. la valeur ‐1 pour lʹattribut production signifie que lʹon ignore sʹil y a eu une production à lʹusine, tandis que le NULL signifie que la production a eu lieu, mais elle est nulle, i.e. nʹa pas été gardée. SELECT noUsine, COALESCE(production, c_materiau, cout_tot) pmc FROM Usine_Prod WHERE no_us IN ( 'u1', 'u2', 'u3', 'u4', 'u5', 'u6'); La réponse est la suivante :
nomUs pmc type du résultat u2 25.0 ‐‐> le type est décimal 6.1 u1 1764.8 ‐‐> le type est décimal 6.1
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
79
u3 876.4 ‐‐> le type est décimal 6.1 u5 ‐1.0 ‐‐> ? pour la production
NULLIF() Cette fonction a deux paramètres, p1 et p2 de type compatible et retourne une valeur NULL si p1 = p2, sinon p1. La réponse contient deux nulls pour lʹattribut pmc, dont lʹune est justifiée par la présence dʹune instance ayant la valeur ‐1.
SELECT noUs, NULLIF(c_materiau,-1)AS pmc FROM UsineProd;
noUs pmc u2 234.2 u1 1764.8 u3 NULL u5 NULL
7.18 Fonctions pour les agrégats avec les tuples sélectionnés Ces fonctions de type ensembliste sont appliquées à un groupe de tuples et renvoient une valeur calculée en ignorant les valeurs nulles dans les tuples (sauf pour la fonction Count(*)). Ces fonctions dʹagrégation acceptent aussi les modificateurs ALL et DISTINCT. Les fonctions peuvent être utilisées dans le SELECT et dans le WHERE d’une requête.
Fonctions SQL dʹagrégation Ces fonctions sont appliquées à des groupements de tuples préalablement formés par le GROUP BY. En dʹautres mots, lorsquʹil y a formation de groupements, cela définit aussi la portée des fonctions dʹagrégation du SELECT .
Fonctions dʹagrégation Description AVG (attribut) Moyenne Count (*) | (DISTINCT attrib) Comptage (NULL inclus) MAX et MIN ([distinct] expr1) Val max ou min dans un ensemble SUM ([DISTINCT,] tauxH) Somme de valeurs STDDEV (DISTINCT|ALL], attrib) Ecart‐type de l’argument attrib VARIANCE([DISTINCT|ALL],attrib) Variance
Figure 7.29 La portée de toute fonction dʹagrégation est limitée à chaque groupe ou partition d’une relation de base ou d’une relation intermédiaire calculée au regard de la liste des attributs utilisée pour définir le groupement listé dans le GROUP BY. Lʹabsence du GROUP BY définit implicitement la portée comme étant celle de toute la table obtenue par le calcul de la requête. Exemples avec la base BD1
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
80
a) Lister pour les clients ʹSergeʹ et ʹTedʹ la moyenne des articles achetés (Ventes) peu importe la transaction.
SELECT Avg(qte) [as] moy_Serge_ted FROM Ventes WHERE nom = 'Serge' or nom = 'Ted';
Lʹabsence du GROUP BY fait en sorte que la moyenne est calculée avec tous les tuples de la table Ventes. L’attribut nom ne peut pas apparaître dans le SELECT, puisque la fonction AVG() s’applique sur l’ensemble des tuples sélectionnés, la réponse ne peut avoir qu’un seul tuple et excluant la possibilité d’avoir une moyenne pour les achats de ʹsergeʹ et une autre pour ʹtedʹ. Cette ambiguïté est détectée dès lʹétape dʹinterprétation de la clause SQL. b) Lister les quantités maximale et minimale des articles vendus jusqu’à maintenant :
SELECT Max(qte), Min(qte) FROM Ventes;
La sélection est formée de tous les tuples en raison de l’absence du prédicat. Les fonctions max() et min() sont alors appliquées sur cet ensemble de tuples, en ne considérant que l’attribut qte pour afficher la plus grande et la plus petite valeur. Les fonctions d’agrégation peuvent être aussi imbriquées à plusieurs niveaux. c) Compter les articles en inventaire dont la vente entraîne une perte.
SELECT Count(article) FROM Inventaire WHERE prixVente < cout;
d) Compter le nombre de ventes faites à des personnes de noms distincts :
SELECT Count(DISTINCT nom) FROM Ventes;
Remarque : S’il existe un index sur lʹattribut nom, le calcul de la requête en tirera profit, puisqu’il s’agit essentiellement d’un comptage possible par la consultation de lʹindex. Dans le cas contraire, cette requête est exécutée par une projection de l’extension de Ventes, la suppression des doublets par un tri et finalement, le comptage des tuples dans la relation intermédiaire du calcul. Lister le nom des clients ayant achetés des articles dont la quantité correspond au plus grand nombre d’un article quelconque déjà vendu dans une même transaction.
SELECT nom FROM Ventes WHERE qte = (SELECT Max(qte) FROM Ventes);
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
81
La sous‐requête est évaluée au début de l’exécution et la valeur obtenue est substituée à la sous‐requête placée entre parenthèses.
7.19 Formation de groupements avec les tuples sélectionnés Il est possible de regrouper les tuples sélectionnés dans le calcul dʹune requête en fonction d’un ou plusieurs attributs. Le groupement inconditionnel implique tous les tuples, tandis que le groupement conditionnel impose un critère supplémentaire pour que les tuples regroupés soient retenus dans la réponse. Cette opération de filtrage des groupes est réalisée dans lʹavant dernière étape. i.e. avant celle qui précède le tri.
7.19 .1 Regroupement inconditionnel des tuples Tous les groupes formés avec l’extension de la réponse et sur la base dʹune liste dʹattributs sont retenus et affichés sans faire l’objet d’aucun autre filtrage. Chaque groupe correspond obligatoirement à un tuple de la réponse. Exemples : a) Lister le nom des clients et la quantité totale de produits achetés qui sont encore en inventaire.
SELECT nom, sum(qte) FROM Ventes WHERE article IN
(SELECT article FROM Inventaire WHERE cout is NOT NULL ) --articles en inventaire
GROUP BY nom; Remarque : L’argument du GROUP BY doit inclure obligatoirement tous les attributs présents dans le SELECT, sauf ceux traités par une fonction dans la clause de 1er niveau. Cette fonction de groupement est exécutée après la sélection et la projection. Le schéma de la réponse est formé des attributs de la fonction de groupement plus ceux des fonctions d’agrégation. Les articles vendus encore en inventaire sont regroupés par nom d’acheteur et la somme est calculée pour chaque groupe. b) Lister les transactions de Ventes (tous les attributs) concernant les achats d’articles effectués avec une quantité égale à la quantité maximum déjà vendue pour cet article.
SELECT article, qte, nom FROM Ventes WHERE (article, qte) IN -- notez l'usage des parenthèses
(SELECT article, max(qte) FROM Ventes GROUP BY article);
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
82
La sous‐requête est calculée en premier; l’extension de Ventes fait l’objet d’une projection et le résultat est trié pour former des groupes virtuels au sein de la seule relation intermédiaire constituée des tuples formés avec les articles et les quantités maximales pour chacun. Le prédicat d’inclusion du premier WHERE utilise un tuple comme argument.
nom* article* qte* article* max(qte) Serge a1 3 a5 3 Ted a2 5 a2 5 André a1 2 a7 5 Serge a2 4 a3 6 Jocelyn a1 2 a1 7 Serge a3 4 Valérie a7 3 Lucie a7 2 réponse article qte nom Serge a5 3 a1 7 Serge Serge a7 5 a2 5 Ted Christine a3 6 a3 6 Christine
Serge a1 7 a5 3 Serge Figure 7.30
La réponse sous‐tend le choix de tuples en fonction d’une condition qui est évaluée au regard du contenu d’une autre table. C’est en quelques sorte une expression de jointure.
Formulation du GROUP BY avec une fonction Le GROUP BY peut contenir un ou plusieurs attributs ou des fonctions. Lorsquʹil contient une fonction, celle‐ci doit être en tout point identique à la même fonction qui apparaît dans le SELECT.
SELECT sum(QTE), TO_CHAR(dateDebut,'YYYY-month')as mois_an FROM Production WHERE TO_CHAR(dateDebut,'YYYY') = '1998' GROUP BY TO_CHAR(dateDebut,'YYYY-month') ORDER BY To_Date(TO_CHAR(dateDebut,'YYYY-month'),'YY-MM');
Une petite différence syntaxique rend lʹexpression du groupement inacceptable pour lʹanalyseur syntaxique !
SELECT sum(QTE), TO_CHAR(dateDebut,'YYYY-MONTH')as mois_an FROM Production WHERE TO_CHAR(dateDebut,'YYYY') = '1998' GROUP BY TO_CHAR(dateDebut,'YYYY-month') ORDER BY To_Date(TO_CHAR(dateDebut,'YYYY-month'),'YY-MM');
La différence syntaxique apportée par le gabarit ʹmonthʹ écrit en minuscules invalide lʹexpression du GROUP BY ! Notez aussi que, si le groupement doit utiliser une vue
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
83
relationnelle, il faut que chaque colonne de celle‐ci transformée par une fonction soit définie avec un alias.
CREATE OR REPLACE view V2 [as] SELECT Trunc(dateP), sum(QTE) FROM Production;
Groupement avec une vue relationnelle Le groupement utilisant une vue doit définir un libellé simple ou un alias pour chaque colonne de la vue. Le renommage de la colonne dʹune vue transformée par une fonction est obligatoire. Create or Replace view V2 [as]
SELECT Trunc(DATEP) [as] dateProduction, Sum(QTE) [as] total_production FROM Production Group By Trunc(DATEP);
Les alias peuvent être lʹobjet dʹune référence dans une clause SQL :
SELECT dateProduction, total_production FROM V2 WHERE Trunc(dateP), = Trunc(SYSDATE);
7.19.2 Regroupement avec filtrage conditionnel des groupes Les tuples sélectionnés dans une première étape forment une table temporaire au sein de laquelle les tuples sont partitionnés par le GROUP BY. Une partition n’est conservée dans la réponse que si la condition de filtrage exprimée par le Having est vérifiée. Exemples : a)Lister le nom des acheteurs qui ont fait plus de deux achats (chaque groupe ou partition devra comprendre plus de deux tuples) dʹun article en inventaire.
SELECT NOM FROM VENTES WHERE ARTICLE in (SELECT ARTICLE FROM INVENTAIRE
WHERE COUT is NOT NULL) GROUP BY NOM Having COUNT(*) >2; --*signifie un comptage de tuples
La clause HAVING Count() est appliquée à chaque partition (interne) formée par le GROUP BY. Cette clause agit comme un mécanisme de filtrage des groupes. Le résultat de la sous‐requête est toujours une seule relation dont l’extension est ordonnée par l’argument simple ou complexe du GROUP BY . Ensuite, la projection sur nom est effectuée sur nom. b)Lister en ordre alphabétique de nom, le nombre des achats effectués par chaque personne.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
84
SELECT NOM, Count(nom) [as] Total FROM VENTES WHERE article in (SELECT article FROM INVENTAIRE
WHERE COUT is NOT NULL) GROUP BY nom Having Count(*) > 2; ORDER BY Total;
Le tri final des tuples de la réponse est effectué après la dernière projection de sorte quʹà ce moment lʹalias Total est défini et peut être utilisé par le tri. c) Lister le nom des acheteurs d’articles présentement en inventaire et dont le coût est plus élevé que 10 $. Regrouper les transactions par nom en ne gardant que les groupes dont la quantité totale est supérieure à 10 et afficher le total des achats effectués.
SELECT NOM, Sum(qte) FROM VENTES WHERE article in (SELECT article FROM Inventaire WHERE COUT is NOT NULL and cout > 10) GROUP BY nom HAVING Sum(qte) > 10;
Une fonction d’agrégation ou de groupement est utilisée partout où il peut y avoir une variable ou un attribut. La projection est calculée comme dans la dernière opération, i.e. après le filtrage des groupes. Exemple : Identification des doublets dans une relation Pour définir une clé primaire à la table Employe, il faut au préalable vérifier lʹabsence de tuples identiques dans lʹextension courante de Employe. a‐ Modification du schéma de la table Employe. ALTER TABLE EMPLOYE ADD PRIMARY KEY(noEmpl);
**** ERROR 02299 DUPLICATE ROWS **** b‐ Identification des tuples dupliqués. Le groupement peut être utilisée pour vérifier les doublets :
SELECT noEmpl FROM Employe Having Count(*) > 1;
noEmpl
995 Ensuite, il suffit de faire l’affichage des tuples dupliqués.
SELECT * FROM Employe WHERE noEmpl = 995;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
85
noEmpl nom ville 995 Paul Québec 995 Paul null
d‐ Suppression des tuples inutiles dans la table Employe
DELETE FROM EMPLOYE WHERE NOEMPL = 995 AND VILLE IS NULL;
e‐ Modification du schéma de Employe et création de la clé primaire.
ALTER TABLE EMPLOYE ADD PRIMARY KEY(NOEMPL);
7.20 Jointure SQL Lorsquʹune requête utilise les attributs de deux ou plusieurs tables, soit dans la réponse, soit dans le prédicat, le calcul fait appel à une jointure naturelle ou à une jointure thêta. Cet opérateur binaire permet de joindre les tuples des tables référées par les opérandes et cela, chaque fois qu’une paire de tuples vérifie la condition de jointure (thêta).
7.20.1 Jointure naturelle La jointure naturelle implique une condition de jointure particulière qui utilise tous (ou avec SQL‐92 une partie des attributs communs) les attributs de même nom dans lʹune et lʹautre des relations opérandes. Avec la base BD3 Dotation des ressources humaines, la jointure entre les tables Assignations et Postes est possible par lʹentremise de la condition de jointure dans laquelle les attributs communs jouent un rôle essentiel. Par exemple, trouver le matricule (noPoste) des employés qui occupent un poste.
SELECT noEmpl FROM Assignations A, Postes P WHERE Assignations.noPoste = Postes.noPoste;
Cette jointure est naturelle parce que lʹattribut utilisé dans la jointure porte le même nom dans les deux tables opérandes. En SQL‐92, la formulation de cette jointure naturelle est plus simple.
SELECT NOEMPL FROM ASSIGNATIONS NATURAL JOIN POSTES;
La formulation demeure la même, quʹil y ait un ou plusieurs attributs dans la jointure naturelle. Si la jointure naturelle doit être restreinte à un seul attribut de même nom dans chaque opérande, il suffit de le nommer explicitement avec la clause USING(). Ce sera le cas
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
86
lorsque la clé est composée de plusieurs attributs qui sont aussi la clé étrangère dans une autre relation. Une jointure pourrait nʹexploiter quʹun seul attribut de la clé étrangère. Par exemple, dans la base Dotation des Ressources Humaines, il suffit dʹenrichir le schéma de la relation Postes avec lʹattribut villePoste pour obtenir une clé composée. Du même coup, le schéma de la table Assignations est aussi enrichi du même attribut et la clé étrangère est composée de trois attributs.
Postes (noPoste*, villePoste*, description) Assignations(noPoste*, villePoste*, noEmpl*, debut, quart)
Pour faire une jointure naturelle en nʹutilisant que lʹattribut noPoste, il suffit de formuler la clause ainsi :
SELECT noEmpl FROM Assignations JOIN Postes USING (noPoste);
Pour faire une jointure basée sur les deux attributs de la clé étrangère :
SELECT noEmpl FROM Assignations JOIN Postes USING (noPoste)
Assignations JOIN Postes USING (villePoste); NB : En SQL‐92, les attributs de jointure naturelle explicitement utilisés dans le USING ne doivent pas être préfixés dans la liste du SELECT et dans le WHERE.
7.20.2 Jointure thêta (jointure sous condition thêta) Pour la jointure thêta, la jointure naturelle est augmentée dʹune sélection formulée avec une condition plus ou moins complexe. Cette sélection est effectuée de préférence en même temps (mode pipeline) que la jointure. Par exemple, sʹil faut trouver les matricules (noEmpl) des employés qui occupent un poste de mécano, la jointure est de type thêta et est légèrement plus complexe que celle de type naturel. En SQL‐92, cette première jointure est formulée avec lʹopérateur JOIN.
SELECT noEmpl FROM Assignations [as] A JOIN Postes [as] P
ON A.noPoste = P.noPoste and description = 'mécano'; Dans ce cas‐ci, la clause ON permet de spécifier la condition de jointure avec les attributs qui peuvent avoir un nom différent tout en partageant le même domaine. Il est toutefois possible de formuler une jointure thêta sans faire appel à l’égalité sur les attributs. Dans ce cas, la jointure sʹapparente à un produit cartésien assorti dʹune condition de filtrage.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
87
7.21 Division relationnelle Lorsqu’une requête suppose la quantification universelle d’au moins un attribut d’une relation de base ou dʹune relation intermédiaire, il faut songer à la division relationnelle. C’est un opérateur utile, mais moins utilisé dans les activités courantes que les cinq opérateurs dits essentiels ou de base. Le dividende de la division est une relation intermédiaire constituée avec un lot dʹattributs de la sortie augmenté avec l’attribut quantifié. Le diviseur est une relation intermédiaire formée de l’attribut quantifié universellement. Ces relations intermédiaires sont calculées par des opérations relationnelles sur les relations de base de données. Exemple : Lister le nom des clients ayant acheté un exemplaire de tous les articles actuellement en inventaire dont le prix de vente est inférieur à 35 $. En algèbre relationnelle la réponse serait obtenue simplement par la division algébrique.
Ventes1**(article, nom) ÷ Inventaire1** (article) Les relations Ventes1** et Inventaire1** sont des relations intermédiaires obtenues par une projection appropriée sur une relation de base. Par exemple au regard de la question précédente, le diviseur Inventaire1 est formé des articles en inventaire dont le prix de vente est moins que 35.00 $. La division relationnelle n’est pas un opérateur essentiel, car elle peut se formuler avec une expression complexe composée que des opérateurs élémentaires comme la projection, la sélection et la différence. Les points essentiels à retenir dans la formulation d’une division sont les suivants : a) Le dividende est une relation qui fournit les attributs de la réponse recherchée. b) Le diviseur est une relation qui contient l’attribut quantifié universellement. Le point critique est l’identification de l’attribut quantifié universellement. Il est souvent précédé du mot tous ou toutes.
7.21.1 Division relationnelle en SQL Une implémentation possible en SQL de la requête ci‐dessous fait appel au GROUP BY et au Having Count(). Lister le nom des clients ayant acheté un exemplaire de tous les articles actuellement en inventaire dont le prix de vente est inférieur à 35 $. Rappelons qu’il s’agit de trouver le nom des clients actuels qui apparaissent dans la table Ventes en combinaison avec tous les articles qui sont dans la table Inventaire.
SELECT nom FROM Ventes V WHERE article in
(SELECT article FROM Inventaire I
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
88
WHERE I.prixVente < 35.00 and cout is NOT NULL) GROUP BY nom Having Count(DISTINCT V.article) =
(SELECT Count(DISTINCT article) FROM Inventaire WHERE prixVente < 35.0);
Inventaire : article* prixVente cout Count (DISTINCTarticle) a5 12 8 4 (diviseur) a1 10 5 a2 20 15 a7 15 15 Nombre d’articles vendus à moins de 35$ a3 25 null a16 45 34
Ventes : nom* article* qte* Serge a1 3 Ted a2 5 André a1 2 Serge a2 4 Jocelyn a1 2 Serge a3 4 Valérie a7 3 Lucie a7 2 Serge a5 3 Serge a7 5 Christine a3 6
Serge a1 7 Figure 7.30a
N.B. Le groupement est fait avec lʹattribut de la réponse et la sous‐requête est formulée avec lʹattribut du diviseur. Notez aussi que lʹattribut du diviseur et celui du dividende nʹétant pas la clé, il faut supprimer les valeurs en double par le mot clé DISTINCT. Le HAVING Count() permet de garder que les tuples des clients qui ont acheté tous les articles encore en inventaire et dont le prix de vente est inférieur à 35 $. Si l’opérateur de division était directement implémenté dans le langage ou disponible comme une fonction en librairie, la formulation de la requête deviendrait relativement simple en dépit de sa complexité inhérente.
SELECT nom FROM Ventes WHERE FOR_ALL article in (SELECT article FROM Inventaire WHERE prixVente < 35.00 and cout is NOT NULL);
Serge
Having Count(*) = 4
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
89
Une autre façon moins élégante, mais qui fournit un résultat correct est le calcul de l’expression algébrique équivalente à la division au moyen des opérateurs de base de l’algèbre relationnelle: (r ÷ s) = ΠR-S(r) - ΠR-S((ΠR-S(r) x s) - r) où R-S est une différence entre les attributs du schéma R et ceux de S.
Formulation de la division en SQL La formulation de la division algébrique peut être réalisée sans trop de difficultés en SQL. Nous allons illustrer lʹalgorithme de passage de lʹalgèbre à La clause SQL au moyen de lʹexemple suivant: Question : Lister le nas des étudiants qui sont inscrits à tous les cours offerts présentement. Lʹexpression relationnelle correspondante est la suivante :
R1 (nas, noCours) ÷ R2 (noCours) Les dividende et diviseur sont obtenus par des opérations sur les relations de base. L’attribut noCours partage le même domaine sémantique.
Etudiant : nas* age noCours* frais Classe : noCours* prof n20 23 ift‐5 50 ift‐5 Audy n30 21 ift‐8 40 mat‐10 Gagnon n40 19 art‐5 60 art‐5 Gagnon n20 23 mat‐10 55 soc‐20 Laurence n20 23 soc‐20 65 ift‐8 Bouquier n20 23 art‐5 40 soc‐22 Dumas n20 23 soc‐22 50 n25 22 art‐5 35 n50 20 mat‐10 45 n50 20 ift‐5 55
Figure 7.30b
a‐ Les cours dont il est question dans le dividende sont ceux choisis par les étudiants. Cette relation intermédiaire peut être fournie par lʹexpression algébrique suivante :
Π nas, noCours (Etudiant)
b‐ Les cours dont il sʹagit dans le diviseur sont ceux offerts présentement et listés dans la table Classe. Cela comprend les cours qui n’ont pas été choisis, bien que ceux‐ci ne seront jamais dans la réponse.
Π noCours (Classe)
La division s’exprime alors avec ces deux opérandes de la manière suivante : Π nas, noCours (Etudiant) ÷ Π noCours (Classe)
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
90
Les deux opérandes sont eux‐mêmes des expressions algébriques aussi complexes que cela est nécessaire. Si dans une SGBD, l’opérateur SQL pour la division est implémenté, alors la formulation de la clause est immédiate. Mais cet opérateur n’est pas encore présent dans le SQL.
Algorithme général Pour formuler une division en SQL, on peut adopter une approche basée sur le comptage des tuples (lʹautre est fondée sur la logique du 1er ordre). Dans lʹexemple précédent, il sʹagit de compter combien de cours différents sont choisis par chaque étudiant et ne retenir un étudiant dans la réponse que si ce nombre égale celui des cours distincts offerts par les classes présentement à lʹhoraire pour le trimestre en cours. En premier, il faut savoir que : ‐ Lʹattribut (ou les attributs) de la réponse est celui qui sera utilisé dans le GROUP BY. ‐ Lʹattribut quantifié universellement (tout) et qui est aussi l’attribut de la relation diviseur sera utilisé pour filtrer les partitions formées par le Having. ‐ La sous‐requête dans le Where du bloc principal exprime la relation de lʹopérande diviseur. À partir de ces considérations l’algorithme d’écriture de la division est la suivante : Protocole d’écriture de la clause SQL pour la division : 1‐ Exprimer le dividende de lʹexpression algébrique en tenant compte du prédicat de sélection imposé aux tuples. Exemple : tous les cours de 1999, ne sont pas tous les cours présents dans la relation Cours, mais bien une sélection de ceux‐ci. 2‐ Regrouper les tuples du dividende calculé par un GROUP BY formulé avec lʹattribut de la du schéma de la réponse escomptée. 3‐ Filtrer les partitions par un Having Count([distinct] <attrib‐diviseur>) qui utilise lʹattribut du diviseur. Le DISTINCT est essentiel si lʹattribut du diviseur nʹest pas aussi la clé. 4‐ Filtrer les partitions par un prédicat inclus dans la clause Having et qui comporte une comparaison avec une sous‐requête formulée avec lʹexpression algébrique du diviseur. Le DISTINCT n’est nécessaire que si lʹattribut du diviseur nʹest pas aussi la clé. 5‐ Supprimer dans le SELECT du début, les attributs qui ne sont pas dans le GROUP BY et y ajouter, au besoin, les fonctions désirées. En SLQ : noCours est une clé primaire, donc il n’y a pas de redondance à supprimer si la table est cohérente :
SELECT nas FROM Etudiant
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
91
GROUP BY nas Having Count(noCours) = (
SELECT Count(noCours) FROM Classe);
Une fonction dʹagrégation peut être incluse dans la réponse :
SELECT nas, Count (noCours), FROM Etudiant GROUP BY nas Having Count(noCours) = (
SELECT Count (noCours) FROM Classe);
Diviseur et dividende plus complexes Le diviseur est une relation quelconque qui peut être fournie par un calcul préalable rendu plus complexe en y ajoutant des sélections ou des jointures. Exemple : Lister le nas des étudiants en informatique qui sont inscrits à toutes les classes de sociologie. Le dividende est ainsi ramené aux seuls étudiants en informatique : (σ SUBSTR(noCours, 1, 3) = ʹiftʹ (Π noCours, nas (Etudiant) )) De même pour le diviseur qui n’inclut que les cours de sociologie : (σ SUBSTR(noCours, 1, 3 = ʹsocʹ (Π noCours (Classe))) La division est alors exprimée ainsi : (σ SUBSTR(noCours, 1, 3) = ‘ift’ (Πcours, nas (Etudiant) )) ÷
(σ SUBSTR(noCours, 1, 3) =’soc’ (Πcours (Classe)))
En SQL : SELECT nas
FROM Etudiant WHERE SUBSTR(noCours, 1,3) = 'ift' GROUP BY nas Having Count (noCours ) = (
SELECT Count(noCours) WHERE SUBSTR(noCours, 1,3) = ‘soc’ FROM Classe);
2‐ Le dividende peut être aussi plus complexe si on ajoute un prédicat de sélection plus sélectif pour le regroupement des tuples. Lister le nas des étudiants ayant plus de 22 ans qui suivent des cours en informatique et qui sont inscrits à toutes les classes de sociologie. Le dividende est transformé pour ne retenir que les tuples des étudiants ayant plus de 22 ans.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
92
(σ age > 22 and SUBSTR(noCours, 1,3)='ift' (Π nas, noCours (Etudiant))
La division est alors exprimée ainsi : (σ age > 22 and SUBSTR(noCours, 1,3)='ift' (Π nas, noCours (Etudiant) ) ÷ (σSUBSTR(noCours, 1,3)= ʹsocʹ (Πcours (Classe) )) En SQL :
SELECT nas FROM Etudiant WHERE age > 22 and SUBSTR(noCours, 1,3)='ift' GROUP BY nas Having Count (noCours) = (
SELECT Count(noCours) FROM Classe WHERE SUBSTR(noCours, 1,3)= 'soc');
Mise à jour de l’extension d’une table : UPDATE ( et DELETE) Il est possible aussi de réutiliser la même question fondée sur la division pour mettre à jour une relation. En effet la mise à jour est précédée dʹune recherche, qui est formulée par un prédicat de sélection qui peut être aussi complexe que l’exige la requête. Le but de cette sélection est d’identifier le ou les tuples à mettre à jour. Exemple : Diminuer de 10.00 $ les frais pour les étudiants qui ont été inscrits à toutes les classes dʹinformatique.
UPDATE Etudiant SET frais = frais - 10.00 WHERE nas IN ( SELECT nas FROM Etudiant WHERE SUBSTR(noCours, 1,3)='ift'
GROUP BY nas Having Count (cours) = (
SELECT Count(noCours) FROM Classe WHERE SUBSTR(noCours, 1,3)='ift'));
6.21.2 Approche par la logique On peut aussi créer une autre expression SQL en utilisant la clause EXISTS pour implémenter l’équivalent de la division relationnelle. Prenons comme exemple la question ci‐dessous avec la base DB3. ‐ Lister le nom des clients ayant acheté un exemplaire de tous les articles actuellement en inventaire.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
93
Cette question a pour réponse le nom des clients ayant une certaine propriété, celle dʹavoir acheté tous les articles en inventaire. Examinons en premier le cas particulier dʹun seul client dont le nom est ʹn5ʹ; ensuite nous généraliserons la requête. L’assertion, il n’existe pas un article en inventaire qui n’est pas vendu à ʹn5 doit être vraie. Pour infirmer ce test, il suffit de trouver un contre‐exemple d’un article en inventaire, par exemple ‘a2’ qui n’est pas mentionné dans une transaction de vente au client ʹn5ʹ. Si un tel article (le contre‐exemple) existe, il est alors évident que le client ʹn5ʹ ne peut pas être dans la réponse de la division, puisqu’il n’a pas acquis cet article ʹa2ʹ. Pour démontrer que cet article ʹa2ʹ est absent de la table Ventes pour le client ʹn5ʹ, il faut effectuer une recherche dans la table Inventaire afin de confirmer que ʹa2ʹ est bel et bien en inventaire et vérifier quʹil nʹexiste pas une transaction de vente pour cet article au nom du client ʹn5ʹ (représenté par la variable v.nom).
SELECT ‘n5’ /*vide car 'n5' pas acquis tous les articles */ FROM Ventes V WHERE NOT EXISTS (
SELECT * FROM Inventaire I WHERE NOT EXISTS (
SELECT * FROM Ventes X WHERE X.article = 'a2' and X.nom = 'n5'));
Si cette clause affiche une réponse, alors cela confirme que l’article ʹa2ʹ est en inventaire et n’a pas été l’objet d’une transaction dans la relation Ventes. Il faut maintenant formuler une condition qui garantit qu’aucun contre‐exemple n’existe pour chaque client de la réponse, peu importe lʹarticle. On vérifie l’absence de contre‐exemple et, du même coup, les clients qui ont acheté tous les articles sont identifiés.
SELECT nom FROM Ventes V WHERE NOT EXISTS (un contre-exemple pour le nom de chaque client testé un à un)
Cette clause fournit donc les clients qui ont acheté tous les articles sur la base de lʹabsence dʹun contre‐exemple. Le contre‐exemple correspond à un article en inventaire qui nʹest pas acheté par le client courant. Pour chaque article, il est formulé ainsi :
(SELECT * FROM Inventaire I WHERE NOT EXISTS (aucun client pour l'article courant tel que testé par la sous-requête qui suit)
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
94
La sous‐requête est donc formulée ainsi : (SELECT * FROM Ventes X WHERE I.article = X.article and X.nom = V.nom) ;
En intégrant toutes ces clauses, on obtient la requête suivante :
SELECT DISTINCT nom FROM Ventes V WHERE NOT EXISTS (
SELECT * FROM Inventaire I WHERE NOT EXISTS (
SELECT * FROM Ventes X WHERE X.article = I.article AND X.nom = V.nom)
) ; Cette clause peut se lire ainsi : Retenir les tuples dans la table Ventes, pour les clients ayant achetés un article de sorte qu’aucun tuple dans la table inventaire soit absent dans la table Ventes pour ce client et cet article. Ventes : Inventaire :
nom* article* qte* article* prixVente cout Serge a1 3 a5 12 8 Ted a2 5 a1 20 15 Serge a5 3
Voici une trace de lʹexécution de cette requête SQL formulée avec la clause logique EXISTS et avec les deux tables ci‐dessus : T u p l e d e V e n t e s Tuple de Inventaire Tuple de Ventes V I X S e r g e , a 1 , 3 a5, 12, 8 (x) Serge, a1, 3 (x) Ted, a2, 5 (x) Serge, a5, 3 ok a1, 20, 15 (x) Serge, a1 ,3 (ok) S e r g e , a 1 , 3 Ted, a2, 5 (x) Serge, a5, 3 x) . . . . . . . . . T e d , a 2 , 5 a5, 12, 15 (retenu) Serge, a1, 3 (x) Ted, a2, 5 (x) Serge, a5, 3 (x)
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
95
T e d , a 2 , 5 a1, 20, 8 (x) Serge, a1, 3 (ok) Ted, a2, 5 (x) Serge, a5, 3 (x) S e r g e , a 5 , 3 a5, 12, 8 (x) Serge, a1, 3 (x) Ted, a2, 5 (x) Serge, a5, 3 (ok) S e r g e , a 5 , 3 a1, 20, 15 (x) Serge, a1, 3 (ok) Ted, a2, 5 (x) Serge, a5, 3 (x)
Suivi de l’évaluation de la clause SQL avec EXISTS
Figure 7.31 Le tuple le plus à gauche n’est conservé que s’il n’y a pas de tuples sélectionnés immédiatement à sa droite. L’implémentation directe du quantificateur universel dans le langage SQL rendrait la formulation plus naturelle et largement plus simple. Exemple : SELECT nom
FROM Ventes WHERE FOR ALL Inventaire.article;
Le mot‐clé FOR ALL proposé comme ajout possible dans la dernière norme sous‐tend alors la division relationnelle. L’attribut opérande du diviseur est celui qui est quantifié. Une autre solution hypothétique serait la suivante :
SELECT nom FROM Ventes, Article DIVIDE [EXACTLY] Ventes BY Article ON Article ;
7.22 Jointure externe gauche (left outer join) La jointure externe gauche est utile pour calculer efficacement certaines réponses que la jointure simple ou complète ne peut pas trouver directement. Par exemple, pour obtenir le nom des articles vendus et leur prix de vente s’il est connu, il suffit de faire une jointure externe gauche. L’ajout des tuples qui ne participent pas à la jointure est effectué en dernier lieu, après la jointure.
SELECT V.article, I.prixVente FROM Ventes V, Inventaire I Where V.article = I.article(+) ;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
96
nom* article* qte* Inventaire : article* prixVente cout Serge a1 3 a5 12 8 Ted a2 5 a1 10 5 André a1 2 a2 20 15 Serge a2 4 a7 15 15 Jocelyn a1 2 a16 45 34 Valérie a7 3 Lucie a7 2 réponse : article prixVente Serge a5 3 a1 10 Serge a7 5 a2 20 Christine a3 6 a7 15
Serge a1 7 a5 12 a3 null
Figure 7.31a
La jointure est effectuée selon les termes de la condition de jointure, ensuite les tuples de la relation Ventes n’y participant pas sont ajoutés à l’extension de la réponse. Le résultat fournit le nom et le prixVente des articles vendus, incluant ceux qui ne sont plus en inventaire. Le (+) identifie la table qui fournit les valeurs nulles aux tuples ajoutés, lesquels sont concaténés avec les tuples de la table de gauche qui ne participent pas à la jointure. Il y aura autant de (+) quʹil y a dʹattributs mis en relation par la condition de jointure. Cependant, la grammaire SQL impose une contrainte qui limite la participation d’une même table à une seule jointure externe dans une requête. Si la requête exige une double jointure externe, il faudra soit renommer la table résultat de la première jointure externe par un alias, soit restructurer la requête. En SQL‐92, la syntaxe de la jointure externe gauche est plus simple et plus explicite.
SELECT V.article, I.prixVente FROM Ventes V LEFT OUTER JOIN Inventaire I ON V.article = I.article ;
La clause ON indique les attributs de jointure qui peuvent avoir un libellé différent.
7.23 Jointure droite (right outer join) Dans ce cas, il y a ajout à la jointure, les tuples de la relation de droite ne participent pas à celle‐ci. Exemple : Lister le nom et le coût des articles en inventaire déjà vendus et y ajouter ceux qui ne sont plus en inventaire, qu’ils aient été vendus ou pas.
SELECT article, cout FROM Inventaire I, Ventes V WHERE I.article = V.article(+) and cout is NOT NULL;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
97
Voici une clause SQL différente de la même requête.
(SELECT article, cout FROM Inventaire WHERE cout is NOT NULL and article IN
(SELECT article FROM Ventes))
UNION (SELECT DISTINCT article, cout FROM Ventes);
Jointure complète Une telle jointure peut être calculée par l’union de deux tables de même schéma. Par exemple, supposons la base suivante représentant les employés travaillant dans les départements. Un employé travaille dans un seul département, mais un département encadre le travail de plusieurs employés.
Departement(noDep*, ville) Employe(noEmpl*,nas, nom, age, noDep)
La requête consiste à lister le nas des employés et le nom du département où ils travaillent, en incluant aussi les employés dont le nas est inconnu et qui travaillent dans certains départements dont le nom est aussi inconnu.
(SELECT nas, nom FROM Departement D, Employe E WHERE D.noDep = E.noDep(+))
UNION (SELECT nas, nom FROM Departement D, Employe E WHERE D.noDep(+)= E.noDep);
La première partie de la clause fournit les données sur les départements encadrant des employés et inclut aussi les départements dont le nom est inconnu. La deuxième partie de la même clause donne aussi les données sur les départements encadrant des employés, mais ajoute aussi les employés n’ayant pas encore d’assignation de travail dans un département. Autres opérateurs : union, différence, intersection. Ces opérateurs exigent une conformité du schéma des tables opérandes. Ainsi, pour faire l’union de deux extensions, il faut que leur schéma respectif ait le même nombre de colonnes et que chaque colonne correspondante ait un même domaine.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
98
7.23 Union Lʹunion relationnelle est similaire à lʹunion ensembliste et requiert lʹidentité du schéma pour les deux opérandes. Ainsi, on obtiendra une même liste des articles vendus en quantité supérieure à 5 et ceux dont le prix de vente est supérieur à deux fois le coût par lʹexpression suivante :
(SELECT article FROM Ventes WHERE qte > 5)
UNION (SELECT article FROM Inventaire WHERE prixVente > 2 *cout);
Un tel énoncé est dit composé d’un bloc de niveau 1 et de 2 blocs de niveau 2. Il est traité en trois étapes : calcul de la première sous‐requête de niveau 2, calcul de la deuxième sous‐requête de niveau 2 et finalement, la réalisation de l’union (niveau 1) accompagnée de la suppression des doublets. Pour garder tous les tuples des deux ensembles y compris les doublets, il faut utiliser lʹopérateur UNION ALL. Le tri d’une telle clause composée de deux sous‐requêtes fait appel au rang des attributs (puisque les deux sous‐requêtes ont le même schéma) ou aux attributs de la première sous‐requête.
(SELECT article FROM Ventes WHERE qte > 5)
UNION ALL (SELECT article FROM Inventaire WHERE prixVente > 2 *cout); ORDER BY 1; <- usage du rang pas recommandé!
Le résultat de cette union est trié par un ORDER BY qui se réfère aux attributs de la première relation soit par leur rang, soit de préférence par leur nom. En effet, certains changements ultérieurs faits au schéma auront des effets sur la requête exprimée au moyen du rang.
7.24 Intersection Cet opérateur permet de trouver les tuples qui sont présents dans deux tables et dont le schéma est mutuellement compatible. Par exemple, la clause ci‐dessous fournit les articles vendus en quantité supérieure à 5 et qui sont toujours dans l’inventaire.
(SELECT article FROM Ventes
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
99
WHERE qte >5) INTERSECT
(SELECT article FROM Inventaire WHERE cout is NOT NULL);
7.25 Différence
Cet opérateur calcule tous les tuples d’un premier ensemble qui ne sont pas dans un deuxième ensemble. Par exemple, pour trouver les articles en inventaire encore invendus, il suffit d’exécuter la clause ci‐dessous.
(SELECT article FROM Inventaire WHERE cout is NOT NULL) MINUS (SELECT article FROM Ventes);
Bien entendu, la différence peut aussi être formulée avec la négation dʹun prédicat dʹinclusion (NOT IN).
SELECT article FROM Inventaire WHERE article NOT IN (SELECT article FROM Ventes;
7.26 Mise à jour de lʹextension d’une table L’extension d’une relation de base est mise à jour par lʹajout, la suppression ou la modification d’un ou de plusieurs tuples. Les opérations de mise à jour peuvent être effectuées si l’utilisateur a les droits requis sur les relations ou les tables ciblées par les modifications. S’il est propriétaire de la relation, il a tous les droits. Si la relation appartient à un autre utilisateur, il lui faudra obtenir les droits d’accès appropriés. Ceux‐ci sont spécifiés par la commande GRANT. Tout propriétaire d’une table peut utiliser cette commande avec l’une de ses tables et autoriser d’autres comptes à faire des manipulations sur sa table, notamment celles de mise à jour et de suppression des tuples. GRANT SELECT,DELETE, UPDATE ON Empl TO util3 [With Grant Option] Dans cet exemple, l’option [WITH GRANT OPTION] permet à utilisateur3 de propager à d’autres les droits reçus du premier utilisateur. Si cette option ne lui est pas accordée, il ne pourra pas propager les droits à d’autres utilisateurs, même s’il est propriétaire des tables.
Ajout de tuples dans une relation Pour ajouter des tuples dans une extension, on utilise la commande INSERT dans un des formats possibles : ‐ Insertion dʹun seul tuple de valeurs : Format 1:
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
100
INSERT INTO <nom-relation> (attrib1, attrib4, attrib5) VALUES (liste de valeurs);
L’ordre des valeurs doit être celui des attributs du schéma. Il est aussi possible de fournir la liste des attributs dans lʹordre correspondant à celui des valeurs fournies. Les attributs absents du schéma correspondent alors à des indicateurs de nulls dans le tuple créé. ‐ Insertion dʹun ensemble de tuples obtenus par une sous‐requête et en provenance d’une autre table : Format 2: INSERT INTO <nom-relation> (liste-attributs-du-schéma)
SELECT <liste des attributs> FROM <relation1> [WHERE <prédicat>];
Les tuples proviennent des tables existantes de la BASE DE DONNÉES. Cette insertion peut être effectuée par un bloc de PL/SQL. Si une table est créée pour ranger les ventes importantes définies comme ayant une quantité supérieure à 10, elle pourra être chargée par le format 2 du DML INSERT : INSERT INTO Ventes_avant2000 (article, nom, qte)
SELECT article, nom, qte FROM Ventes WHERE qte > 10;
7.27 Suppression de tuples dans une relation Pour effectuer cette opération, il faut d’abord rechercher le ou les tuples à supprimer. Si la condition est spécifiée avec une clé, un seul tuple sera supprimé; autrement, tous les tuples vérifiant le prédicat de sélection seront supprimés. La suppression est autorisée par le propriétaire de la table ou par lʹutilisateur qui a le privilège RESOURCE
DELETE FROM <relation> [WHERE <prédicat>]; où le prédicat est une formule logique comportant au besoin une combinaison de conditions dont lʹévaluation fournit une valeur booléenne. Lors de lʹopération de suppression des tuples, les déclencheurs de suppression de tuples sont activés, lʹespace disque alloué reste monopolisé par cette table et la journalisation de la suppression est faite à la fois dans le journal et dans les segments de rollback de la transaction. Pour éviter cette lourdeur qui accompagne une telle suppression de tuples, il faut utiliser la commande TRUNCATE TABLE. Cette dernière supprime les tuples sans qu’il y ait une journalisation dans les segments de rollback (Voir sur l’architecture du SGBD, liste LRT de la ZMP) et une autre dans le journal interne. De plus, lʹespace peut être conservé ou supprimé et lʹindex est mis à jour.
TRUNCATE TABLE Empl {REUSE STORAGE | DROP STORAGE } ;
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
101
Exemples : a) Supprimer les ventes faites à Renée ou à Marie‐Claude.
DELETE FROM Ventes WHERE nom = 'Renée' or nom = ‘Marie-Claude’;
b) Suppression de tous les tuples de la table Ventes.
DELETE FROM Ventes; TRUNCATE TABLE Ventes REUSE STORAGE; ‐‐supprime le contenu de la table sans faire de journalisation c) Supprimer les transactions de vente concernant les articles sans marge bénéficiaire, ou ceux qui sont en rupture de stock (null pour cout). Chaque tuple supprimé est inscrit dans le journal.
DELETE FROM Ventes WHERE article NOT IN SELECT article
FROM Inventaire WHERE (prixVente - cout) < 0 or cout is null);
La sous‐requête est en premier évaluée afin d’identifier les tuples correspondant à des articles vendus avec perte ou ceux dont le coût est inconnu. Par la suite, la table Ventes est balayée pour supprimer les tuples présents dans le sous‐ensemble identifié par la sous‐requête.
7.28 Mise à jour des tuples Les tuples sont modifiés après une recherche implicite des tuples vérifiant le prédicat de recherche.
UPDATE <relation> set <expression> WHERE <prédicat>; Par exemple, pour corriger la transaction d’achat faite en janvier par Valérie pour inscrire neuf(9) articles ‘a7’ et non pas trois(3) comme cela fut enregistré dans la table Ventes par la transaction initiale. La recherche est en premier faite en spécifiant les trois attributs de la clé de sorte quʹun seul tuple sera trouvé, soit celui de lʹachat fait en janvier.
UPDATE Ventes SET qte = 9 WHERE nom = ‘Valérie’ and article = 'a7' and qte = 3;
En supposant que la transaction soit aussi inscrite dans une table des ventes du mois et qu’elle soit faite correctement par Valérie , la nouvelle valeur pour la mise à jour peut être obtenue par une sous‐requête sur la table Ventes_Janvier.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
102
UPDATE Ventes SET qte = (SELECT qte FROM Ventes_Janvier) WHERE nom =‘Valérie’and article =‘a7’);
La nouvelle valeur est fournie par le résultat dʹune sélection dans une autre relation. La sous‐requête doit retourner un seul tuple, sinon il y aura une erreur dans lʹopération de comparaison.
7.29 Modification du dictionnaire de données (DD) La modification du dictionnaire de données est une opération plutôt rare et réservée au DBA et aux quelques utilisateurs dotés exceptionnellement des privilèges sous‐tendus par les rôles RESOURCE et CONNECT ou les privilèges équivalents accordés à l’utilisateur par le DBA. La commande CREATE pour la création dʹune table peut être très complexe et inclure notamment, lʹespace de table et les paramètres PCTFREE et PCTUSED.
CREATE TABLE <nom-table>( attribut-i spec-colonne,...) ;
Figure 7.31b
Affichage du type des attributs dʹune relation Le module SQL*Plus a une commande DESCRIBE qui affiche la description des attributs dʹune relation ou dʹune vue relationnelle stockée dans le dictionnaire. DESCRIBE <nom‐relation> SQL> DESCRIBE Parent
spec‐colonne :=
typ
DEFAULT expr contrainte‐
,
PCFREE entier PCTUSED entier INITRANS entier MAXTRANS entier TABLESPACE param STORAGE param CLUSTER param
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
103
réponse : Name Null? Type nas NOT NULL number(3,0) pren varchar2(25) nase NOT NULL number(3,0) prene varchar2(25)
La réponse exige aussi de paramétrer correctement lʹenvironnement SQL*Plus pour limiter la largeur des colonnes et remplacer la valeur par défaut fournit par la longueur du type.
COLUMN nas FORMAT 999; COLUMN NASEENF FORMAT A20; /*col.20 car. pour afficher NASEENF*/
Exemple: DES[CRIBE] Client Voici les types de la table Client fournis par la commande DESCRIBE : DESCRIBE Client Client : Attribut Type nom NOT NULL varchar2 30 adresse varchar2 20 ville varchar2 20 tel char 13 La commande DESCRIBE donne la liste des attributs de la relation et leur type respectif, et cela, à partir du dictionnaire de données. Selon la norme SQL/92 de lʹISO, la commande de création spécifie la clé primaire et les clés étrangères d’une relation, de même que les vérifications du domaine des attributs. La création d’une table peut donc être plus complexe et inclure aussi des contraintes du domaine définies au niveau de l’attribut ou au niveau de la table. En voici un exemple en SQL92. CREATE TABLE Empl(
matricule integer CHECK (matricule < 99999), nom char varying (40) check (LENGTH (nom > 3), salaire real NOT NULL, tauxOfficiel real NOT NULL, noDep integer check( noDep >= 10 and noDep <= 30), CONSTRAINT cp_empl primary key (matricule), CONSTRAINT fk_empl_depart FOREIGN KEY (noDep) REFERENCES Depart(noDep) MATCH FULL (SELECT noDep FROM Depart)) CONSTRAINT tauxHoraire check(salaire/35 > tauxOfficiel));
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
104
La création de la même table avec le SQL‐DDL d’Oracle est spécifiée par la clause DDL suivante : CREATE TABLE Empl(
matricule number(5) NOT NULL check (matricule < 99999), nom varchar2(40) check (LENGTH (nom > 3), salaire number(8,2) NOT NULL, tauxOfficiel number(4,2) NOT NULL, noDep number(3)NOT NULL check (between 10 and 30), CONSTRAINT cp_empl primary key (matricule), CONSTRAINT fk_emplDepart FOREIGN KEY (noDep) REFERENCES Depart(noDep), CONSTRAINT tauxHoraire check(salaire/35 > tauxOfficiel));
Selon le SGBD, le système peut créer automatiquement un index unique (SYSTEM Index) pour la clé primaire ou lancer une procédure interne pour valider tout ajout dans Empl afin de vérifier que cet employé est absent de la table. De même, le SGBD vérifie si le département auquel est associé le nouvel employé existe déjà dans la base de données en exploitant l’index système créé pour la clé primaire de la table Depart. Lʹindex interne du système sur la clé principale peut être temporairement désactivé par le DBA. Lorsque l’index deviendra actif, le SGBD vérifiera l’unicité des tuples présents dans la table de manière à vérifier la contrainte de clé. En SQL de Oracle, il est possible de contrôler le placement de la table en précisant le tablespace. De plus, le DBA a la possibilité de contrôler le rangement des tuples en accordant les droits d’accès appropriés aux différents schémas. CREATE TABLE Inventaire(
article varchar2(10) CONSTRAINT cp_inventaire PRIMARY KEY CHECK(article = UPPER(article)), prixVente NUMBER(5,2), cout NUMBER(5,2));
CREATE TABLE Ventes(
nom VARCHAR2(35) NOT NULL CHECK (nom = UPPER(nom)), article VARCHAR2(10) NOT NULL, qte NUMBER(2) CHECK (qte < 99), CONSTRAINT cp_ventes PRIMARY KEY (nom, article, qte), CONSTRAINT fk_ventes_inventaire FOREIGN KEY(article) REFERENCES Inventaire (article)tablespace donnees_bd1);
Les contraintes dʹune table peuvent être modifiées par un ALTER TABLE MODIFY ou une combinaison de ALTER TABLE DROP et ALTER T ABLE ADD.
ALTER TABLE Ventes MODIFY article number CHECK (article <100);
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
105
Pour désactiver temporairement une contrainte référentielle :
ALTER TABLE Ventes DROP fk_ventes_inventaire; ALTER TABLE Ventes ADD fk_ventes_inventaire FOREIGN KEY (article) REFERENCES Inventaire(article));
La contrainte référentielle est supprimée et peut être recréée par la suite par la commande ALTER TABLE ADD. Au moment de la création, la contrainte doit être vraie pour tous les tuples de la table. La validation est rétrospective. La suppression d’une table requiert le privilège RESOURCE si lʹutilisateur nʹest pas le propriétaire de la table.
DROP TABLE <relation> [CASCADE CONSTRAINTS]; Supprimer la table Client et du même coup son schéma et son extension :
DROP TABLE Client [CASCADE CONSTRAINTS]; La table est supprimée ainsi que tous les index et contraintes (CHECK et les triggers) définis sur celle‐ci.
Ajout d’un nouvel attribut dans un schéma de relation Une modification à un objet‐table du dictionnaire de base de donnéesse fait par la clause ALTER TABLE :
ALTER TABLE <relation> ADD (< col spec>); ALTER TABLE <relation> MODIFY (< col spec>);
Exemple : Ajouter la colonne (attribut) taxe à la relation Ventes :
ALTER TABLE Ventes ADD (taxe number(5,2)); La valeur du nouvel attribut est nulle pour les tuples déjà présents dans l’extension au moment de l’ajout du nouvel attribut. Cette valeur initiale nulle peut être modifiée par une mise à jour subséquente. L’insertion automatique du null dans les tuples déjà dans la table avant l’ajout de l’attribut fait réapparaître les problèmes d’interprétation des nulls.
Modification du type d’un attribut dans un schéma Modifiez le schéma de la relation Ventes pour que le nom de l’article occupe dorénavant trois caractères au lieu de deux initialement prévus :
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
106
ALTER TABLE Ventes MODIFY (article Char (3));
Éditeur et format dʹaffichage du logiciel SQL*Plus (Rappel) Le module SQL*Plus a son petit éditeur de ligne. Il affiche les résultats selon des formats précisés par des directives simples dont les valeurs par défaut sont dérivées de la spécification du type des attributs. Il est aussi possible de faire appel à un éditeur du système dʹexploitation par la commande EDIT.
7.30 Directives de SQL*Plus 1‐ Pour initialiser les variables dʹenvironnement de lʹéditeur :
SET PAUSE 'appuyer sur le ENTER' SET PAUSE ON -- activation de la pause dans le déroulement
Pour arrêter le déroulement de lʹaffichage des données et solliciter lʹutilisateur dans le but dʹamorcer la suite de lʹaffichage :
SET PAUSE OFF -- désactivation de la pause 2‐ Pour remplacer le tampon SQL*Plus par un autre qui devient le courant
SET BUFFER tampon1.sq1 Le tampon courant pour le module SQL*Plus est changé pour un autre nommé tampon1.sql 3‐ SET PAGESIZE 40 Pour spécifier le nombre de lignes sur chaque écran de sortie. 4‐ Pour garder dans un fichier, une trace de lʹaffichage :
SPOOL C:\trace1.sql –trace dans fichier C:\trace1.sql SPOOL OFF --arrête la trace et retour au fichier par défaut
5‐ Pour spécifier le format dʹaffichage par SQL*Plus et spécifier la largeur des colonnes pour les valeurs numériques et les chaînes.
COLUMN salaire FORMAT 999.99 COLUMN nom FORMAT A25
Cette directive permet de remplacer les largeurs dérivées du type des attributs par celles fournies pour la durée de la session. 6‐ Éditeur de ligne : L’utilitaire a quelques fonctions d’édition plutôt élémentaires :
CHANGE .chaine.nouvelleChaine.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
107
Le caractère ʹ.ʹ est le délimiteur de chaîne et permet de trouver la chaîne pour la remplacer par une nouvelle. Ce délimiteur peut être quelconque et son choix exige seulement quʹil nʹapparaisse pas dans la chaîne qui fait l’objet de la recherche. Exemple : Remplacement de la chaîne ‘ function’ par ‘ fonction’ change .function.fonction. ou encore change ;function;fonction; -- si le . est présent dans la chaîne recherché, il faut utiliser un autre caractère, par exemple le ;. - LIST pour lister le contenu du buffer SQL. 2 ‐‐ pour positionner la ligne 2 comme ligne courante de lʹéditeur. - RUN pour lancer l'exécution de la clause SQL placée dans le tampon SQL. - START A:\creation.sql -- pour lancer un fichier de commandes. - SAVE A:\fichier.sql Pour sauvegarder dans un fichier la clause SQL dans le tampon. ‐ GET A:\fichier.sql Pour rappeler le contenu du fichier et le placer dans le tampon de lʹéditeur. Il peut ensuite être exécuté.
EDIT nom_fichier Pour ouvrir un fichier avec lʹéditeur local et y insérer les commandes SQL. La suite de commandes peut aussi être terminée par le caractère /, auquel cas, le fichier est exécuté lors de sa fermeture. Autrement, le fichier est « exécuté » par la suite par la commande :
start C:\nom_fichier ou @ A:\nom_fichier. 7‐ Pour appeler lʹéditeur par défaut du système et éditer le fichier nom_fichier ou celui nommé dʹaprès le tampon de SQL*Plus, il suffit de faire :
EDIT [A:\fichier.sql]. Il devient alors facile de modifier le fichier et de lʹexécuter soit par @ ou par un START. Si le nom du fichier est absent dans lʹappel de lʹéditeur, le fichier ouvert porte le nom du buffer interne de SQL*Plus. Par exemple : En entrant EDIT, lʹinterprète appelle lʹéditeur par défaut de manière à vous permettre de le modifier par lʹentremise des fonctions de lʹéditeur. Ensuite, il suffit dʹexécuter ce fichier avec la commande suivante :
START A:\fichier1.sql.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
108
Exercices résolus 1‐Voici une instance de la base de donnée JMC dans laquelle le type de lʹattribut date‐m a été modifié pour devenir une date de type Date.
Joueur : nom* prenom age nationalite societe Tremblay Patricia 22 fr CGI Gagnon Jacques 23 ca Bell Marois Sherly 20 us Bell Beaumont Michel 24 fr Bell Dery Paul 19 ca ‐‐‐‐ Fournier Nadine 22 ca DMR
Match : dateM* lieu* score nomG nomP 12‐jan‐1998 Qc 12‐7 Beaumont Gagnon 5‐sept‐1995 Mtl 3‐5 Marois Fournier 23‐mai‐1996 Qc 12‐3 Fournier Gagnon 3‐jan‐1998 Qc 8‐6 Tremblay Dery 22‐oct‐1999 Qc 2‐5 Beaumont Gagnon 18‐nov‐1999 Paris 7‐4 Tremblay Gagnon
Commanditaire : societe* montant duree Bombardier 4000 2 CGI 2000 5 LGS 3000 2 DMR 6000 3 Bell 5000 2 Caisse Populaire 5000 5
1.1 Formuler la clause SQL pour afficher les noms des joueurs n’ayant perdu aucun match joué à Qc. Vous ne savez pas à priori si un joueur a joué ou pas un match, mais il doit en avoir joué un pour être dans la réponse escomptée. Réponse : Il y a plusieurs formulations possibles pour obtenir une bonne réponse dans l’hypothèse de tables cohérentes. 1ère : Les joueurs de match qui ont joué un match à Québec sans perdre leur match.
(SELECT [distinct] nomG FROM Match WHERE lieu = 'Qc') MINUS (SELECT [distinct] nomP FROM Match
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
109
WHERE lieu = 'Qc'); 2ème SELECT nom
FROM Joueur J, Match M WHERE J.nom = M.nomG and M.lieu =’Qc’ and NOT IN (SELECT nomP FROM Match WHERE lieu = 'Qc');
3ème SELECT nom
FROM Joueur J, Match M WHERE J.nom = M.nomG or J.nom = M.nomP and M.lieu =’Qc’ and not exists ( SELECT * FROM Match M WHERE J.nom = M.nomP and lieu = 'Qc');
4ème SELECT nomG FROM Match WHERE lieu = 'Qc' and nomG not in (SELECT nomP FROM Match WHERE lieu = 'Qc');
2‐ Formuler la clause SQL pour lister le nom de joueur, le montant de la commandite et la société qui commandite un joueur en incluant dans la réponse les sociétés qui ne commanditent pas encore un joueur. Réponse : La jointure externe droite en SQL sʹexprime avec le (+) du côté de la relation qui reçoit les valeurs nulles ajoutées dans le calcul de la réponse. Cette jointure peut être aussi exprimée par un LEFT OUTER JOIN de SQL‐92 :
SELECT nom, montant, C.societe FROM Joueur J, Commanditaire C WHERE J.societe(+) = C.societe;
N.B. Si les clés primaire et étrangère étaient composées de plusieurs attributs, la condition de jointure serait un prédicat conjonctif de plusieurs égalités et avec autant de signes (+) pour formuler la jointure externe.
nom montant societe Gagnon 5000 Bell Marois 5000 Bell Beaumont 5000 Bell 4000 Bombardier Tremblay 2000 CGI 5000 Caisse‐Pop Fournier 6000 DMR 3000 LGS
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
110
3‐ Formuler la clause SQL pour lister le nom du perdant et du gagnant du premier match, i.e. le plus ancien match inscrit dans la base de données. Réponse :
SELECT nomP, nomG FROM Match WHERE dateM in (SELECT MIN(dateM)FROM Match);
Les dates sont ordonnées selon lʹordre chronologique. La fonction min() retourne la plus petite, soit la plus ancienne. nomP nomG Gagnon Beaumont La clause suivante fournit aussi la bonne réponse :
SELECT nomP, nomG FROM Match WHERE dateM < ALL (SELECT dateM FROM Match);
4‐ Voici une requête SQL dont la réponse est calculée sur la base de données ALU‐NORD.
SELECT ville, sexe, Count(nas) [as] nb-de-Personnes FROM Ouvrier O, Usine U WHERE ville = 'Quebec' -- une seule ville retenue GROUP BY ville, sexe; -- pour québec ==> 2 sexes
Combien de lignes et de colonnes seront présentes dans la réponse ? Réponse : Le GROUP BY est calculé sur l’attribut sexe de la table temporaire ne contenant que les ouvriers de la seule ville Québec. Les valeurs possibles pour lʹattribut sexe sont ʹMʹ et ʹFʹ. Donc la réponse comportera 2 lignes et 3 colonnes.
réponse : ville sexe nb‐de‐Personnes Québec F 67000 M 66560
Sommaire Les implémentations complètes de la norme SQL‐92 sont encore rares. Quelques SGBD de bon niveau ont une implémentation de niveau 2 de la norme. Les manufacturiers de SGBD semblent toujours avoir la volonté de faire un peu moins que la norme et dʹajouter des clauses qui particularisent leurs produits. Le SQL est un langage de données utilisé
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
111
par les SGBD en sus parfois de leur langage de données propriétaire. Il permet dʹexprimer facilement les requêtes jusquʹau niveau intermédiaire. Les requêtes complexes présentent cependant quelques difficultés lorsque lʹon veut les exprimer en SQL. Les divisions relationnelles sont parfois plus difficiles à exprimer. En dépit de leur complexité, les requêtes utilisant la division relationnelle sont plus courantes dans le contexte de l’exploitation dʹun entrepôt de données. Finalement, les opérations de jointures externes gauche et droite sont de plus en plus utilisées dans les applications de bon niveau permettant ainsi de faire en une seule opération ce qui en demanderait plusieurs. Exercices du chapitre 7 Le calcul de la réponse aux questions ci‐dessous est effectué en faisant référence à lʹextension de la base de BD3. Formuler une clause SQL (conformément à la version SQL qui est disponible) et donner lʹexpression algébrique équivalente et cela, pour chaque question. Lorsque dans lʹénoncé on fait référence au seul nom de la relation (table), il sʹagit alors de fournir tous les attributs de cette relation, soit en les énumérant, soit en utilisant lʹastérisque. Attention : toutes les fonctions et types étudiés dans ce chapitre ne sont pas nécessairement disponibles dans la version du SGBD qui vous sera rendue disponible pour faire les exercices ci‐dessous. 1. Afficher le nom et le numéro des employés dont le nom renferme la chaîne du prénom (et dont le taux horaire est plus grand que 13.00$. 2. Afficher, sans redondance, le numéro des employés qui sont inscrits comme travaillant le soir ou le jour. 3. Afficher, par ordre alphabétique décroissant, le nom des employés dont le taux horaire est compris entre 13.00 et 26.25 $. 4. Afficher le numéro des employés inscrits comme ne travaillant pas le jour. 5. Le taux horaire pour un employé qui travaille plus de 40 heures par semaine est le double de son taux horaire normal. Quel est le nom des employés qui touchent plus de 750.00 $ par semaine lorsquʹils travaillent 10 heures en temps supplémentaire ? 6. Quels sont les numéros de poste occupés par les titulaires inscrits comme travaillant le jour ou le soir, mais pas la nuit ? 7. Lister le numéro des employés qui n’ont pas de quart de travail.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
112
8. Lister le numéro des employés dont la date dʹembauche (début) est inconnue ou antérieure au 24‐sep‐1994. 9. Lister le nom, le numéro de poste, la description du poste et la date dʹembauche des employés dont le taux horaire est supérieur à 12.50$. 10. Lister le nom des employés dont le métier est ʹmécano1ʹ et dont la date dʹembauche est postérieure au 31 décembre 93 (31‐DEC‐1989). 11. Donner la valeur de vérité des conditions ** SQL‐92 ci‐dessous :
(1, 2, 3) is NULL (1, 2, null) is null not (1, 2, 3) is null not (null, null) is null (null, null) is NOT NULL (1, null, 3) is NOT NULL
** Ces expressions ne sont pas nécessairement implémentées ou disponibles dans tous les SGBD courants. 12. Formuler le schéma dʹune table Fournisseur dʹarticles et trouver deux à deux, ceux qui offrent exactement les mêmes articles. Pour formuler la solution, il faut pouvoir exprimer en SQL que deux ensembles sont égaux (exercice non trivial). 13*** (Problème avec une certaine difficulté) Une base de données est constituée de deux tables Cours(noCours*, inscriptions) et Salle(noSalle*, capacite). Le problème générique dʹallocation formulé par E. F.Codd et dont la solution nʹest pas triviale consiste à formuler une requête SQL avec ou sans vue relationnelle permettant dʹallouer les salles à des cours en respectant la contrainte suivante : (inscriptions) <= (capacité) . En dʹautres mots, lʹallocation doit se faire de manière à ce que chaque personne inscrite ait un siège dans la salle sans limiter ou préciser le nombre de sièges à conserver libres pour les inscriptions tardives. Voici les deux extensions à utiliser pour lʹénoncé ci‐dessus.
Cours : noCours* inscriptions Salle : noSalle* capacite c6 40 s10 30 c5 50 s4 45 c4 55 s6 50 c3 65 s14 55 c2 70 s12 65 c1 80 s2 70 s8 85
La réponse attendue est la suivante :
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
113
réponse : noCours* noSalle inscriptions capacite c6 s4 40 45 c5 s6 50 50 c4 s14 55 55 c3 s12 65 65 c2 s2 70 70 c1 s8 80 85
La première étape pourrait être celle de faire une opération pour trier au préalable les tuples des deux extensions en ordre descendant selon la capacité de la salle et ensuite, selon les inscriptions à chaque cours. Une autre façon serait de créer une vue pour calculer une jointure avec la condition inscriptions <= capacité. 14 *** ‐ Refaire le problème no 3 en faisant lʹhypothèse que dans chaque cours il doit y avoir au moins une place vide pour une inscription tardive. Dans ce cas, la condition sera la suivante : (inscriptions) < (capacite). Est‐ce que les solutions précédentes sont valables ? Comment est‐il possible d’obtenir la meilleure solution avec les tables fournies ?
Projet ALU-NORD
Une base de données relationnelle composée des tables qui correspondent au MCD ci‐dessous doit être implantée et chargée avec un état initial correspondant à celui des extensions fournies. Modèle conceptuel La société ALU‐NORD fabrique plusieurs pièces moulées en aluminium dans différentes usines spécialisées réparties dans le monde. Les usines implantées obligatoirement dans différentes villes produisent diverses pièces sur commande; ces dernières sont le plus souvent stockées sur place et livrées entièrement à la fin de la production. Le matériau en vrac est livré en volume prédéterminé par le fournisseur sur commande de chaque usine. Les fonctions de gestion des ressources humaines se limitent à gérer un dossier sur chaque ouvrier. L’implantation de cette base doit se faire en ne validant que les contraintes de participation et de cardinalité et cela sans développer de triggers. Pour y arriver, vous disposez du mécanisme de contrainte référentielle et des actions référentielles disponibles dans le SGBD utilisé pour réaliser ce projet.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
114
Voici, à titre dʹinformation seulement, quelques règles plus complexes sous‐tendues par le MCD et qui ne seront pas au départ validées dans la base de données, car cela exigerait l’utilisation de triggers et de packages qui seront étudiés brièvement dans un chapitre ultérieur. ‐ La suppression dʹun métier entraîne celle de sa participation à lʹassociation Titularise et du même coup, la suppression des ouvriers compétents dans ce métier. Il est possible de supprimer une entité dans VracMat si et seulement si sa participation à lʹassociation Utilise est aussi supprimée. ‐ La suppression dʹune usine entraîne aussi celle de ses productions, mais pas de ses ouvriers, ni des matériaux en vrac qu’elle utilise. Un métier n’est inscrit dans la base que si un ouvrier est qualifié pour le pratiquer dans son travail. Un client n’est représenté que s’il a effectué au moins l’achat d’une production complétée par une usine. Globalement, il ne peut pas y avoir plus de 10 métiers différents dans tout le réseau des usines. De même, le même matériau ne peut pas être fourni par plus de quatre fournisseurs. Ces contraintes seront formulées dans
Production noPiece* lot* qte dateDebut delai
Realise(1,1) Client
noClient* ville credit Achete (1,n) (0,1)
Ouvrier nas* nom prenom sexe dNaiss
b h
Metier noMetier* classeMetier taux
Titularise (1,n)
Usine noUsine* ville capacStock surfaceUsine
VracMat noMat* noFourn* volL cat
Travaille (0,1)
(0,n)
(0,n) (0,m)Vol
(0,n)
(1,1)
Commande
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
115
le schéma dans la mesure où le SGBD utilisé pour lʹexploitation le permet. Le modèle ALU‐NORD est un graphe acyclique.
Sémantique de quelques attributs :
dNaiss Date de naissance de lʹouvrier comprenant le jour, le mois et lʹannée. embauche : Date du premier contrat de travail exprimée avec le jour, le mois et lʹannée. classeMetier: Classes de compétence pour un métier identifiées par un entier de 1 à 5. capacStock : Nombre de pièces pouvant être conservées à lʹusine dans une enceinte dont la
surface représente obligatoirement 10% de celle de lʹusine. lot : Unité de production dʹune sorte de pièces correspondant à une commande. delai : Nombre prévu de jours pour la fin de la production dʹun lot de pièces. volL : Quantité minimale dʹun matériau en vrac pouvant être livré à une usine. noMetier : Identifiant pour un métier exercé par plusieurs ouvriers. cat : Qualité du matériau en vrac fourni ou livré par un fournisseur. qte Quantité de pièces produites dans un lot de production VolC : Volume de matériau en vrac commandé par une usine. surfaceUsine : Surface totale de lʹusine dont 10% est consacrée à lʹentreposage. nas : Numéro assurance sociale sous forme dʹune chaîne composée uniquement de
chiffres Modèle relationnel Le MCD ci‐dessus est transposé en modèle logique relationnel (MRD) en appliquant les règles de transposition appropriées. Certaines des contraintes seront implémentées par les clés référentielles, tandis que dʹautres le seront par des contraintes dʹattribut et finalement certaines par des triggers. VracMat(noMat*, noFourn*, volL, cat) Usine(noUsine*, ville, capacStock, surfaceUsine) Metier(noMetier*, classeMetier, taux) Production(noPiece*, lot*, qte, dateDebut, delai, noUsine, noClient) Ouvrier (nas*, nom, prenom, sexe, dNaiss, embauche, noUsine, noMetier) Commande(noUsine*, noMat*, noFourn*, volC) Client (noClient*, ville, credit) N.B . Lʹastérisque (*) indique un attribut primaire, i.e. un attribut qui compose la clé primaire. Pour une meilleure lisibilité du MRD, le trait dʹunion est utilisé dans le libellé dʹun attribut à la place du souligné qui demeure cependant le caractère dʹespacement admissible par le langage SQL‐DDL. Par exemple, lʹattribut classe‐Metier sera spécifié dans le schéma avec le libellé ʹclasseMetierʹ. Les autres caractères de ponctuation sont exclus du libellé du schéma. Les caractères $ et # sont admissibles, mais à toute fin pratique inutilisés.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
116
Domaines de quelques attributs Certains attributs ont un domaine sémantique particulier défini dans le tableau ci‐dessous. Par définition, l’indicateur null est hors domaine. Toutefois, elle peut être utilisée pour valuer certains attributs. Le domaine des autres attributs est considéré comme étant implicite et dérivé des extensions des tables ci‐dessous. Les attributs dont le domaine n’est pas imposé auront un domaine syntaxique qui repose sur le type et que vous pouvez définir en vous référant aux contenus des tables. cat: { ʹAʹ, ʹBʹ, ʹC, ʹD’} capacStock: ]10...9999] volL ]0... 1000] sexe : {ʹFʹ, ʹMʹ} classeMetier : { 1...5} taux: ]10.00 ... 32.75] lot : [ʹb10ʹ.... ʹb99ʹ] credit:[1000....10000] noMetier : [‘j1’... ‘j99’]
surfaceUsine:]100...5000]
nas : chaîne de 9 car.
noUsine : [ʹu10’...’u99’]
N.B. Un crochet ouvert par la gauche ou par la droite indique une borne exclue du domaine. Un crochet fermé par la gauche ou par la droite indique lʹinclusion de la borne dans le domaine. La valeur nulle nʹest pas une valeur du domaine, mais correspond quand même à une réalité pour quelques attributs, notamment les attributs délai et noUsine. Les attributs dont le domaine n’est pas défini explicitement doivent être spécifiés selon les données fournies par les tables.
Remarques 1‐ De par la contrainte sur la capacité de stockage, toute production inférieure à 11 unités doit quitter lʹusine immédiatement et donc être livrée au client. Cette petite production exceptionnelle nʹest donc pas inscrite dans la base de données, ce qui explique la définition du domaine sémantique pour cet attribut. 2‐ La sémantique de plusieurs attributs peut être dérivée du contexte de la relation. 3‐ Les signes orthographiques ne sont pas autorisés pour le nom dʹattribut, sauf si le système utilisé implémente le Unicode. Seulement les dernières versions des SGBD assurent la représentation des caractères sur 16 bits ou plus. Les données peuvent être cependant stockées avec les signes orthographiques. 4‐ La connaissance des données a aussi permis de formuler quelques règles de validation syntaxiques qui viennent en restreindre le domaine et qui seront renforcées dans le schéma de la base ALU‐NORD. Ces règles ne sont pas complètes, mais il vous est demandé d’implémenter que celles‐ci et cela pour ne pas surcharger la description de la base de données.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
117
Attribut Validation syntaxique nas chaîne de 9 caractères représentant des chiffres seulement noFourn 1 ère lettre est un ʹfʹ noMetier 1 ère lettre est le caractère ʹjʹ et sa longueur max. est 3 noUsine 1ère lettre est le caractère ʹuʹ, les autres étant des chiffres. La
longueur max. est 3 noClient 1ère lettre est le caractère ʹcʹ noMat Chaîne dont la 1ère lettre est le caractère ʹm ʹ nom Chaîne de lettres en majuscules. prenom 1ère lettre est un haut de case. Les autres lettres sont des
caractères éventuellement mixtes. noPiece 1ère lettre est un ʹpʹ et la dernière un caractère chiffre. lot 1ère lettre est un ʹbʹ .
5‐ Les clauses SQL étudiées dans les chapitres précédents sont extraites soit de la norme SQL‐89, soit de celle du SQL‐92. Le SGBD utilisé pour réaliser votre mini projet nʹa pas nécessairement implémenté le niveau 3, voire même le 2 de la norme SQL‐92. Par conséquent, certaines commandes ou options du DDL peuvent ne pas être disponibles pour la réalisation du projet (par exemple : ON DELETE SET NULL est absent dans Oracle).
Instance de la base de données à la création N.B. Au moment de lʹexploitation, lʹétat de la base de données peut être différent de lʹinstance ci‐dessous qui doit cependant être incluse. La valeur nulle est représentée par le NULL ou par des espaces. Lʹinstance de la base de données doit vérifier les contraintes dʹintégrité définies. Lʹabsence dʹune valeur nulle pour un attribut dʹune table signifie que cet attribut doit avoir obligatoirement une valeur dans lʹextension.
Production : noPiece* lot* qte dateDebut delai noUsine noClient p1000 b70 1200 17‐dec‐1998 20 u12 c35 p2000 b80 900 23‐nov‐1998 15 u24 null p1500 b75 1150 5‐nov‐1998 null u12 c50 p1000 b35 700 28‐nov‐1998 12 u18 c25 p2300 b42 2375 10‐dec‐1998 7 u14 c35 p1000 b74 130 18‐nov‐1995 5 u10 c45
Metier : noMetier* classeMetier taux j27 1 32.75 j41 2 28.53 j75 5 27.68 j19 4 18.40 j40 3 22.90 j44 3 19.29
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
118
Client : noClient* ville credit c35 Québec 7500.00 c45 Paris 3000.00 c25 New‐York 2000.00 c50 Toronto 10 000.00
Ouvrier :
nas* nom prenom sexe dNaiss embauche noUsine noMetier196573432 BOIVIN Louise F 10‐1‐1965 20‐oct‐1995 u12 j27 198029823 DION Paul M 17‐5‐1980 17‐nov‐1998 u12 j41 199549525 BOUCHARD Félix M null 8‐jan‐1995 u14 j75 197479013 GENEST Christian M 4‐2‐1974 27‐jan‐1994 u12 j27 196871002 GAUTHIER Fabrice M 25‐4‐1968 19‐sep‐1995 u14 j19 197287815 DUSSAULT Luce F 11‐10‐1972 12‐mar‐1995 u18 j40 195545260 TREMBLAY Jacques M 9‐11‐1955 1‐mar‐1991 u22 j19 196236649 SOUCY Sybelle F 2‐12‐1962 23‐jan‐1998 u32 j41 195265972 DRECJACKSEN Jack M 8‐5‐1952 10‐jan‐1995 u10 j75 195023961 SMITH Marie F 27‐11‐1950 26‐jan‐1996 null j44
Commande : noUsine* noMat* noFourn* volC u12 m27 f510 15 u14 m27 f320 25 u22 m75 f420 30 u32 m41 f650 80 u18 m41 f100 40 u14 m75 f520 45 u10 m27 f320 60 u18 m27 f100 90 u18 m75 f100 50
Usine : noUsine* ville capacStock surfaceUsine u10 Chicoutimi 1000 300 u12 Québec 2000 1700 u14 Montréal 3000 1250 u18 Boston 1000 1550 u20 Rimouski 1275 1100 u22 Toulouse 1400 500 u24 Paris 3000 450 u26 Zurich 2300 1200 u28 Atlanta 2000 1700 u30 Toronto 1250 420 u32 Vancouver 970 600
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
119
VracMat : noMat* noFourn* volL cat m35 f270 20 B m75 f520 15 A m35 f300 15 C m27 f100 25 A m27 f510 25 C m75 f100 30 B m41 f650 40 A m75 f420 50 A m41 f100 20 D m27 f320 15 B
Chargement initial de la base de données Le schéma des relations (tables) doit être créé de sorte quʹune table (parent) soit déjà créée avant quʹelle soit référencée par une autre table (type enfant). Lors de la suppression des tables, lʹordre inverse sʹimpose! Le chargement de la base de données peut être fait par lʹexécution dʹun script de l’annexe B et lancé de préférence avec la commande START A:\ALU_data.txt (pour un volume plus important de données, il y a le chargeur SQL*LOADER dont les fonctions sont décrites en annexe). Le script de chargement est aussi disponible au moyen du lien hypertexte disponible sur le site Web.
Exploitation de la base de données Lorsque lʹénoncé de la question fait référence seulement au nom de la table, ce sont les valeurs de tous les attributs qui sont demandées, sauf si les attributs sont demandés explicitement. Exemple, lister lʹusine de Québec signifie afficher la valeur de chaque attribut de lʹusine de Québec. La rubrique de chaque section de questions est indicative. Normalement, la réponse à une question de cette section fait appel aux opérateurs mentionnés dans la rubrique de cette section et, au besoin, aux autres des sections précédentes. Toutefois, il nʹy a pas quʹune seule façon de formuler la réponse à une question. Dans tous les cas, privilégier la clause SQL la clause la plus simple pour répondre à la question. Il serait utile de lire ou relire immédiatement la section sur les commandes de lʹéditeur de SQL*Plus de manière à formater correctement le reportage de vos réponses et cela, dès le début de votre travail. Lorsque la réponse consiste à formuler une commande SQL, placez cette dernière dans un fichier sur disquette A; ou sur le disque de votre station ou ordinateur de travail. Son exécution peut ensuite être lancée à partir du fichier : @A:\fb3.sql ou START A:\fb3.sql. Le nom du fichier est formé par la lettre et le numéro de la question, exemple: b3. N.B. Si exceptionnellement lʹimplémentation SQL du système qui vous est demandé d’utiliser ne permet pas de répondre à une question, formulez la requête sur papier en SQL‐92.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
120
Contenu du rapport du mini projet Dans le rapport, il faut identifier chaque question, reprendre le texte de la question demandée et fournir la clause SQL permettant de calculer la réponse. Une trace de lʹexécution correcte permet de vérifier la justesse de la réponse obtenue. Pour quelques questions, notamment les opérations de mise à jour et de suppression, certains autres éléments doivent être présentés afin de démontrer que lʹopération demandée a été exécutée avec succès. Par exemple, si lʹon demande dʹajouter des tuples dans une table, il faudra donc afficher la table avant et après pour démontrer que les ajouts demandés ont été réalisés correctement. Le schéma de la base de données sera vérifié et reconnu correct s’il passe avec succès le test appelé test_alu.rtf. Bien que ce test ne soit pas complet, il sera présumé suffisant pour valider votre schéma. Il faudra donc dans votre rapport présenter une trace imprimée qui illustre que le test a été passé avec succès.
Questions à formuler en SQL Attention : Les questions ne sont pas nécessairement en ordre croissant de difficulté. Elles ont été mises dans un ordre quasi aléatoire de manière à ce que, s’il y a lieu, la répartition des questions dans une équipe fournisse des ensembles de complexité similaire.
A‐ Création et chargement de la base de données À partir du MCD, commenter les transpositions nécessaires pour obtenir le MRD donné dans lʹénoncé du projet. Ensuite, spécifier le schéma dans lequel sont formulées les contraintes dʹintégrité de domaine et syntaxiques formulées explicitement de même que celles inhérentes au modèle relationnel ALU‐NORD qui peuvent être implémentées sans avoir recours à un trigger. N.B. Attention au type de chaîne de caractères : un attribut dont la valeur apparaît être de longueur variable dans les instances de table doit être spécifié VARCHAR2(), sinon le type CHAR() peut être utilisé. L’instance des tables présentée dans l’exercice doit aussi servir d’indication pour identifier les attributs pouvant avoir une valeur nulle. Effectuer la création de la base de données pour ensuite la charger avec les tuples fournis par le fichier Aludata.txt. La création des tables parents doit précéder celle des tables enfants. Pour la suppression des tables, cʹest lʹordre inverse quʹil faut respecter. Pourquoi doit‐il en être ainsi? Validité du schéma. De plus, vous pouvez vérifier la validité de votre schéma en exécutant le Test‐Alu que vous trouverez sur le site web ou éventuellement sur le disque CD d’accompagnement.
B‐ Création de tuples avec un fichier de commandes 1‐ Représenter dans la base de données chaque membre de lʹéquipe (lorsque le projet est réalisé en équipe) comme un ouvrier qui travaille dans une usine existante (en excluant l’usine ʹu50ʹ) de votre choix et qui titularise un métier déjà exercé par un ouvrier. Comme valeur du nas, utiliser votre numéro de dossier étudiant auquel vous ajoutez un 0 devant, mais avec un nom fictif. Ensuite, inscrire la date du jour comme étant celle de
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
121
votre embauche dans une usine du groupe ALU‐NORD. Tous les attributs doivent avoir une valeur, valide dans le domaine associé. Lorsque le travail est fait en équipe, avec un seul ordre DML les membres de lʹéquipe sont ajoutés dans la base, sauf un. Lorsquʹil sʹagit dʹune modification ou dʹun ajout dʹun tuple, afficher la table avant et après les opérations. 2‐ Ajouter 2 nouvelles usines qui nʹont pas encore utilisé de matériau en vrac. 3‐ Pour un membre de lʹéquipe, formuler un ordre SQL pour modifier la date de lʹembauche en lʹaugmentant de 15 jours. 4‐ Pour la même personne de lʹéquipe dont la date dʹembauche a été modifiée précédemment, changer lʹusine où elle travaille pour quʹelle soit ʹu50ʹ. Commenter le résultat de lʹopération. 5‐ Supprimer une usine que vous avez ajoutée précédemment.
C‐ Sélection simple 1‐ Rechercher et afficher le tuple que vous avez ajouté dans la base de données (le membre de votre équipe encore représenté). 2‐ Lister toute lʹinformation concernant seulement les deux premiers métiers présents dans la table Metier. Les libellés de la table doivent être affichés intégralement. Utiliser le pseudo‐attribut ROWNUM présent implicitement dans toutes les tables pour ne retenir que les tuples dont le rang dans la table réponse est inférieur à ROWNUM. 3‐ Lister les différents métiers titularisés par les ouvriers en bonifiant leur taux horaire de 15%. 4‐ En présumant que lʹâge de la retraite légale soit 65 ans, lister le nom, le nas et la date exacte prévue pour la retraite des ouvriers masculins inscrits dans la base de données. La retraite commence au 65e anniversaire dʹun ouvrier. Utiliser la fonction ADD_MONTHS() pour le calcul de l’âge de la retraite. 5‐ Lister le nom, le prénom et le nas des ouvriers dont le prénom est inclus dans le nom. 6‐ Lister le numéro des usines situées à Montréal ou à Boston qui ont une superficie de plus de 1500 mètres carrés. 7‐ Lister la ville et le numéro des usines dont 40% de la capacité de stockage est supérieure 1100 pièces. 8‐ Lister le nom et le prénom des ouvriers encore sans affectation de travail dans une usine. 9‐ Simuler ce quʹaurait été la date de fin des productions en augmentant le délai de livraison de 10% arrondi à lʹentier supérieur. La réponse est fournie avec les no‐pièce, le lot et la date de fin de production avec lʹintitulé ʺFin de production + 10%ʺ. N.B. : il y a une différence entre une chaîne encadrée par des ʹ ʹ, qui correspond à une constante littérale et une autre encadrée par des ʺ ʺ, qui correspond à un alias ou à un intitulé de colonne dans lequel il peut y avoir des espaces. Noter aussi le résultat obtenu avec lʹaddition dʹune valeur nulle, i.e. lorsque le délai nʹest pas spécifié. 10‐ Lister les nas, nom et prénom des ouvriers embauchés entre le 1 janvier 1995 et le 31 décembre 1998.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
122
D‐ Sélection avec les fonctions SQL 1‐ Lister les ouvriers qui ont été embauchés en 1995. 2‐ Combien y a‐t‐il de productions dont le client est inconnu? 3‐ Lister la plus grande surface dʹentreposage disponible peu importe lʹusine. 4‐ Quel est lʹâge moyen des ouvriers embauchés au cours des 6 premiers mois, peu importe lʹannée? 5‐ Quel est le taux horaire moyen payé pour tous les métiers confondus? 6‐ Lister le nom, lʹâge et le numéro dʹusine où travaillent les ouvriers nés après le 1 janvier 1950. Lorsquʹun ouvrier nʹa pas dʹaffectation, le libellé ʺSANS USINEʺ est affiché tel quel. Utiliser la fonction NVL(). 7‐ Afficher le nombre total dʹouvrières inscrites présentement dans la base de données. 8‐ Quel est le plus grand volume de matériau en vrac utilisé par une usine? 9‐ Quel est le délai le plus court imposé aux productions réalisées par l’ensemble des usines ʹu12ʹ et ʹu18ʹ ? 10‐ Lister le nom et le prénom de chaque ouvrier en le précédent du libellé M. ou Mme selon le cas. Le nom est affiché en majuscules suivi de la première lettre du prénom. 11‐ Modifier votre nom dʹouvrier (éventuellement pour chaque membre de lʹéquipe) dans la base de données pour quʹil apparaisse en lettres minuscules sauf pour la 1e lettre qui reste une majuscule. Souvenez‐vous que vous êtes encore inscrit(s) dans la base de données ! 12‐ Quelle est la quantité totale de pièces produites jusquʹici par lʹensemble des usines? 13‐ Quelle est la date de livraison des pièces dont la production a débuté en novembre, peu importe lʹannée? 14‐ Quel est le nombre de voyelles dans le nom des ouvriers occupant un métier et dont le nom commence par une des lettres suivantes : {ʹBʹ, ʹDʹ, ʹSʹ}. Afficher le nom et le nombre de voyelles quʹil contient. 15‐ Combien dʹouvriers sont des hommes (peu importe lʹusine) dont les quatre premiers chiffres du nas forment un nombre supérieur à 0971? Afficher uniquement le nombre dʹouvriers obtenu. 16‐ Quel est l’âge des ouvriers au moment de leur embauche ? 17‐ Quelle est la quantité totale des productions dont le délai est inférieur à 10 jours ? 18‐ Quel est le taux horaire moyen payé pour les métiers de classe 3 ? 19‐ Quel est le nombre d’ouvriers masculins travaillant dans l’usine ‘u12’ ? 20‐ Lister les numéros des usines qui stockent plus de 3 pièces au mètre carré ?
E‐ Jointures (interne et externe), union et différence 1‐ Lister le nom, le nas des ouvriers et la ville des usines où ils travaillent présentement. 2‐ Lister le numéro des pièces et les quantités produites par les usines de Montréal. 3‐ Lister le no et la classe des métiers titularisés par un homme né en mai peu importe lʹannée. 4‐ Lister le no dʹusine, le no de matériau et la catégorie du VracMat utilisé par les usines. Inclure aussi le numéro des usines nʹutilisant pas présentement un VracMat. 5‐Lister le numéro des lots de pièces et la ville où ils sont réalisés.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
123
F‐ Fonctions de groupement, union, différence et tri 1‐ Donner, pour chaque matériau, le nombre de fournisseurs qui approvisionnent les usines. Afficher le numéro du matériau et le nombre de fournisseurs avec le libellé ʹNb_fournisseursʹ. 2‐ Lister le numéro de pièce, le lot, la date du début et le numéro de lʹusine pour les productions. La liste est présentée par ordre croissant des numéros de lot. 3‐ Compter le nombre dʹusines pour chaque métier. Afficher le numéro du métier et le nombre dʹusines avec le libellé ʹNombre_usinesʹ. 4‐ Afficher les numéros de métiers et leur taux horaire, pour les métiers exercés par au moins deux ouvriers. 5‐ Lister en ordre descendant, le numéro des usines qui nʹont pas encore dʹouvriers. 6‐ Lister pour chaque no de matériau, le volume total de vrac livré aux usines, peu importe le fournisseur. 7‐ Lister le numéro de matériau qui peut être livré par plus de deux fournisseurs. 8‐ Lister par mois d’anniversaire le nas et le nom des ouvriers de métier qui ne travaillent pas encore dans une usine. 9‐ Lister en ordre croissant de nom, le nas et le nom des ouvriers qui travaillent dans des usines qui ont commandé leur matériau en vrac. 10‐ Lister le numéro et la ville des clients ainsi que le numéro de leur lot pour les productions terminées.
G‐ Division relationnelle 1‐ Quels sont les numéros des usines qui ont des ouvriers de tous les métiers dont le taux horaire est supérieur à 28.00 $? 2‐ Quelles sont les usines de Boston ou de Montréal qui ont acheté tous les matériaux de catégorie ‘D’ en vrac? 3‐ Est‐ce quʹil y a un matériau commandé par toutes les usines présentement en opération et dont la capacité de stockage est égale ou supérieure à 3000 pièces ? Une usine est en opération lorsquʹelle a commandé un VracMat pour assurer sa production. C’est bien une division, malgré des apparences qui peuvent être trompeuses! 4‐ Quels sont les clients (noClient seulement) qui n’ont pas acheté toutes les pièces produites, peu importe l’usine ?
H‐ Requête imbriquée 1‐ Afficher le numéro, la ville et la surface des usines ayant la plus grande surface dʹentreposage? N.B. Dans lʹinterprétation du modèle, chaque usine consacre obligatoirement 10% de sa surface à lʹentreposage. 2‐ Quel est le nom et le nas des ouvriers nés la même année (peu importe le mois et le jour) que celle du plus jeune des ouvriers ? 3‐ Lister le numéro des pièces produites en quantité supérieure à la moyenne des productions inscrites jusquʹici, peu importe le lot.
Chapitre 7 Langage SQL
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
124
4‐ Lister le numéro et la surface dʹentreposage des usines dont la capacité de stockage est plus grande que 2 fois la capacité de stockage de lʹusine ayant la plus petite surface dʹentreposage. 5‐ Lister les numéros des clients dont le crédit est supérieur à la moyenne du crédit des clients inscrits dans la base.
I‐ Vue relationnelle 1‐ Créer une vue nommée v1 pour obtenir le nombre total dʹouvriers qui travaillent à chaque usine. N.B. Il est toujours possible de définir un libellé particulier pour désigner le résultat dʹune fonction, et ce libellé peut être utilisé par la suite comme un alias de la colonne. Dans la vue demandée, le nombre total des ouvriers est libellé nbTotalOuvriers. 2‐ Créer une autre vue v2 qui permet dʹobtenir le nombre total dʹouvriers qui ne sont pas au travail, peu importe le métier. Notez quʹil est possible dʹutiliser un libellé pour représenter le résultat dʹune fonction. Ce libellé peut être par la suite utilisé comme un alias ou attribut de la table intermédiaire. 3‐ En utilisant les vues précédentes, formuler une clause SQL pour obtenir le nombre total dʹassignations de travail encore possibles pour les ouvriers. 4‐ Créer une vue relationnelle fournissant le numéro de matériau la quantité totale de chaque matériau livré suite à une commande et cela, peu importe le fournisseur.
J‐ Trigger (déclencheur) Pour répondre à ces deux questions, il faudra avoir terminer le chapitre suivant qui décrit le langage des triggers. 1‐ Implémenter la contrainte qui limite à 10 le nombre total de métiers différents dans tout le réseau. Tester la contrainte en augmentant le nombre de métiers de façon à dépasser cette limite. 2‐ Implémenter une contrainte qui limiterait à quatre le nombre de fournisseurs du même matériau. Tester cette contrainte avec un trigger d’énoncé BEFORE INSERT.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
125
Chapitre 8 Indexation , vue relationnelle, droits d’accès et base de données réactive
8.1 Indexation Il y a plusieurs types dʹindex allant du simple séquentiel, indexé avec un ou plusieurs niveaux, au plus complexe représenté par la famille des index organisés avec la structure d’un arbre. Dans tous les cas, lʹindex utilisé par le moteur de la BD est en quelque sorte un mécanisme dʹaccès rapide aux données, qui vient bonifier lʹaccès séquentiel (balayage) de base toujours disponible par défaut. Sous cet angle d’accès rapide, la famille des index inclut aussi celui par Hashing qui implémente un accès rapide aux tuples par lʹentremise dʹune fonction de calcul d’adresse. Les index les plus utilisés par les SGBD sont ceux de la famille des B‐arbres, notamment le B*‐arbre. Rappelons quʹun B‐arbre organise les valeurs sous la forme dʹun arbre n‐aire de façon à permettre par exemple une recherche dans un lot de 2 millions de tuples en moins de 5 lectures sur disque. Le B*‐arbre se distingue par le fait que les nœuds internes ne pointent que sur dʹautres nœuds internes, sauf pour les feuilles qui pointent sur les données regroupées dans les feuilles. De plus, la gestion et lʹéquilibrage de lʹarbre sont faits pour maintenir une hauteur moyenne quasi fixe et un remplissage moyen des cellules appelé facteur dʹoccupation (fill factor) qui peut varier de 0.25 (Oracle 8) à 0.66.
8.2 B*‐arbre Comme nous lʹavons vu dans un chapitre précédent, le B*‐arbre est une hiérarchie de cellules dʹordre p, ce qui signifie quʹil y a p pointeurs et p‐1 valeurs dans les nœuds internes. Par exemple, lʹindexation du matricule employé, dont le domaine varie de 1 à 100, présente la structure suivante à un moment particulier de lʹexploitation de la BD. B*‐arbre dʹordre 3 (pointeurs et au plus 2 valeurs de clé)
Figure 8.1
20
35 15 17 18 20
25 17
23 25
tuple tuple tuple tuple tuple tuple tuple
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
126
Chaque cellule interne est un nœud et les cellules du dernier niveau sont des feuilles. Chaque feuille a une même structure qui peut varier selon le type d’arbre. Voici une structure typique de la cellule interne (nœud)
Structure d’un nœud Figure 8.1a
Le pointeur à la gauche dʹune valeur conduit à un sous‐arbre dont les valeurs sont inférieures ou égales à la valeur vi, tandis que le pointeur de droite conduit à un sous‐arbre dont les valeurs sont supérieures à vi. La sémantique des pointeurs est la même pour tous les nœuds internes. La structure dʹune feuille est toutefois légèrement différente puisque le pointeur fournit l’adresse du tuple sous la forme de rid (row id ou adresse de tuple).
Caractéristiques dʹun B*‐arbre Lʹintérêt du B*‐arbre ou de lʹune de ses variantes comme le B+‐arbre, réside dans le fait quʹil nʹest jamais largement déséquilibré et que sa hauteur est quasi constante quel que soit le chemin parcouru pour aboutir à une feuille. A tout moment, cette structure arborescente (similaire aux arbres AVL) présente les propriétés suivantes : 1‐ Chaque valeur indexée présente dans les nœuds (internes) est aussi présente dans les feuilles. Les valeurs de chaque bloc d’index sont en ordre lexicographique. 2‐ Seulement quelques valeurs de clé sont utilisées par les nœuds internes de lʹarbre. C’est un index non dense sur les blocs stockant les tuples d’une même table. 3‐ Chaque valeur indexée et présente dans un nœud interne apparaît aussi comme étant la valeur la plus à droite dans le sous‐arbre pointé par le pointeur gauche de la valeur indexée. 4‐ A lʹajout dʹune nouvelle valeur, si la feuille (cellule terminale) devient saturée (i.e. le facteur de saturation est atteint), elle est divisée en deux feuilles de même niveau et la valeur centrale de la feuille dʹorigine devenue saturée (celle avant division) est déplacée vers le nœud de niveau supérieur. Si, ce faisant, ce nœud devient saturé, la division des
Pointeur sur le sous‐arbre droit dont les clés sont supérieures à vi+1.
vi vi+1
Pointeur sur le sous‐arbre droit dont les clés sont supérieures à vi ou égales à vi+1
Pointeur sur le sous‐arbre gauche dont les clés sont inférieures (et égales) à vi .
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
127
valeurs est à nouveau effectuée avec insertion‐déplacement de la valeur centrale du nœud saturé vers un autre de niveau supérieur suivant. 5‐ Lorsqu’on supprime une valeur, elle est en premier supprimée dans la feuille. Il en est de même si elle apparaît aussi dans un nœud interne de l’index. Dans ce cas, la valeur à gauche dans la feuille remplace la valeur supprimée qui apparaît dans le nœud interne. Si la suppression dʹune valeur dans une feuille amène le niveau dʹoccupation en dessous dʹun seuil minimal, les valeurs sont redistribuées entre la feuille dʹorigine et celle immédiatement à sa gauche au même niveau. Sinon, les deux cellules sont fusionnées et la disparition dʹune feuille est propagée au niveau supérieur.
Création et ajout dans un B*‐arbre La création dʹun B*‐arbre dʹordre 3 sous‐tend celle dʹun en‐tête sans aucun nœud ni feuille. Lʹajout de la valeur 20 déclenche la création dʹun premier nœud dans le B*‐arbre, cʹest‐à‐dire que cette structure a au maximum 2 clés et 3 pointeurs par nœud interne. Le facteur de saturation est de 2. Ajout de 20 : la cellule contient alors un pointeur sur le tuple identifié par la valeur 20. Ajout de 40 : idem Ajout de 25 : cet ajout dans la cellule provoque la saturation et donc la division en deux feuilles, suivie de la création dʹun nœud. La valeur la plus à droite dans le sous‐arbre gauche est celle qui apparaît dans le nœud qui pointe sur ce sous‐arbre (lien gauche). Ajout de 15 : ajout de la valeur dans une feuille avec division de la feuille et insertion dʹune valeur dans un nœud interne parent.
20 40
tuple tuple
25
tuple
40 20 25
tuple tuple
20
tuplenull
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
128
Ajout de la clé 17 : Lʹajout de 17 dans une feuille a provoqué sa division et la création dʹune nouvelle feuille, mais aussi la réplication de la valeur centrale vers un nœud. Ce dernier est devenu saturé (>2) et sʹest divisé provoquant la création dʹun autre nœud de même niveau et du déplacement dʹune copie de la valeur centrale vers ce nouveau nœud supérieur. Le processus se répète jusquʹà ce que lʹinsertion de la valeur dans un nœud ne provoque plus de division. Ajout de 14 : Ajout dans une feuille d’un index dʹordre 3
Figure 8.2
20 25
tuple
25 15 20
tuple tuple
40
Blocs de tuples
tuple tuple tuple tuple tuple
20
40 15 17 20
25 17
25
null
14 15
20
40 20
25 15 17
25
tuple tuple tuple tuple tuple
17
tuple
null
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
129
Lʹajout sature une feuille. Il y a donc création dʹune nouvelle feuille pour y ranger la valeur 17. La valeur centrale 15 est répliquée dans le nœud supérieur quʹelle ne sature pas. Le rôle de la valeur qui est déplacée dans une cellule de niveau supérieur est de servir de séparateur entre les deux sous‐arbres inférieurs : le gauche et le droit. Si le système avait placé une autre valeur comme séparateur, disons le 16 qui n’est pas une valeur indexée, la mécanique de l’indexation aurait tout aussi bien fonctionné, à la seule condition que cette valeur ait un bit pour indiquer que c’est un séparateur étranger aux valeurs. Dans un arbre indexant des chaînes de caractères, il devient alors possible d’économiser de l’espace en utilisant des séparateurs formés de chaînes beaucoup plus courtes, ce qui permet d’augmenter sensiblement le nombre d’entrées dans une page d’index. La croissance d’un B*‐arbre est symétrique parce que le rééquilibrage est effectué généralement après chaque lot dʹinsertions (cette opération est dépendante de lʹimplémentation). A lʹétat stable, la longueur du chemin qui sépare la racine de toute feuille est approximativement la même. Par exemple, avec des clés de 100 octets et des pointeurs de 4 octets, un arbre dʹordre 50 avec une cellule de la taille dʹune page peut avoir au cinquième niveau, plus de 2, 500,000 nœuds, chacun contenant près de 50 clés. En dʹautres mots, la recherche dʹune clé parmi les 3 millions de tuples nécessitera dans le pire des cas 6 accès au disque.
Facteurs de croissance d’un B*‐arbre La hauteur dʹun B*‐arbre dépend de plusieurs facteurs dont principalement le nombre de tuples dans la table indexée. Avec un nombre important de tuples, la hauteur de lʹarbre augmente bin sûr, mais très lentement. Il est courant dʹavoir maintenant des tables contenant des milliards de tuples et dʹobtenir des hauteurs dʹenviron 7 (sept) niveaux. Un autre facteur important est celui de la longueur de la clé ou de la valeur indexée. Par exemple, plus une chaîne de caractères indexée est grande, moins il y aura dʹentrées dans une même cellule ou page (ordre plus petit de l’arbre) et, partant, plus la hauteur de lʹarbre sera grande pour indexer le même nombre de tuples. La hauteur de l’arbre peut cependant être raccourcie par l’utilisation de séparateurs de sous‐arbres formés en tronquant la chaîne de la première clé du sous‐arbre droit.
23 , 34 ,
42 , 19 ,
Jean‐Etienne Lazure du Portail, rid16
34 ,
(90 entrées par page) (1000 entrées par page)
page j page j
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
130
Finalement, lorsque lʹattribut indexé est très sélectif, comme c’est le cas pour une clé, la hauteur de lʹarbre sera plus grande que dans le cas dʹun attribut moins sélectif comme lʹattribut ville. En générale, lʹordre de la cellule est choisi pour y ranger un grand nombre de valeurs (exemple: cellule = page) de sorte que les suppressions et les ajouts ne génèrent pas à chaque opération un réajustement du B*‐arbre. Ceci évite un travail qui peut exiger une ou plusieurs lectures de nouvelles pages sur le disque et affecter la performance du SGBD.
Suppression dans un B*‐arbre La suppression dʹune valeur dans une feuille se répercute aussi au niveau des nœuds où lʹon observe non plus une saturation, mais une sous‐saturation. A chaque suppression, la valeur est physiquement supprimée dans la feuille et si cette valeur apparaît aussi dans un nœud, elle doit être aussi enlevée et remplacée par la clé qui se trouve dans la feuille à la gauche de la valeur supprimée. Lʹoccurrence dʹune feuille sous saturée suite à une suppression dʹune valeur déclenche, sʹil y a lieu, la fusion avec une feuille adjacente (gauche ou droite) qui est aussi sous‐saturée, suivie dʹune répartition des valeurs, de telle sorte que les deux feuilles soient remplies à moins de la moitié de la valeur de saturation. Sinon, il y a fusion des deux feuilles pour obtenir une seule qui nʹest pas saturée. Cette création ou disparition des feuilles a aussi une répercussion au niveau des nœuds. Après plusieurs suppressions, lʹarbre peut alors décroître en hauteur.
Index avec des valeurs dupliquées Les valeurs dupliquées peuvent être aussi indexées par le B*‐arbre. Le seul ajout est le chaînage des feuilles dont la valeur est identique, mais avec des adresses de tuple différentes. Au niveau des nœuds, une seule valeur est utilisée pour lʹindexation.
Index avec valeurs dupliquées Figure 8.3
20
40 14 15 20
25 15 17
25
tuple tuple tuple tuple tuple
17 17 Cellule de débordement pour les valeurs identiques. tuple
17
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
131
La recherche des tuples vérifiant le critère dʹune plage de valeurs est possible avec le B*‐arbre par lʹentremise des pointeurs latéraux qui enchaînent les cellules d’un même niveau. La cellule de débordement qui nʹest pas pointée par un nœud interne est dite déconnectée par rapport aux nœuds (ex. Système SGBD SYSBASE) de lʹarbre. De plus, les entrées de lʹindex peuvent être simples ou complexes. Dans ce dernier cas, lʹentrée est traitée comme une composition ordonnée (juxtaposition) des valeurs dʹattributs qui font lʹobjet de lʹindexation de la table. Par la suite, la recherche avec une clé concaténée sera très sensible au respect de lʹorthographe de la chaîne. Dʹun autre côté la recherche avec une valeur partielle (préfixe) nʹest pas très efficace. Elle est cependant hors de question avec un suffixe, car la fouille du B*‐arbre avec une clé partielle tronquée du début est impossible. Recherche dans une table avec un attribut primaire La recherche dans une seule table lancée par une clause de sélection est rapidement effectuée si le prédicat est conjonctif et si les attributs du prédicat sont indexés. Exemple :
Employe (nas*, nom, age, salaire, ville) Select * From Employe Where age = 24;
L’indexation de l’âge dans la table Employe permet d’obtenir rapidement les rid des tuples des employés de 24 ans. Ensuite il suffit de lire ces tuples en utilisant à bon escient les rid obtenus. Si la clause de sélection est une disjonctive, le calcul de la réponse ne bénificie pas autant de l’indexation d’un seul des attributs. Puisque le deuxième attribut exigera le balayage séquentiel de la table, autant alors ne pas utiliser l’indexer et effectuer la vérification du prédicat disjonctif à chaque accès à un tuple de la table. En revanche, si les tous les attributs de la clause de sélection sont indexés, alors les index seront pleinement utilisés pour obtenir les adresses des tuples de la réponse (rid). Dans la dernière étape, chaque rid de cet ensemble d’adresses de tuple permet d’obtenir directement le tuple correspondant et d’effectuer en ligne la projection de sortie. Exemple : Les attributs salaire et ville sont indexés.
Select * From Employe Where Salaire = 25000.00 And ville = ‘Québec’;
L’index sur salaire fournit les rid des employés ayant le salaire de 25000.00. Ces adresses sont regroupées dans un ensemble temporaire T1. Ensuite l’index sur la ville permet de former l’ensemble T2 avec les rid des employés de Québec. L’intersection de T1 et T2 fournit les rid des tuples qui constituent la réponse.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
132
Ce type de calcul est efficace avec les attributs indexés sélectifs c’est‐à‐dire dont le facteur de sélectivité est près de 1. Par contre, si le facteur de sélectivité est inférieur à 0.6, l’ensemble des rid constitué au moyen de l’index contiendra beaucoup trop de rid que le SGBD devra gérer éventuellement sur disque entraînant par le fait même une augmentation des lectures et écritures. Dans un tel cas, le SGBD pourrait préférer faire un balayage séquentiel de la table. Recherche avec un prédicat d’intervalle Une telle recherche peut être aussi effectuée avec le B*‐arbre. L’intervalle est formulé avec le prédicat suivant :
Select * From Employe Where Salaire = 25000.00 And salaire <= 50000.00;
En premier, il y a une recherche dans l’index avec la borne inférieure de l’intervalle. Au terme de cette opération les rid des tuples indexés avec 25000.00 sont regroupés dans un ensemble T1. Ensuite les entrées de ce nœud d’index sont parcourues séquentiellement et en suivant le pointeur horizontal jusqu’à l’atteinte du premier salaire plus grand que la borne supérieure. Durant cette opération les rid sont ajoutés à T1. Il ne reste qu’à lire les tuples au moyen de ces adresses. C’est cette opération qui alourdit souvent le calcul. En effet, les rid peuvent à la limite demander autant de lectures de blocs lorsque tous les tuples sont dispersés avec le ratio de un par bloc. 8.3 Fonction de Hashing Cette fonction H() permet de stocker et de retrouver une ou plusieurs valeurs par lʹentremise dʹune adresse qui est dérivée de la valeur même. Par exemple, si le nom dʹune société est lʹobjet dʹune indexation par la fonction H, la recherche de la société ʹViticole‐de‐Gersʹ débutera par un accès à un tuple dont lʹadresse est calculée par la fonction H(ʹViticole‐de‐Gersʹ). La valeur obtenue est utilisée comme rid de sorte quʹen allant à cette adresse on trouvera le tuple ou le début dʹune chaîne de tuples, parmi lesquels se trouvera celui recherché. La fonction H ne génère pas un ordre interne à l’image de l’ordre des valeurs externes, de sorte quʹil nʹy a plus de relation d’ordre parmi les valeurs calculées et celle des adresses de Haching. Ainsi, le stockage des noms de personnes ordonnés en ordre lexicographique sera effectué à des adresses qui ne respectent pas la même relation dʹordre. Finalement, la fonction de Hashing (H) permet la gestion dense des valeurs et la gestion des collisions par le chaînage dans une zone de débordement. Ainsi, si les matricules à stocker sont largement discontinus, la fonction H peut les transformer pour les stocker avec un plus petit nombre dʹadresses contiguës, facilitant ainsi le parcours séquentiel sur un plus petit nombre de pages. Toutefois, il y aura des collisions et celles‐ci devront être gérées par le logiciel.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
133
En mode recherche, la fonction de Hashing (H) est théoriquement très rapide. Elle exige dʹavoir une orthographe exacte du critère de recherche. Ainsi, une valeur en minuscules donnera un mauvais accès si la valeur utilisée initialement avec la fonction H était composée dʹun mélange de lettres minuscules et majuscules. Il en est de même pour les recherches approximatives, comme cʹest le cas avec des valeurs partielles ou voisines. Par exemple, les recherches des articles fabriqués respectivement par ʹDeschênesʹ ou ʹDeschainesʹ ne donneront pas les mêmes résultats, malgré le fait que le nom désigne la même personne, sauf si la fonction Soundex est utilisée lors d’une recherche dite approximative.
8.4 Densité de lʹindex et le placement regroupé des tuples Un index dont les pointeurs des feuilles conduisent directement à un tuple est dit dense, tandis que s’ils pointent sur une page de tuples (lot), il est qualifié de non dense.
Figure 8.4 Densité dʹun index pour accéder aux tuples Dans ce dernier cas, la recherche dʹune valeur particulière exigera un balayage séquentiel de la page. Cela suppose aussi que les tuples soient ordonnés sur l’attribut indexé et que valeurs identiques soient rangées dans la même page (clustered). Un index non dense pointe sur la page qui contient un tuple dont la valeur d’attribut a été trouvée dans l’index. Lʹavantage d’un tel index consiste en une utilisation plus optimale de lʹespace des pages dʹindex lorsque lʹattribut indexé a un facteur de sélectivité faible. En effet, les tuples partageant la même valeur sont rangés (éventuellement) dans la même page et une seule entrée est nécessaire dans lʹindex avec fournissant un seul rid pour faire référence à cette page. Par contre, certaines requêtes de comptage exigent un accès physique aux tuples pour obtenir une réponse. Ainsi, la requête pour calculer le total des salaires payés aux employés âgés de plus de 25 ans exige normalement un accès aux pages de données.
référence à une page (non dense)
page j+1
page j
référence à un tuple (dense)
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
134
Avec un index dense certaines requêtes peuvent être calculées sans avoir accès aux tuples. Par exemple, le comptage des ouvriers qui ont 25 ans est fait sans accéder aux pages, puisque chaque rid de lʹindex sur âge identifie indirectement un ouvrier qui satisfait le critère de recherche. Le comptage peut donc se faire au niveau de lʹindex sans accès aux pages de données.
Index regroupé Un index regroupant (clustering index) est un index dans lequel deux entrées voisines pointent sur des tuples qui sont aussi voisins dans lʹespace de stockage des tuples de la BD, c’est‐à‐dire dans la même page de données ou dans des pages voisines. Index avec regroupement des tuples dans la page
Figure 8.5 Ce type dʹindex est créé lors de lʹindexation d’une structure de table appelée cluster dans laquelle cohabitent des tuples de plusieurs tables qui sont présumés être lʹobjet de jointures fréquentes. La création dʹun cluster doit être suivie de celle dʹun index de regroupement pour que le premier puisse être exploité par le SGBD. Exemples : CREATE INDEX cluster_idx ON CLUSTER Lieu_travail; [Oracle]
‐‐ le cluster Lieu_travail est présumé déjà créé CREATE NONCLUSTERED INDEX article_idx on Ventes(article); [SYSBASE]
8.5 Index à clés transformées Lorsque les ajouts sont effectués dans un ordre croissant de la valeur dʹun attribut et que des suppressions régulières sont aussi faites dans un ordre quelconque, cela engendre un déséquilibre du B‐arbre, suivi du déclenchement du processus de réajustement. Pour éviter ce phénomène épisodique, certains SGBD, dont Oracle8, offrent la possibilité de transformer chaque valeur indexée en inversant systématiquement lʹordre de ses octets, ce qui entraîne une randomisation des entrées de lʹindex. En ce faisant, les espaces libérés par les entrées supprimées ont une plus grande chance dʹêtre réutilisés en cours des ajouts, avant même lʹamorce du travail de réorganisation. Bien entendu, cette répartition est faite au détriment de la recherche par plage de valeurs (index scan),
35 47
35 47
Index sur âge
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
135
puisque trois valeurs initialement ordonnées lors de leur insertion dans l’index ne le seront plus suite à lʹinversion des octets de ces mêmes valeurs.
8.6 Index inversé Un index inversé (inverted index) sur un attribut est un index construit avec les différentes valeurs de l’attribut dont chacune est associé à une liste de rids de tuples. Par exemple, pour sélectionner les soudeurs dans la table Ouvrier, le SGBD accède à un index inversé et dans une seule opération obtient la liste des rids des tuples représentant un employé‐soudeur. De même, pour obtenir les soudeurs et/ou les fraiseurs, il suffit de faire la concaténation des deux listes et ensuite de référer aux tuples identifiés par les adresses de tuple.
specialite soudeur r23 r34 r44 <‐‐liste de rids fraiseur r89 r56 sableur r123 r78 r22
Index inversé sur la spécialité (vision logique) Figure 8.6
Lʹattribut qui est inversé peut être organisé selon un B*‐arbre avec, dans les feuilles, les rid qui conduisent aux tuples utilisant cette valeur. Ce type dʹindex permet lʹindexation des attributs, peu importe leur sélectivité, et le calcul des requêtes comportant un prédicat complexe formé avec les opérateurs OU et ET logiques. Il est particulièrement utile dans les grandes bases ou les entrepôts de données comportant des millions de tuples.
8.7 Index bitmap Ce type dʹindex est particulièrement utile pour indexer les attributs à faible sélectivité pour lesquels le B*‐arbre n’a pas de performance exceptionnelle. Il sera particulièrement exploité avec un star schema de lʹentrepôt de données (data wharehouse). Par exemple, indexer une table des employés avec lʹattribut sexe dont le facteur de sélectivité pour une répartition uniforme est 0.5, nʹajoute pas à lʹefficacité de la recherche des employés féminins! En effet, il est fort probable que le calcul soit fait par un balayage séquentiel de la table. Une recherche classique dans un B*‐arbre aboutit à une feuille où le système trouve un rid permettant dʹaccéder, selon le cas, à la page de données ou au tuple. Dans le cas de lʹindex bitmap, la structure de B*‐arbre est toujours utilisée pour organiser les valeurs (de préférence peu nombreuses) de lʹattribut indexé. Cependant, les feuilles ne contiennent pas les rids, mais un vecteur de bits dans lequel chaque position représente un tuple de la table ouvrier identifié par son adresse. La longueur du vecteur de bits correspond au nombre de tuples dans lʹextension de la table. Par exemple, un index bitmap sur la spécialité de la table Ouvrier permet dʹavoir accès à des vecteurs de 4 bits,
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
136
soit un bit par spécialité du domaine. Pour chaque valeur de l’attribut specialite, le vecteur correspond à la colonne de la table ci‐dessous.
Ouvrier (nas*, nom, salaire, specialite, noUs) Lʹextension est la suivante : ROWID (rid) soudeur fraiseur sableur tourneur <‐index sur specialite 00055:000:0023 0 1 0 0 <‐liste des specialités 00234:020:8922 1 0 0 0 19000:328:6200 0 0 0 1 21088:120:1002 0 0 1 0
Figure 8.7 Index bitmap sur lʹattribut specialite Ce type dʹindex accélère sensiblement les requêtes avec comptage, opération souvent utilisée dans une requête de type OLAP (On Line Analysis Processing). Ainsi, pour connaître le nombre de soudeurs, cela nʹexige pas un accès aux tuples de la table Ouvrier. Seul lʹaccès à lʹindex bitmap de lʹattribut specialite suffit pour calculer la réponse. Le B*‐arbre sera réduit à sa plus simple expression, et dans le cas de cet exemple, à une seule page de données. CREATE INDEX BITMAP ON Ouvrier(spec)
TABLESPACE spec_espace STORAGE (INITIAL 2M PCTINCREASE 10); Un autre index bitmap peut être aussi créé sur lʹattribut salaire. Ce deuxième index permettra de calculer rapidement la réponse à une question du genre suivant : Quels sont les employés soudeurs qui gagnent 30 000.00$?
ROWID 20000 30000 40000 <‐‐bitmap sur salaire 00055:000:0023 1 0 0 00234:020:8922 0 1 0 19000:328:6200 0 0 0 21088:120:1002 0 0 1
En effet, avec un index sur l’attribut salaire, il suffit de faire lʹintersection des deux colonnes, salaire de 30 000.00 avec celle de soudeur. Les tuples du résultat sont accessibles via la clé de la table Ouvrier.
ROWID soudeur 30000 réponse 00055:000:0023 0 0 0 00234:020:8922 1 1 <‐‐‐‐1 19000:328:6200 0 0 0 21088:120:1002 0 0 0
Calcul dʹune réponse à une requête conjonctive Figure 8.8
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
137
Lʹouvrier décrit par le tuple 00234:020:8922 est un soudeur gagnant 30 000 $ par année. Cʹest le seul tuple de la réponse.
8.8 Index partitionné Avec le SGBD Oracle 8, les index peuvent être aussi partitionnés comme cʹest le cas avec les tables. En ce faisant, chaque partition peut avoir ses caractéristiques de stockage, notamment un fichier exclusif utilisé pour le stockage des seules données de la partition. L’utilité de ce mécanisme reste cependant à démontrer.
Création des index simple et composé Un index (B*‐arbre) est avant tout un mécanisme qui privilégie un chemin d’accès particulier et rapide pour accéder aux tuples. Si cette valeur est unique, i.e. associée à un seul tuple, cela permet de vérifier l’unicité de valeur dans une colonne dʹattributs sans faire un accès physique aux tuples de la table. Un message d’erreur est émis si une deuxième valeur identique est ajoutée. La spécification d’une clé primaire active dans le schéma d’une table génère automatiquement un index interne géré par le système. Cela n’est pas le cas avec la spécification de la clé étrangère qui peut être indexé par la création subséquente d’un index. Un index peut donc être créé, au besoin par le DBA, afin dʹaccélérer lʹaccès aux tuples.
CREATE [unique] index <nom_index> on <nom_relation> (col1, col2, ...) La commande de création d’un index peut avoir des arguments concernant le stockage : tablespace, PCTused, PCTfree, initrans, etc. L’index est lui‐même une table qui peut être consultée comme n’importe quelle autre table de base de la BD. Toutefois, en cours d’exploitation, la table d’index est parcourue avec une structure de B‐arbre ou une de ses variantes.
CREATE INDEX nom de lʹINDEX
schéma.
ON table (‐‐‐‐‐‐‐‐‐‐‐‐‐ colonne ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐schéma.
ASC DESC
CLUSTER nom_cluster schéma.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
138
Figure 8.9
Caractéristiques des index Un index est stocké sous la forme de tuples dans une table comportant une colonne pour lʹentrée de lʹindex et une autre pour le rid du tuple indexé. Si un index est unique, chaque valeur dʹindex est appariée à une seule valeur de rid, donc à un seul tuple de données. Les propriétés d’un index sont parfois explicitement spécifiées ou sous‐tendues par la procédure de création des index du SGBD. a) L’index est créé automatiquement lorsqu’une clé primaire est spécifiée dans un schéma. Il est alors du type système en ce sens quʹil ne peut pas être supprimé par le DBA autrement que par redéfinition du schéma. L’index de la clé primaire peut cependant être désactivé temporairement par le DBA au moyen de la clause ALTER TABLE. Lʹindex peut être supprimé avant une mise à jour importante et être refait par la suite en une seule opération (facilite le chargement en lot). b) Les tuples d’index peuvent cohabiter avec les tuples de données dans le même espace de données, mais dans des segments (donc des pages) différents; ils peuvent aussi être avec profit regroupés dans un tablespace spécialisé réservé exclusivement au stockage des index. c) Les entrées sont mises automatiquement à jour au cours de l’exploitation de la base, ce qui peut entraîner un ralentissement lors dʹune mise à jour intensive. Ce ralentissement vaut aussi pour une entrée composée de plusieurs attributs de la même table. d) Les index sont utilisés implicitement par l’optimiseur de requêtes. Il est possible de bloquer l’usage d’un index dans la phase d’optimisation par la formulation du prédicat ou en fournissant directement une directive à lʹoptimiseur (hint). La recherche des données dans une table est accélérée par un index, mais l’ajout est ralenti par la mise à jour sous‐jacente de l’index en question. Le parcours complet d’une table (full table scan) n’est pas nécessairement accéléré par un index. Ce parcours exhaustif est une opération qui demeure lente et que lʹon doit de préférence éviter dans les applications. e) Les index utilisent lʹespace de la ZMP (Zone de Mémoire Partagée gérée par le moteur SGBD) qui autrement serait utilisé par les pages de données; un index ne devrait être utilisé que pour les tables dont la cardinalité est de lʹordre de 1 000 tuples et plus.
extension TABLESPACE nom_tablespace NOSORT
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
139
f) Un index peut‐être dense ou non selon que les entrées pointent sur un tuple ou sur un lot de tuples (i.e. une page). En outre, il peut induire un placement (clustering) des tuples associé à lʹordre des entrées de lʹindex.
Usage des index par lʹoptimiseur En règle générale, les index sont utilisés pour le calcul d’une réponse, car ils sont en principe capables d’en accélérer l’obtention. Il est cependant possible dʹen bloquer lʹexploitation par une formulation syntaxique particulière des clauses SQL ou par le recours aux ʺhintsʺ de l’optimiseur du SGBD. Lʹoptimisation est bloquée dans le cas des requêtes suivantes qui comportent une des caractéristiques suivantes : a) Un attribut indexé est modifié par une fonction ou une expression arithmétique; b) Un attribut indexé est modifié par un NOT ou un != ou est testé pour un NOT NULL; c) Lʹabsence de la clause WHERE ; d) Un attribut indexé associé à une recherche de caractères par lʹopérateur de chaîne % placé en début de chaîne. Les mécanismes détaillés mis en œuvre dans l’optimiseur seront étudiés dans un prochain chapitre.
Exemples de création dʹindex : a) Créer un index avec le nom de l’article en inventaire (clé candidate) :
CREATE UNIQUE INDEX idx_article_Inventaire on Inventaire (article);
Le nom de lʹindex est choisi par le DBA et peut être normalisé en le préfixant par exemple par idx_, suivi du nom de lʹattribut indexé et finalement terminé par le nom de la table. b) Créer un index unique et composé dont les entrées sont formées avec les attributs (description et proprio) de la relation Pieces. Cette paire d’attributs étant une clé candidate, un index unique peut être créé.
Pieces (nop*, description, km, ville_p, proprio) Create UNIQUE index idx_desc_propr_Pieces on Pieces (description, proprio);
Cet index composé peut accélérer de façon significative le calcul de la réponse aux requêtes qui utilisent les deux attributs dans un prédicat conjonctif de sélection. La recherche est réalisée par la consultation de lʹindex composé pour obtenir l’ensemble des rids de la réponse.
SELECT * FROM Pieces WHERE description =‘porte’ And proprio ='f1';
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
140
Si la requête est exécutée fréquemment, le gain de performance sera alors d’autant plus important. Lorsqu’une requête conjonctive ne concerne qu’une seule table, l’existence d’un index sur lʹun des attributs permet normalement d’accélérer le calcul de la réponse. L’optimisation en tient compte pour établir le plan d’exécution de la requête. En revanche, une requête disjonctive ne gagne pas nécessairement en temps de calcul en utilisant un seul index. En effet, il faudra faire un balayage complet de la même table pour vérifier la deuxième partie du prédicat disjonctif. Autant alors éviter l’appel de l’index et vérifier le prédicat lors d’un seul balayage séquentiel. Si tous les attributs du prédicat sont indexés, alors les index peuvent être utiles. Le balayage d’une table (full table scan) est une opération longue qu’il faut autant que possible éviter en raison du transfert d’un nombre important de pages entre les disques et la zone de mémoire partagée (ZMP ou SGA‐Oracle). Avec une requête concernant plusieurs tables, l’absence de tout index pour les attributs non primaires utilisés dans le prédicat sous‐tend un balayage obligatoire complet des deux tables. Avec un seul index, l’optimiseur choisira entre plusieurs plans d’exécution en estimant lequel sera le plus rapide pour calculer la réponse. C’est la base de la stratégie qui repose sur le coût de calcul.
Suppression d’un index L’index est un objet de la BD comme lʹest une table de données. Il peut donc être supprimé par la commande DROP INDEX : Drop index <nom de l’index> L’index est alors supprimé et sa définition est enlevée du dictionnaire de données. Exemple : Supprimer l’index nommé idx_piece_inventaire:
Drop index idx_piece_inventaire L’espace utilisé par l’index est libéré sans entraîner automatiquement une réorganisation de l’espace disque impliqué dans la suppression des pages de l’index. Cet espace sera cependant éventuellement réutilisé par le SGBD.
8.9 Guide dʹutilisation des index en cours dʹexploitation de la BD Plusieurs règles de pratique sont proposées par les sociétés qui commercialisent les systèmes haut de gamme sur lʹusage des index dans l’exploitation de la BD. Il faut tenir compte des facteurs suivants : a) Les index utiles sont ceux nécessaires à l’exploitation courante des données (évitez les traitements hypothétiques ou prévus mais non confirmés par des applications en production) en fonction du profil d’utilisation des données. Il est utile dʹavoir recours à un fichier de commandes pour créer les index, afin de spécifier plus facilement les paramètres concernant l’espace de données (tablespace) nécessaire à la création du segment d’index. Il devient alors tout aussi facile de les supprimer que de les créer.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
141
b) Il faut supprimer les index lorsquʹil sʹagit de traiter un flux important de transactions du type mise à jour sur une table. Les index sont d’abord supprimés, puis recréés au besoin après les mises à jour. Cette opération est indiquée lors des traitements en lot ou du chargement d’une table avec un fichier de données validées. c) Durant une session intensive de création de rapports et de calcul de requêtes, le DBA verra à créer les index nécessaires en tenant compte des tables qui seront utilisées au cours de la préparation du rapport et du calcul des réponses. d) L’index de clé (en version système) est aussi utilisé pour assurer l’unicité de la clé monoattribut, i.e. pour renforcer la contrainte de clé spécifiée dans le modèle relationnel. Au besoin, la clé primaire peut être désactivée par un DISABLE pour bloquer la mise à jour de l’index système sous‐jacent, mais l’index sur la clé primaire n’est pas détruit. La réactivation de la clé primaire lance cependant la mise à jour rétroactive de lʹindex système correspondant. e) Une accélération de la jointure est possible par la création des index : les attributs de jointure qui sont indexés permettent un accès plus direct aux tuples vérifiant la condition de jointure. Exemple :
SELECT Empl.nom, Y.dnoDep,Y.nomDep FROM Empl E, Dep Y WHERE Empl.noDep = Y.noDep ;
Dans ce cas, l’index sur la clé primaire est automatiquement utilisé en conjonction avec celui créé par le DBA sur la clé étrangère. Index simple et composé de Oracle Deux sortes dʹindex sont disponibles avec le SGBD Oracle : simple et composé. Dans les deux cas, ils sont de type dense, i.e. que les entrées pointent sur des tuples et non sur des pages. Lʹindex simple est construit avec les valeurs d’un attribut d’une seule table. On doit éviter de créer un tel index pour les attributs peu sélectifs dont le facteur de sélection est supérieur à .35, par exemple le sexe d’une personne ou le nom. Il convient dʹutiliser l’index simple déclaré UNIQUE pour valider l’unicité des valeurs d’un attribut, car la vérification est alors effectuée sans faire un accès aux tuples de la table.
CREATE UNIQUE INDEX idx_assig_empl ON Assignations(noEmpl); L’index composé est défini avec plusieurs attributs dʹune même table. Il y a alors accélération du calcul pour les requêtes dont le prédicat se réfère à tous les attributs de l’index composé.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
142
CREATE INDEX idx_no_poste_no_mpl_assignations ON Assignations (noPoste, noEmpl);
Lʹindex composé est construit avec les entrées formées de plusieurs attributs ordonnés dʹune même relation. Une entrée peut avoir jusquʹà 16 attributs dont lʹordre est figé dès la création de lʹindex. Si le prédicat d’une clause SQL fait référence au premier attribut de l’index composé, celui‐ci sera employé pour accélérer le calcul. Si seulement le deuxième est référencé, l’index ne peut pas être utilisé. Dans ce cas, s’il existe un index simple sur un des attributs de l’entrée de l’index composé, il sera mis à contribution pour le calcul de la réponse. Voici un exemple avec création d’un index supplémentaire monoattribut pour la relation Assignations, en sus d’un index composé existant sur les attributs (no‐poste, no‐empl). La requête suivante utilise une jointure qui fait seulement référence au deuxième attribut de l’index composé, soit le noEmpl.
Select E.noEmpl, E.nom From Empl as E, Assignations as A Where E.noEmpl = A.noEmpl ;
Le calcul de la réponse en lʹabsence dʹun index simple sur la clé étrangère ne tire pas profit de l’index composé qui inclut la clé étrangère. Il y aura un balayage complet et séquentiel de la table Assignations, puisque cette jointure fait référence au deuxième attribut de lʹindex composé. Une meilleure performance sera obtenue par la création avec l’attribut noEmpl d’un index simple sur la clé étrangère.
CREATE INDEX idx_noEmpl_Assignations ON Assignations(noEmpl) ;
Ainsi, le calcul de la réponse précédente est accéléré, car l’index simple idx_noEmpl_Assignations est disponible et l’optimiseur en tirera profit dans le calcul de la jointure.
Optimiseur et index composé Les index sont utilisés par l’optimiseur, notamment si certaines informations sont présentes dans le prédicat (WHERE) des requêtes. Nous allons faire un bref survol de quelques choix d’optimisation que ce dernier peut faire en cours de traitement : a) Tous les attributs d’un index composé doivent être présents dans la clause WHERE, peu importe leur ordre dans la clause, pour que l’optimiseur en tire profit. b) Si le premier attribut de l’index composé idx_noPoste_noEmpl_Assignations est présent dans le WHERE, l’index composé est utilisé. S’il est absent, l’index composé devient inutile.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
143
segment 2Page d’index
Page d’index
Page de données
Page de données
segment 1
SELECT * FROM Assignations A, Empl E WHERE A.noEmpl = E.noEmpl and article =ʹa1ʹ;
L’absence de l’attribut noPoste rend l’index composé inutilisable. c) La négation dans une condition bloque l’usage des index composés par lʹoptimiseur puisque les valeurs hors domaine ne sont pas indexées.
SELECT * FROM Ventes WHERE nom != ʹChristineʹ and Ventes.article = ʹa1ʹ;
Quand faut‐il créer un index composé ? Lorsque certains attributs sont fréquemment utilisés ensemble dans l’exploitation d’une BD, il peut être alors avantageux de définir quelques index composés. La décision dépendra du suivi de l’exploitation fait par le DBA et des statistiques de performance mesurées par le noyau du SGBD.
8.10 Indexation avec Oracle Les relations créées ne sont pas indexées automatiquement, sauf pour la clé primaire (simple ou composée) qui est spécifiée dans le schéma d’une table par la clause PRIMARY KEY. Cette clause impose la création de l’index sur la clé primaire, lequel index est maintenu par le système SGBD indépendamment du DBA. La clause PRIMARY KEY spécifie une contrainte de clé vérifiée par le SGBD au moyen dʹun mécanisme approprié qui peut être un index, un trigger ou une fonction de hasching. La clé étrangère spécifiée dans un schéma de relation ne génère pas automatiquement un index sur celle‐ci. La contrainte référentielle spécifiée dans le schéma identifie simplement les attributs qui sont impliqués dans une contrainte référentielle et rien de plus .
Index et données dans divers segments Figure 8.10
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
144
Lʹindex sur la clé étrangère doit être créé explicitement par la commande CREATE INDEX. Les pages des tuples d’une même table forment un segment, tandis que celles d’un index sur la même table forment un autre segment distinct quant à sa structure. La lecture d’une page d’index permet de lire une page de données de tuples grâce aux rids fournis par le B‐arbre. Lʹespace dʹune page dʹindex est géré à peu près comme celui des données, soit avec le paramètre PCTFREE. Par exemple, avec un PCTFREE de 20 %, de nouvelles entrées dʹindex pourront être ajoutées dans une page (via de nouvelles cellules) et cela, tant quʹil y a 20% dʹespace libre dans celle‐ci. Lorsque l’occupation passe la barre du PCTFREE, la page perd son statut de page insérable. Il est alors impossible dʹajouter de nouvelles entrées tant que lʹespace utilisé nʹest pas revenu à un niveau inférieur à la valeur du PCTUSED. À ce moment, la page devient à nouveau insérable et est ajoutée à la liste des pages libres (free list). La libération dʹespace provient, soit des modifications soit des suppressions des entrées de lʹindex. Toutefois, la mise à jour dʹun index non unique par lʹajout de ROWID comme une entrée sera possible grâce à lʹespace disponible (20 %) pour le débordement. Puisquʹun index est lʹobjet dʹune grande activité en cours dʹexploitation, le PCTFREE doit être assez élevé pour un index non unique afin de permettre lʹinsertion des adresses de tuple dans les feuilles et cela, pour une entrée existante.
Occupation d’une page d’index Figure 8.11
Enfin, il y a avantage à privilégier la consultation des pages dʹindex par des lectures logiques (logical read), cʹest‐à‐dire des lectures dans les pages déjà présentes dans le SGA, de manière à éviter les lectures physiques (physical read), cʹest‐à‐dire celles toujours lentes des pages physiques sur un disque. Les lectures logiques seront probablement plus fréquentes si les adresses de tuple pour une même entrée sont dans la même page. Les pages des index sont rangées dans la ZMP et sont lʹobjet de verrouillage. Ainsi, un tuple de données, qui sera trouvé par un index et dont la valeur de lʹattribut indexé sera modifiée, forcera le système à verrouiller lʹentrée de lʹindex (le tuple correspondant à son entrée) en plus de poser un verrou RX sur la table à modifier. Le double verrouillage
20% pour le PCTFREE 70% pour le PCTUSED
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
145
peut ralentir la concurrence de lʹaccès aux données, notamment lorsque la localité de référence aux données est relativement grande lors de l’exploitation de la base. La mise à jour des index suit toute modification et toute insertion de tuples dans les relations et est assurée automatiquement par le SGBD. Plus les attributs dʹune table sont indexés, plus le temps global de mise à jour est important. En effet, le SGBD doit faire la modification dans le tuple, mais aussi dans tous les index faisant référence aux tuples modifiés. Un index peut être simple ou composé selon que son entrée comprend un ou plusieurs attributs.
CREATE INDEX [schéma.]<nom_idx> ON [schéma.]<table> (attribut [asc|desc], ...) INITRANS <entier> MAXTRANS <entier> TABLESPACE <tablespace> STORAGE <initial , next , MINEXTENTS ,... > PCTFREE <entier> [NOSORT] ;
La suppression dʹun index est réalisée par la commande DROP INDEX.
DROP INDEX [schéma.]<nom_index> ; Exemple : CREATE INDEX idx_proprio_pieces on Pieces (proprio) ASC
INITRANS 3 MAXTRANS 6 Tablespace espace1 storage 5M initial 10K next 10K minextents 2 maxextents 20 PCTINCREASE 20 PCTUSED 40 PCTFREE 40;
Lors de la création de lʹindex idx_proprio_pieces, le segment est créé avec deux extensions de 10K octets. Au besoin, une 3e extension est créée de 10Ko, et par la suite les extensions sont créées avec une augmentation de 20%.
Quelques facteurs de performance avec le SGBD Oracle Le calcul des réponses est accéléré avec les index et les groupements des données (clusters) et sous‐tend une gestion performante de la mémoire physique14. Le noyau d’Oracle aura accès plus rapidement aux tuples avec un mécanisme d’indexation. De plus, un accès plus direct aux tuples favorisera la réduction de l’activité de I/O sur le canal du disque. En revanche, les index ralentissent la mise à jour parce que toute modification de la valeur d’un attribut dans une relation de base doit être, si l’attribut est indexé, répercutée dans l’index. La séparation des pages d’index et des pages de données dans leurs tablespaces respectifs favorisera la performance en minimisant les échanges de pages, sous réserve d’une ZMP suffisamment grande pour contenir à la fois les pages de données et celles des index. La présence de plusieurs disques avec des
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
146
contrôleurs différents est aussi un facteur de performance pour autant que les index et les données des tables soient rangées sur des disques différents.
8.11 Indexation de la clé primaire et de la clé étrangère Une clé primaire simple ou composée, spécifiée dans le schéma dʹune relation, est implémentée par la création automatique dʹun index système qui est rendu immédiatement, actif, sauf si la clause DISABLE est spécifiée. Les valeurs nulles sont exclues implicitement de la clé et n’apparaissent donc pas dans un tel index système. Toutefois, la clé primaire pouvant être désactivée, les attributs qui la composent doivent être déclarés NOT NULL, car autrement lʹinsertion de tuples avec des attributs primaires nuls serait tolérée jusquʹà la prochaine réactivation de lʹindex de la clé primaire. C’est à ce moment que la contrainte de clé sera vérifiée. Normalement, l’index sur la clé primaire est actif à la création de la base de données et le demeure au cours de son exploitation transactionnelle. Voici un exemple pour illustrer la désactivation de la clé primaire de la table Empl.
ALTER TABLE Empl primary key DISABLE;
Ajout de tuples dans une table enfant En l’absence d’un index sur la clé étrangère, la contrainte référentielle est vérifiée par un trigger ou une procédure interne qui sous‐tend le balayage complet des données de la table.
Figure 8.11a Dans ce cas, pour éviter un verrou bloquant sur la table et favoriser la performance, il suffira de créer un index avec la clé étrangère, index qui, ajouté à celui de la clé principale de la table parent, permettra de vérifier la présence dʹune valeur de clé primaire (ajout dans la table enfant) ou de clé étrangère (suppression dans la table
Suite à l’ajout dans la table enfant, un trigger balaie la table parent pour déterminer si l’usine référencée dans le nouveau tuple est bien présente dans la table Usine. Pour faire ceci, tout ajout est temporairement bloqué dans la table Usine.
Ajout d’un atelier
Table Atelier
Les tuples de Usine sont verrouillés pour assurer la consistance de lecture
Table Usine
noUsine Déclenchement du trigger
T2 : Delete u1 ??
T1 : (a2, …. u1)
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
147
parent) sans verrouiller ni accéder aux tuples de la table parent. Une contrainte de clé étrangère est automatiquement renforcée en lʹabsence de la clause DISABLE pour la clé primaire de la table parent. Cependant, cette contrainte référentielle ne se traduit pas nécessairement par la création automatique dʹun index avec la clé étrangère.
Figure 8.12 Elle est implémentée par un mécanisme interne qui fait référence à la clé primaire de la table parent ou par un trigger qui sous‐tend le verrouillage complet de la table parent suivi de son balayage en entier (full table scan). Au début de la transaction T1 qui ajoute un tuple dans la table Atelier, si lʹusine référencée par ce nouveau tuple est absente, le trigger enclenché doit aboutir à un échec. La présence d’un index sur la table parent, permet au système de vérifier la présence de l’usine référencée et de placer un verrou partagé (Shared lock) sur l’entrée de l’index de la clé primaire, qui bloque de facto la suppression de ce tuple parent par toute autre transaction. Le verrou est maintenu jusqu’à la fin de la transaction T1. Lʹeffet net sera une plus grande concurrence aux données au profit des autres transactions. De même, la suppression d’un tuple dans la table parent par T1 exige la vérification de la référence éventuelle de ce tuple par ceux de la table enfant. Pour éviter de bloquer la table enfant aux autres transactions, un index créé avec la clé étrangère permet de vérifier rapidement une référence à la clé primaire, de poser un verrou temporaire sur l’entrée correspondante dans l’index de la clé étrangère, bloquant momentanément toute suppression aux tuples enfants et cela, par le moyen d’un verrou partagé appliqué sur l’entrée de l’index pour la durée de T1. Ainsi, toute autre transaction T2 est interdite d’ajout d’un tuple dont la clé étrangère aurait la valeur de l’entrée de l’index verrouillée par T1.
Pertinence de lʹindexation Il nʹest pas toujours approprié dʹindexer les attributs dʹune relation sans tenir compte de lʹexploitation réelle des tables. Ce nʹest que par un suivi approprié et rigoureux de lʹexploitation des données et un bon travail de coordination avec les concepteurs, quʹun DBA peut mettre au point et adapter les accès à la BD par des index qui sont essentiellement un mécanisme dʹaccès rapide aux tuples. Lʹindexation doit être revue périodiquement pour tenir compte des changements dans le profil dʹexploitation des données. Un index fort utilisé pendant une période peut devenir nuisible par la suite si le taux dʹaccès à la table indexée chute radicalement.
noUsine, noUsineIdx
noUsine Ajout d’un atelier
Verrou sur l’entrée
25
25Table enfant Atelier
Table parent Usine
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
148
Observations pratiques pour lʹindexation 1. La spécification de la clé primaire engendre la création d’un index système unique et actif, sauf sʹil est désactivé par le mot clé DISABLE. 2. Si la clé est utilisée fréquemment dans une jointure impliquant la clé étrangère, une meilleure performance pourra être obtenue avec un index actif défini sur la clé étrangère. La contrainte référentielle doit cependant rester active. Par contre, un tel index peut ralentir les mises à jour sur la clé étrangère (DELETE CASCADE) parce que celles‐ci doivent être répercutées sur l’index. Il faut donc éviter dans ce cas de figure lʹindexation de la clé étrangère. 3. Si la cardinalité dʹune relation est supérieure à 1000 tuples et que cette table est utilisée fréquemment dans une jointure, la création dʹun index avec les attributs de jointures accélère généralement le calcul de la réponse. 4. Un index composé est utile si les attributs indexés sont souvent utilisés comme une suite conjonctive de valeurs. Un tel index composé est moins utile avec les requêtes disjonctives. 5. Un index nʹest pas nécessaire pour des exploitations séquentielles du type ʺfull scanʺ comme la préparation de listes exhaustives des tables qui utilisent toutes les valeurs des attributs dʹune relation. Un index est guère utile avec un attribut dont la sélectivité est inférieure à 0,35. 7. Les attributs de la clause WHERE seront l’objet d’une indexation. Avec une condition dʹégalité, le calcul est accéléré avec une fonction de Hashing, tandis qu’avec une condition impliquant une plage de valeurs, le calcul est plus rapide avec un index en B‐arbre. 8. Généralement, un index sur une relation peut être de type cluster pour accélérer les requêtes de jointure assorties d’un intervalle (plage), sauf si lʹévaluation n’est faite quʹau niveau de lʹindex. Dans ce dernier cas, le clustering nʹest pas nécessaire. Durant la phase dʹanalyse, le DBA doit en premier répertorier les applications et les attributs utilisés par celles‐ci pour accéder aux données. Si certains attributs sont fréquemment utilisés par les applications, ce sont des attributs candidats à lʹindexation. Il faut aussi considérer les applications déjà planifiées pour s’attarder particulièrement à celles qui font de très nombreuses lectures. En contrepartie, si les applications ont aussi une charge de mise à jour très importante, il faut prendre en compte le fait que les index sont plutôt lourds à mettre à jour. Quelle que soit la décision, le DBA doit surveiller de près l’évolution des performances du système et prendre les mesures qui sʹimposeront pour les améliorer, notamment la création ou la suppression d’index.
8.12 Placement des tuples par le cluster Le clustering ou le regroupement des tuples dʹune ou de plusieurs tables est un mécanisme de placement des tuples partageant une même valeur pour les attributs définis comme attributs du regroupement. Il y a donc création dʹune nouvelle structure de page caractérisée par deux en‐têtes. Normalement les tuples de deux tables différentes sont rangés dans des pages différentes respectivement allouées aux deux tables. Il en est autrement avec le cluster. Il sʹagit pour le système SGBD de placer
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
149
physiquement les tuples qui interviennent dans le calcul des jointures le plus près possible les uns des autres, de préférence dans la même page. Il y a deux types de placement indexé : le cluster indexé et le cluster avec Hashing. Le premier implique deux ou plusieurs tables, tandis que le deuxième sous‐tend une même table dont les tuples sont placés dans des pages dont lʹadresse est dérivée de la valeur dʹun attribut. Ces clusters sont spécifiés et créés au besoin par le DBA. Prenons lʹexemple de la BD de Dotation des ressources humaines, BD3 dans laquelle le taux horaire est multiplié par 100.
postes : noPoste* description j12 gérant j34 peintre j98 null j21 mécano1 j55 contrôleur j75 mecano2
empl : noEmpl* nom prenom tauxH (x100)
P346 Audy rudy 1300 P456 Gagnon andré 1715 P345 Audy michel 1250 P651 Tremblay robert 1250 P762 Dussault marc 1325 P337 Bérube null 1250 P450 Dion fabrice 1325 P535 Joncas sylvie 1450
assignations : noPoste* noEmpl* debut quart j12 P456 21‐jan‐1992 soir j34 P651 22‐mar‐1991 jour j21 P762 23‐dec‐1990 nuit j34 P345 24‐sep‐1994 jour j75 P346 12‐oct‐1997 null j55 P456 25‐jan‐1994 jour j34 P337 21‐jan‐1992 nuit j75 P450 27‐jan‐1994 null
Pour connaître la description du poste occupé par un ou plusieurs employés, il faut faire une jointure entre les tables Assignations et Postes, en utilisant lʹattribut noPoste. Pour accélérer le calcul, le système peut placer, de préférence, les tuples de ces deux tables dans la même page en se basant sur la valeur de noPoste. Ainsi, les tuples de ces deux tuples qui concernent les employés occupant la fonction ‘j75’ seront placés dans la même page pour en faciliter les accès et les comparaisons. Les attributs spécifiés avec le critère de placement sont appelés la clé du regroupement (ou du clustering). Dans notre exemple, le noPoste est lʹattribut de clustering.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
150
Le placement par clustering ne se limite pas au seul calcul dʹune jointure. Il peut être utilisé pour gérer le placement des tuples de diverses tables dʹune BD que les applications rapprochent par leur traitement. Par exemple, si une application en L3G doit faire la liste des transporteurs dans la colonne gauche dʹun rapport et ensuite celle des pièces dans la colonne droite, elle exploite simultanément les deux tables Transporteurs et Pièces sans faire de jointure. Dans ce cas, on aura avantage à placer les tuples des deux tables dans les mêmes pages en déclarant les deux clés, primaire et étrangère, de ces tables comme un attribut de clustering (cʹest‐à‐dire placement regroupé). Les tuples ayant la même valeur pour noPoste sont rangés dans les mêmes pages ce qui accélère le calcul de la jointure.
8.12.1 Placement par Hashing (Hash clustering) Dans ce cas, le placement concerne les tuples dʹune ou de plusieurs tables dont le rangement dans la BD est calculé par une fonction de Hashing, soit la fonction H fournie par le SGBD, soit selon la valeur fournie explicitement par un attribut. Lʹespace des clés du cluster est défini comme la plage des valeurs possibles définie par les bornes inférieure et supérieure. En pratique, toutes les valeurs de la clé du cluster ne sont par utilisées par la clé. Lʹintervalle de ces valeurs est donc condensé en premier à une plage plus petite au moyen d’une fonction T appelée par l’application et dont les valeurs sont par la suite traitées par un Hashing pour donner les adresses de pages. Par exemple, le taux horaire (x100) payé à un employé varie de 1250 à 1450 dans la table et seulement 5 % de ces valeurs sont réellement utilisés, soit environ 50 numéros.
(j34, peintre) (j34, P651, 22‐mars‐1991, jour) (j34, P337, 21‐jan‐1992, nuit) (j34, P345, 24‐sep‐1994, jour)
(j21, mécano1) (j21, P762, 23‐dec‐1990, nuit)
Page de cluster (structure logique) Page de cluster(structure logique)
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
151
Figure 8.14 La fonction de transformation T consistera donc à calculer pour chaque taux une valeur entre 0 et 50 par une transformation appropriée souvent basée sur un modulo. En supposant la taille dʹun tuple à 1Ko et que 200 valeurs sont théoriquement possibles, il faudra prévoir un maximum de 4 tuples ayant les mêmes valeurs de Hashing regroupés dans une même page de 4 Ko. Lʹespace à prévoir pour le stockage des tuples et les collisions sera donc de 4 *1Ko/t = 4Ko, soit la taille d’une page. Pour créer un cluster de Hashing, il faut spécifier les attributs de clustering et le nombre de valeurs de H possibles, ainsi quʹun estimé du nombre de tuples ayant la même valeur pour la fonction H. Dans lʹexemple ci‐dessus, il peut y avoir jusquʹà cinquante valeurs de Hashing différentes (mod50). On estime que 4 tuples, qui ont la même valeur et dont la taille est près de 1Ko, nécessitent un espace de rangement de 4K (SIZE). Il y aura donc jusquʹà 4 tuples par page (de 4K). Le SGBD fournit par défaut sa fonction de Hashing . CREATE CLUSTER TAUX_CLUST (c_taux number(4))
SIZE 4K ‐‐espace du cluster pour ranger les tuples de même H ; taille max = 1 page) HASHKEYS 50; ‐‐ nombre possible de clés de Hashing
Après la création du cluster, il faut créer la ou les tables (pour en ranger la définition dans le dictionnaire) en spécifiant que les tuples seront rangés dans le cluster TAUX_CLUST, qui est en fait un espace de table un peu spécial quant à la stratégie de rangement des tuples de la ou des tables associées au cluster.
CREATE TABLE Empl ( noEmpl char(4) not null, nom varchar2(30)) prenom varchar2(30), tauxH number(4), CLUSTER TAUX_CLUST(taux);
Le lien entre lʹattribut de cluster c_taux et l’attribut de la table tauxH est fait lors de la création de la table et de son association au cluster. Lorsquʹune requête fait référence à la table rangée dans un cluster, le SGBD utilise la valeur de lʹattribut de cluster (fournie par
Valeurs de l’attribut ( x 100) 1300 : mod(50) = 0 1752: mod(50) = 2 1249 : mod(50) = 49 1325: mod(50) = 25 1431 : mod(50) = 31
Page 0
Page 1
Page 49
Page 2
T
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
152
lʹattribut de la table correspondant) pour calculer une clé de Hashing et lire la page qui contient le ou les tuples demandés.
SELECT * FROM Empl WHERE tauxH = 1250 ;
Lʹattribut taux est inscrit dans le dictionnaire avec un indicateur signalant quʹil est utilisé dans la spécification dʹun cluster. Le SGBD effectue le calcul de la clé de Hashing à partir de la valeur 1250 pour ainsi connaître directement lʹadresse de la page à transférer dans la ZMP : MOD(1250, 50) = 0. Lʹaccès est plus rapide par rapport à celui réalisé par un index qui dans ce cas peut nécessiter le transfert de plusieurs pages avant dʹidentifier le rid du tuple.
Cluster avec Hashing explicite Il est aussi possible de fournir la fonction de Hashing (H) en remplacement de celle fournie par le SGBD. Par exemple, la fonction pour générer le no‐poste peut être définie comme un nombre généré par la séquence poste‐ID‐seq : CREATE CLUSTER POSTEID_CLUST(poste_ID_SEQ NUMBER(3))
SIZE 4K ‐‐espace pour ranger les tuples de même valeur H HASH IS (no_poste) mod(25) /* no généré par la séquence*/ HASHKEYS 31; ‐‐ arrondi au premier nombre premier suivant 25
Dans ce cas, le placement des tuples est dirigé par le modulo 25 de lʹattribut no_poste dont la valeur, dans cet exemple, est générée automatiquement par une séquence.
8.12.2 Cluster indexé Un cluster indexé regroupe dans la même page les tuples ayant la même valeur pour lʹattribut de cluster. Un tel cluster doit être indexé pour permettre dʹexploiter la cohabitation des tuples dans la même page. Dans ce type de placement, il faut créer un objet cluster et ensuite créer obligatoirement un index. Il est aussi possible dʹutiliser un cluster indexé avec une seule table pour regrouper les tuples ayant une même valeur dʹattribut. Considérons un modèle pour représenter les comptes bancaires et les clients. CREATE CLUSTER clust_comptes_bancaires (c_no_compte number(3)); CREATE INDEX ind_clust_comptes_bancaires ON CLUSTER clust_comptes_bancaires; CREATE TABLE Comptes (
noCompte number (3) primary key, ville varchar2(35)) CLUSTER clust_comptes_bancaires (no_compte);
CREATE TABLE Client ( nas char(8) primary key,
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
153
nom varchar2(45), . . . noCompteClient number (3) Foreign key References Comptes(no_compte) ) CLUSTER clust_comptes_bancaires (noCompte);
Une page dʹun cluster (indexé ou pas) regroupe donc des tuples des deux tables sur la base de leur valeur commune pour lʹattribut du cluster.
Figure 8.15 Les pages du placement ne sont pas prédéterminées par le moteur du SGBD, mais bien par la valeur de la clé du cluster. Les deux tuples de lʹexemple ont une taille différente, mais sont associés au même attribut de cluster. Donc les trois tuples concernant le compte c35 se trouveront dans la même page. Un cluster, de par sa structure mixte, freine lʹaccès séquentiel à tous les tuples dʹune même table. En effet, ces mêmes tuples sont rangés dans plus de pages que nécessaire et cela constitue une charge supplémentaire pour la lecture séquentielle des pages sur disque.
noCompte
En‐tête de la page 4
nas7, Tremblay, c35
nas56, Gagnon c35
nas78, Poulin, c38
C20 C35 C38
1 2 3 4 5Répertoire du cluster Chaînage des pages pour les entrées de même valeur
Index du cluster
C35, Québec
tuple client
tuple comptetuple client
tuple client
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
154
Pages regroupées (clustered) La jointure est faite via lʹindex et avec la condition suivante :
Client.noCompte = Compte.noCompte Le calcul dʹune telle jointure est très rapide, car les tuples sont déjà regroupés dans la même page.
Index de regroupement (cluster) et ses propriétés : 1‐ Une clé entièrement nulle a une entrée dans le répertoire. Ce type d’index est donc différent de celui en B‐arbre qui n’indexe pas les valeurs nulles. 2‐ L’index de cluster est obligatoire pour accéder aux données. Une entrée pointe sur la 1ere page pour une valeur donnée de la clé du cluster. Autre exemple :
CREATE CLUSTER Lieu_travail (no_usine number) SIZE 4K, -- espace pour ranger les tuples ayant la même --valeur pour l'attribut du cluster TABLESPACE espace_table1; CREATE TABLE Ouvrier( ... noUsine number ) CLUSTER Lieu_travail(noUsine); CREATE INDEX cluster_idx ON CLUSTER Lieu_travail; [Oracle]
8.13 Création et suppression de vues Au chapitre sur le modèle relationnel, la mise à jour par lʹentremise de la vue relationnelle a été présentée comme étant possible dans certaines conditions précises, lesquelles ne peuvent pas être généralisées. Avec les implémentations SQL commerciales, la mise à jour à travers une vue est limitée davantage. CREATE OR REPLACE VIEW VentesImportantes (nom, article, qteTot) AS
SELECT * FROM Ventes WHERE qteTot > 5; Une vue relationnelle ne peut pas être supprimée, ni être utilisée pour la mise à jour d’une relation de base s’il y a violation des contraintes relationnelles actives et renforcées par le SGBD (view dependency). Une vue est conservée sous forme d’une définition SQL et ne correspond pas généralement à une sous‐table réelle et persistante. La vue n’a donc pas d’extension permanente et nʹest pas un instantané (snapshot). Une vue composée d’une jointure de deux relations de base pourrait être autorisée à faire une mise à jour seulement à certaines conditions très particulières qui exigeraient une certaine intelligence de la part
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
155
du SGBD, en plus d’une consultation fréquente du dictionnaire. Cependant, en pratique les systèmes n’autorisent pas la mise à jour à travers une vue définie par une jointure. Finalement, une vue peut être reprise par une requête SQL (à titre de relation intermédiaire) en remplaçant ainsi la variable du type table qui n’existe pas en SQL. Ainsi, on aura :
SELECT V.article FROM VenteImportante V ; -- VenteImportante est une vue
L’extension d’une vue peut être affichée par une consultation de la vue et sans spécification du prédicat WHERE. La vue est aussi dynamique, puisque toute mise à jour sera reflétée dans la vue aussitôt quʹelle est recalculée. La notion de table temporaire de la norme SQL‐92 est différente de la vue, puisquʹil sʹagit dʹune véritable table inscrite dynamiquement dans le schéma et qui nʹest pas actualisée par les mises à jours subséquentes. Elle est donc ainsi différente de la notion de snapshot qui est un instantané des tables et qui peut être mis à jour. Pour créer une vue, lʹutilisateur doit normalement avoir les droits de lecture et de mise à jour sur la ou les tables de base référencées dans la vue et avoir le privilège système de créer des vues dans son schéma. Toutefois, un utilisateur autorisé à créer des vues peut le faire même sʹil nʹa pas les droits dʹaccès sur les tables de base dont il nʹest pas le propriétaire. Cette opération est possible avec la clause CREATE FORCE VIEW.
CREATE FORCE VIEW Ventes_Tres_Importantes (nom, article) as SELECT nom, article FROM Ventes V WHERE qteTot> 10;
La table Ventes peut ne pas être encore créée au moment de la formulation de cette vue.
Fonction dʹagrégation dans une vue relationnelle Une vue définie avec une fonction dʹagrégation (ex. SUM()) doit aussi définir un alias essentiel pour référer à la valeur agrégée. Une telle vue ne permet pas la mise à jour.
CREATE VIEW ventePop as SELECT article, SUM(qte) AS total FROM Ventes WHERE qte > 4;
Variable de table Il en est autrement avec la variable du type table que certains langages implémentent. Lʹexemple ci‐dessous, illustre le stockage du résultat d’une recherche dans une variable de table et sa réutilisation dans une autre expression du langage :
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
156
(vue relationnelle) G_transactions = (SELECT Ventes.article FROM Ventes); Select G.article FROM G_transactions G ;
L’inconvénient majeur de cette approche est le caractère statique de la variable de table. Une application qui utiliserait cette variable n’a pas l’assurance d’avoir accès aux dernières mises à jour faites sur la BD. En effet, seules les relations de base sont mises à jour et les modifications ne sont pas propagées aux variables de table. La vue est similaire à une variable de table, à la différence que la table correspondant à la vue a une vie éphémère et est recalculée à chaque fois que celle‐ci est référencée par une clause SQL. L’instantané sera aussi similaire à une variable de table. L’instantané matérialisé et synchronisé pour la mise à jour correspond à à une variable de table mais dont le contenu est fidèle à la base de données.
8.14 Droits dʹaccès et contrainte pour le calcul dʹune vue Lʹexpression de la vue par une sous‐requête permet de refléter lʹétat réel de la BD au moment de son exécution. La vue étant un objet du schéma de la BD, elle a des privilèges dʹaccès, dʹinsertion, de suppression et de modification comme pour toute table de la base.
CREATE VIEW VentesRecentes as SELECT * FROM Ventes WHERE date > To_Date('01-jan-1995',‘DD-MON-YYYY’);
Ainsi, un usager Paul qui nʹest pas le propriétaire de la vue peut cependant lʹutiliser pour avoir accès aux ventes enregistrées après le 1 janvier 1995. Il doit auparavant obtenir du propriétaire de la table le droit de sélection que ce dernier formulera ainsi :
GRANT SELECT ON VentesRecentes TO Paul;
Lʹusager Paul peut dorénavant utiliser la vue Ventes_Recentes pour effectuer seulement des sélections. Peut‐il faire une insertion concernant une vente de 1996? Il ne peut le faire que sʹil a le privilège dʹajout (insert) sur la vue Ventes_Recentes. Ce privilège lui est accordé par la clause Grant.
GRANT INSERT ON VentesRecentes TO Paul; Peut‐il alors aussi ajouter un tuple pour enregistrer une transaction de 1994 même si la vue ne référence que les tuples de 1995? Oui, car le prédicat dʹune vue qualifie les tuples qui peuvent être sélectionnés et ne contraint pas les tuples qui peuvent être ajoutés, sauf si lʹoption CHECK OPTION est explicitement insérée dans lʹexpression de la vue. Dans
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
157
ce cas, lʹopération résultant sur les tables de base n’est effectuée que si le prédicat de la vue nʹest pas infirmé par celle‐ci. Pour accepter que les transactions ajoutées par Paul se limitent à l’année 1995, il faut modifier lʹexpression de la vue :
CREATE view VentesRecentes_ajout as SELECT * FROM Ventes WHERE date >'01-jan-95' WITH CHECK OPTION;
Cette dernière option va lancer une vérification du prédicat de la vue et cela, pour chaque tuple ajouté par Paul. En cas dʹéchec, le tuple sera refusé par le système. Comme la vue est exprimée par une requête ou une expression SQL, il est possible dʹexploiter toute fonction SQL disponible afin de formuler un prédicat complexe. Par exemple, pour bloquer toute transaction inférieure à 5.00 $ effectuée avant la date du jour, il suffit de formuler la vue ainsi :
CREATE view VentesCourantes as SELECT * FROM Ventes WHERE date = SYSDATE and prix > 5.00 WITH CHECK OPTION;
Ensuite, il faut accorder les droits de sélection et dʹinsertion à Paul :
GRANT SELECT, INSERT on Ventes_courantes to Paul. Si le DBA désire limiter lʹusage de cette vue au lundi, il formulera alors la vue ainsi :
CREATE view VentesCourantes as SELECT * FROM Ventes WHERE date = LOWER(TO_CHAR(SYSDATE,'DAY')) = 'monday' and prix > 5.00 WITH CHECK OPTION;
Lʹajout dʹun tuple par Paul ne sera donc autorisé que si lʹopération est faite un lundi. Il faut souligner que ces vérifications sont faites en ligne et que la contrainte de vue est généralement gardée dans la ZMP.
Suppression de la vue en SQL‐92 Pour supprimer une vue, il faut en être le propriétaire:
DROP VIEW <nom_vue> {RESTRICT | CASCADE}; Lʹoption RESTRICT échoue si la vue est aussi utilisée par une autre vue ou une autre contrainte. Il y a alors une dépendance de vue qui bloque la suppression tant quʹelle subsiste dans la base de données. Quant à lʹoption CASCADE, elle supprime la vue et toute autre vue ou contrainte la référençant. Lʹeffet de cascade se propage dans la chaîne de dépendances de la vue.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
158
Suppression des privilèges sur la vue. Il est aussi possible de supprimer les privilèges liés à une vue sans pour autant supprimer la vue elle‐même.
Figure 8.16 Patricia ne peut pas transmettre son privilège de sélection à dʹautres usagers puisquʹelle nʹa pas lʹautorisation de le faire (sans GRANT OPTION). Si lʹon supprime le privilège de Patricia, aucun autre usager nʹest touché par cette action :
REVOKE SELECT FROM Patricia; Par contre, Sylvain a le privilège de propager ses propres privilèges à dʹautres usagers. La suppression de son privilège dʹinsertion est automatiquement propagé aux usagers dont le même privilège dépend de celui de Sylvain :
REVOKE SELECT FROM Sylvain; La suppression du privilège prend effet seulement lors de la prochaine ouverture de la base de données par lʹusager visé. Sʹil est en ligne au moment de la suppression du privilège, il ne perd pas illico lʹaccès aux objets!
8.15 Modification de la BD à travers une vue SQL Supposons les relations de base définies par les schémas de relation Employe et Departement. La clé étrangère, noDep, permet de relier les deux tables et donc d’effectuer des jointures. Par exemple, il est possible de connaître tous les employés du département d2.
Employe : nas* nom noDep salaire Dep : noDep* site 27 audrey d1 15k$ d1 Québec 20 benoit d2 20k$ d2 Montréal 21 christine d2 19k$ d3 Trois‐Riv 22 audrey d2 18k$ d4 Québec 23 audrey d4 15k$
Vue3 : Vue2 : Vue1 : nas* nom site nom* site* nas* salaire 27 audrey Québec audrey québec 27 15K 20 benoît Montréal benoît Montréal 20 20K 21 christine Montréal christine Montréal 21 19K
Marie‐Claude (Select + Update)
Patricia (Select)
Sylvain (Select + Insert + Update Grant
Jacques (Select + Insert)
Vue Ventes‐99
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
159
22 audrey Montréal audrey Montréal 22 18K 23 audrey Québec audrey* Québec 23 15K
Figure 8.17 Ce tuple est invisible et redondant dans Vue2 malgré son association à une personne différente. La Vue3 est simple et correspond à une projection de la table Employe. Les vues Vue2 et Vue3 sont plus complexes et correspondent à des jointures de tables. Les deux relations suivantes ont les attributs suivants comme clé primaire : nas et noDep. Les vues sont définies par des expressions algébriques qui correspondent à des tables virtuelles calculées à chaque fois quʹune application fait référence à la vue. La vue reflète donc lʹétat de la BD au moment de son calcul ou de sa matérialisation.
Expression algébrique des vues La vue relationnelle est exprimée par une expression algébrique qui est évaluée seulement au moment de lʹexécution. Avec un langage de données de plus haut niveau comme le SQL, la vue sera formulée par une clause SQL stockée dans le dictionnaire des métadonnées qui sera aussi évaluée seulement lors de lʹexécution. Ainsi, lʹextension de la vue reflète toujours lʹétat actuel de lʹinstance de la BD, qui a peut‐être été mise à jour depuis la dernière matérialisation de la vue. Vue1 : Πnas,salaire (employe) <‐‐ projection, relation de base Vue2 : Πnas,nom,site((Πnas,nom, noDep(employe)|x|departement) Vue3 : Πnom, site ((Πnom,noDep(employe))|x|departement) Examinons en premier la vue la plus simple, soit la vue Vue1. Elle correspond à un sous‐ensemble d’une seule relation de base. Les vues Vue2 et Vue3 ont chacune un schéma composé d’attributs provenant de plusieurs relations de base. Supposons que les droits d’accès pour la lecture et pour la mise à jour soient accordés aux propriétaires de ces vues pour effectuer les opérations ci‐dessous. Rappelons aussi que lʹinsertion se fait au moyen dʹun seul ordre DML dʹinsertion.
Usager 1 Par lʹentremise de la vue Vue1, modifier le salaire de l’employé dont le nas est 21 pour que son salaire soit maintenant 20 K$. La mise à jour peut être effectuée parce que le SGBD peut identifier (procédure unique pour le trouver) dans une relation de base et sans ambiguïté le tuple à mettre à jour. On dit alors que cette vue est modifiable ou ʹupdatableʹ. Dans l’application lʹordre DML est le suivant :
Update vue1 set salaire = 20 K$ WHERE nas = 21; (avec la vue) Le SGBD peut transposer cette opération de mise à jour par lʹentremise de la vue en une procédure interne sur les relations de base, laquelle peut effectuer sans ambiguïté la
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
160
modification demandée. Cette procédure interne doit être composée dʹordres DML de même nature que celui de la procédure externe (i.e. le DML utilisé sur la vue). Ordre interne exécuté par le SGBD :
Update Employe set salaire = 20 K$ WHERE nas = 21; (avec une table de base) Un seul tuple sera mis à jour conformément à l’intention de l’application, soit celui identifié par le nas 21 dans la relation de base Employe. La mise à jour à travers cette vue sera donc possible.
Usager 3 Par lʹentremise de la vue Vue3, faire lʹajout du tuple : (28, ‘denis’, ‘Hull’).
Insert INTO Vue3 VALUES (28, ʹdenisʹ, ʹHullʹ); Pour l’application, un seul tuple doit être mis à jour et la modification de l’extension doit pouvoir se refléter dans les relations de base.
Employe nas* nom noDep salaire Dep : noDep* site 27 Audrey d1 15k$ d1 Québec 20 Benoit d2 20k$ d2 Montréal 21 Christine d2 19k$ d3 Trois‐Riv 22 Audrey d2 18k$ d4 Québec 23 Audrey d4 15k$ ‐‐> ‐‐ Hull ‐‐‐> 28 Denis ‐‐ ‐‐
Figure 8.19 En effet, un affichage de lʹextension de la vue révèle quʹaprès cette insertion il y a un seul tuple représentant Denis dont le nas est 28. Son lieu de travail est absent de la base, car le tuple véhiculant cette information nʹa pas pu être inséré dans la relation Departement puisque la clé est absente. Lʹintention de lʹutilisateur est claire, mais il y a une ambiguïté au niveau du SGBD. Quel est lʹeffet au niveau de la BD? La procédure interne exécutée par le noyau du SGBD serait la suivante :
Insert INTO Employe VALUES (28, ʹDenisʹ, null, null); Insert INTO Departement VALUES (null, ʹHullʹ);
Cet ajout viole cependant une contrainte fondamentale du modèle relationnel, à savoir que la clé de la table Departement ne peut être nulle. Pour éviter ce problème, le système devra interdire cette mise à jour. La Vue1 correspond à une jointure des relations de base, mais elle n’inclut pas les clés primaires des deux relations intervenant dans la jointure. Cʹest une condition suffisante, mais non nécessaire pour exclure ce type de mise à jour par lʹentremise dʹune vue. Usager 2
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
161
Avec la Vue2, effectuer une modification pour refléter le fait que lʹemployée audrey de Québec travaille maintenant à Montréal. Selon la vue Vue2, il y nʹa qu’une seule personne nommée Audrey qui devrait faire l’objet de la mise à jour, soit un seul tuple : UPDATE Vue2 set site = ʹMontréalʹ WHERE nom = ʹAudreyʹ and site = ʹQuébecʹ; Lʹintention de lʹapplication est claire, mais le SGBD ne peut la comprendre, car il y a plusieurs tuples candidats à cette modification. Sans autre connaissance externe, le SGBD traduit cette procédure en une autre qui utilise seulement les relations de base et les constantes fournies par lʹapplication :
UPDATE Employe set noDep = (SELECT noDep FROM Departement WHERE site = ʹMontréalʹ) WHERE nom = ʹAudreyʹ and noDep in (SELECT noDep FROM Departement WHERE site = ʹQuébecʹ);
La procédure interne est composée dʹun seul ordre DML du même type constitué des constantes de lʹexpression fournies par lʹapplication. L’effet net modifiera deux tuples dans la table de base! Ainsi, lʹapplication anticipe une seule mise à jour et deux sont réalisées dans les tables de base par le SGBD! Qu’en est‐il maintenant si le tuple de mise à jour spécifie que Audrey du département d1 (Québec) travaillera maintenant à Montréal? Dans ce cas particulier, la relation de base pourra être mise à jour seulement si Vue2 est enrichie du numéro de département de la relation Departement. Il nʹest cependant pas possible de généraliser ce cas.
Traitement dʹune requête SQL référant à une vue relationnelle Une requête SQL avec une référence à une vue peut être traitée soit par substitution, soit par matérialisation. La vue est définie par son expression SQL stockée dans le dictionnaire de données sous forme dʹune chaîne de caractères.
8.16 Matérialisation dʹune vue Dans certains cas, l’exécution d’une requête avec une vue est impossible par substitution et cela, pour des raisons syntaxiques, mais elle le devient avec la matérialisation. Voici la BD FPF composée des trois relations suivantes :
Fourn(noF*, nomF, cote, ville) Piece(noP*, nomP, couleur, poids, ville) FournPiece(noF*, noP*, qte
où nof le numéro du fournisseur noP le numéro de la pièce
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
162
La vue SQL : CREATE view P_QTE as SELECT SUM(FP.qte) as qteTot <‐‐ alias FROM FournPiece as FP GROUP BY FP.noP;
Le schéma de la vue P_QTE est le suivant : P_QTE (qteTot). Soit la requête sur la vue P_QTE :
SELECT AVG(qteTot) as mqp FROM P_QTE;
La réponse attendue a un schéma composé du seul attribut‐alias mqp qui représente une moyenne numérique. Si le SGBD procède uniquement par substitution, la requête obtenue sera refusée par plusieurs SGBD, en raison de l’imbrication des fonctions d’agrégation :
SELECT #p, AVG(SUM(FP.qte)) as mqp <‐ imbrication au niv.1 FROM FP GROUP BY FP.noP ;
Toutefois, la même requête sera acceptée si le SGBD utilise une matérialisation de la vue. Le calcul de la vue donne une relation intermédiaire temp1 :
temp1 : noP SUM(FP.qte) 1 44 3 33 2 22
Figure 8.20 La relation temp1 est l’objet d’une projection pour obtenir la valeur de SUM(FP.qte) lequel peut être renommé Somme‐quantité. C’est sur cette table temporaire que la requête SQL est exécutée pour donner la moyenne escomptée. Certains logiciels commencent par utiliser la substitution et, en cas d’échec, font appel à la matérialisation (voir DB2 dʹIBM). Cette matérialisation est aussi employée pour le traitement des sous‐requêtes, c’est‐à‐dire les clauses SQL de niveau supérieur à 1.
Matérialisation de la vue en Oracle Les versions récentes dy système Oracle autorisent la matérialisation de la vue au moyen d’une commande DDL particulière. Ce mécanisme est similaire à l’instantané avec ou sans mise à jour. Pour créer une vue, il faut avoir les privilèges CREATE MAERIALIZED VIEW, CREATE DIMENSION et QUERY REWRITE.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
163
Create MATERIALIZED view SalaresMoy never refresh – ou refresh (mises à jour exclues) enable query rewrite ‐‐ autorise l’optimisation as Select nom
From Empl Where salaire > 30000 and salaire < 60000;
A la première référence à cette vue, elle sera matérialisée de sorte que l’extension correspondante soit disponible lors des traitements subséquents. Cette vue existe au delà du COMMIT de la transaction qui a lancé sa création. Une vue matérialisée est aussi appelée Snapshot.
Droit dʹaccès aux tuples de la vue relationnelle Pour traiter les données à travers une vue, il faut que lʹapplication ait des droits dʹaccès aux tables et que les manipulations ne violent pas les contraintes dʹintégrité définies sur les tables de base : a) En lecture, l’opération est réalisée si l’usager a les droits de lecture sur toutes les relations de base fournissant des attributs à la vue. b) En mise à jour, l’usager doit avoir les droits de lecture et de suppression sur les relations de base fournissant les attributs à la vue; la mise à jour étant traitée comme une suite atomique d’une suppression et d’un ajout. c) En mise à jour, les contraintes d’intégrité actives quant à la BD ne peuvent pas être violées dans les relations de base après l’opération de mise à jour. d) Si la vue est définie sur une seule relation de base et qu’elle inclut la clé de cette première sans autre fonction, alors il est possible d’ajouter, de supprimer et de modifier un tuple de la vue sans créer d’ambiguïté. Cette condition est difficile à faire vérifier par le SGBD autrement quʹen lui faisant valider systématiquement les vues dans le dictionnaire. Si un utilisateur a le privilège de créer des vues, il peut le faire librement avec les relations auxquelles il a accès. Le DBA devrait‐il autoriser les vues au préalable et interdire les modifications subséquentes non contrôlées ? Si une telle approche était adoptée, serait‐elle perçue comme un obstacle dans les opérations que peut effectuer un utilisateur sur les données.
Mise à jour à travers une vue incluant une fonction Si la vue comprend une fonction de groupement, d’agrégation, arithmétique ou de chaîne, la mise à jour est interdite. En effet, toute modification de l’information obtenue par la fonction ne peut pas être transposée de façon appropriée dans les relations de base. Il est cependant possible d’interroger à volonté cette vue (read only view). La lecture par lʹentremise dʹune vue ne pose pas de problème, si ce n’est généralement celui de l’interprétation des tuples affichés, en particulier en ce qui concerne le résultat
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
164
obtenu avec les fonctions arithmétiques, d’agrégation et de groupement. Pour contourner la difficulté, les langages de données permettent à lʹutilisateur de remplacer les en‐têtes de colonne par des expressions plus significatives. Un autre cas très particulier à éviter est celui de la lecture d’une relation obtenue par la jointure de vues interdites par une dépendance de jointure. Cette anomalie est possible lorsquʹil y a une dépendance de jointures dans le schéma de la BD et que le schéma nʹest pas en 5FN. Sur le plan théorique, certaines vues peuvent être utilisées pour la mise à jour, tandis que d’autres sont à proscrire15. Il y a donc certaines vues qui sont indésirables en ce qui concerne la mise à jour et que l’administrateur de la BD devrait repérer par une validation des vues proposées et au besoin enrichir de manière à ce que les opérations demandées par les usagers puissent être exécutées correctement. Cʹest cependant une solution au cas par cas qui nʹest pas acceptable dans un contexte dʹexploitation le moindrement dynamique.
Création des vues avec SQL (ordre DDL de SQL) La création d’une vue consiste à définir l’expression SQL correspondante qui dans certains cas tient lieu de pseudo‐variable de table. Exemples : Vue 1 : Les nas, nom et site des employés qui travaillent dans un département. �nas, nom, site ((�nas, nom, noDep (Employe)) |x| Departement):
CREATE [REPLACE] VIEW vue1 AS SELECT nas, nom, site FROM Employe [as] E, Département [as] D WHERE E.noDep = D.noDep;
Vue 2 : Le nom et le site des employés qui travaillent dans un département.
�nom, site ((∏nom, noDep(Employe)) |x| Departement) CREATE [REPLACE] view vue2 as SELECT nom, site FROM Employe[as] E, Departement [as] D WHERE E.noDep = D.noDep;
Vue 3 : Le nas et le salaire des employés.
�nas, salaire (Employe) : <‐‐ relation de base CREATE [REPLACE] view vue3 as SELECT nas,salaire FROM Employe;
Ces expressions de vue relationnelle sont stockées dans le dictionnaire de données de la base. Voici une base composée de deux taables qui décrit le lieu de travail des personnes qui sont en ménage.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
165
Menage (conjoint, conjointe) LieuTrav(nas, nom, ville, salaire) Chaque attribut de Ménage partage le même domaine sémantique, soit le nas de chaque personne. Le domaine des autres attributs est suggéré par leur libellé respectif. Les domaines sont donc les suivants : conjoint d‐nas conjointe d‐nas nas d‐nas salaire d‐entier nom STRING (varchar(40)) domaine syntaxique
Menage : conjoint conjointe 345 456 923 786 387 384 629 310 319 231 127 612
Figure 8.21 Vue 4 : Le nom et le salaire des conjointes dont le salaire est supérieur à 25 000 $ .
CREATE view vue4 (nom_conjointe, salaire) as SELECT nom, salaire FROM Menage [as] M, Lieu_travail [as] L WHERE M.conjointe = L.nas and L.salaire > 25 000;
Vue 5 : Le nom des conjoints qui ont un salaire égal à celui de leur conjointe.
Lieu_Travail : nas nom ville salaire 345 Jacques Québec 20K$ 231 Danielle Trois‐Rivières 19K$ 456 Sylvie Québec 28K$ 127 Nahn Nice 32K$ 612 Josée Chicoutimi 26K$ 387 Pierre Québec 25K$ 384 Clotilde Ste‐Foy 22K$ 923 Rachid Québec 25K$ 319 Arthur Québec 22K$ 187 Gerry Montréal 25K$ 310 Patricia Québec 25K$ 452 Estelle Montréal 23K$ 629 André Montréal 25K$ 876 Denise Québec 27K$
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
166
Comme il n’est pas possible de définir des relations temporaires en SQL, les vues serviront à cette fin, car elles peuvent être interrogées comme si elles étaient des tables de base. La vue ci‐dessous permet de trouver l’information ayant trait aux conjoints : conjointe, son salaire et la ville de son domicile.
CREATE view vue5.1 as SELECT conjoint, conjointe, salaire, nom FROM Menage [as] M, Lieu_travail [as] L WHERE M. conjointe = L.nas;
De même pour la conjointe :
CREATE view vue5.2 as SELECT conjoint, conjointe, salaire, nom FROM Menage [as] M, Lieu_travail [as] L WHERE M. conjoint = L.nas;
CREATE view reponse as SELECT conjoint FROM vue5.1 X, vue5.2 Y WHERE X.conjointe = Y.conjointe and X.conjoint = Y.conjoint and X.salaire = Y.salaire;
SELECT conjoint [as] nas-conjoint /*renommage attribut*/ FROM reponse;
ou plus directement :
SELECT conjoint[as] nas-conjoint FROM vue5.1 [as] X, vue5.2 [as] Y WHERE X.conjointe = Y.conjointe and X.conjoint = Y.conjoint and X.salaire = Y.salaire;
Vue 6 : Les conjointes et le lieu de leur travail pour celles qui travaillent dans la même ville que leur conjoint.
CREATE OR REPLACE view vue6.1 as SELECT conjoint, conjointe, nom, ville FROM Menage [as] M, LieuTravail [as] L WHERE M.conjointe = L.nas; CREATE view vue6.2 as SELECT conjoint, conjointe, nom, ville FROM Menage [as] M, LieuTravail [as] L WHERE M.conjoint = L.nas; CREATE view reponse as
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
167
SELECT conjointe FROM vue6.1 [as] X, vue6.2 [as] Y WHERE X.conjoint = Y.conjoint and X.conjointe = Y.conjointe and X.ville = Y.ville; SELECT conjointe [as] nas-conjointe FROM reponse;
ou plus directement :
SELECT conjointe [as] nas_conjointe FROM vue6.1 [as] X, vue6.2 [as] Y WHERE X.conjoint = Y.conjoint and X.conjointe = Y.conjointe and X.ville = Y.ville;
Vue 7 : La ville et la moyenne des salaires, le plus petit et la somme des salaires.
CREATE view vue7 (ville, moy_sal, min_sal, som_sal) AS SELECT ville, Avg(salaire), Min(salaire), Sum(salaire) FROM LieuTrav GROUP BY ville; SELECT ville, moy_sal /*interrogation possible*/ FROM vue7;
Restrictions associées à la vue relationnelle La vue relationnelle est définie par une expression algébrique. Elle permet un renommage des attributs (alias), mais interdit les opérations suivantes : a) Le changement de type pour les attributs spécifiés dans les relations de base. b) La modification des droits d’accès aux tables de base. Avec SQL, une mise à jour est possible par lʹentremise dʹune vue si, et seulement si, toutes les propositions ci‐dessous sont validées : a) L’expression de la vue est une clause SELECT qui ne contient pas les mots clés suivants : JOIN, UNION, INTERSECT, EXCEPT (sauf); b) La clause SELECT ne contient pas le mot clé distinct. Chaque attribut du SELECT fait une référence simple à une colonne de la table (sans fonction); c) Il y a une seule table dans la clause FROM, donc pas de jointure; La table référencée dans la clause FROM est une table de base ou une vue de mise à jour acceptable; d) L’expression de la vue n’inclut pas un WHERE avec une sous‐requête qui se rapporte aussi à la même table (autojointure interdite); e) L’expression de la vue n’a pas de GROUP BY; f) L’expression de la vue n’a pas de HAVING.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
168
Il est donc possible de faire une mise à jour très limitée dans une BD par lʹentremise dʹune vue relationnelle (incluant la suppression et l’ajout). 8.17 Instantané
Le concept de l’instantané ou snapshot est intéressant parce qu’il permet de garder facilement une image de la BD. Cet objet est une table qui reflète lʹétat dʹune autre table de base ou dʹune vue relationnelle à un moment donné et, au besoin, les mises à jour subséquentes effectuées sur la table de base. Ce mécanisme est utile parce quʹil permet de conserver localement une copie dʹune table distante qui est mise à jour périodiquement et automatiquement. C’est une notion apparentée à celle de la vue matérialisée. CREATE SNAPSHOT <schéma> <nom‐snapshot>
REFRESH [FAST |COMPLETE | FORCE] START WITH <date> NEXT <date> FOR UPDATE AS <sous‐requête> ;
Le mode de rafraîchissement dʹun instantané détermine comment sera faite lʹacquisition des mises à jour effectuées sur la table de base (locale ou distante), sous‐tendue par la définition de l’instantané. Les modes possibles sont : FAST : la mise à jour est incrémentielle et effectuée à partir dʹun journal de table spécial créé et associé à la table de base à laquelle est relié lʹinstantané. Le journal de table est créé par la commande suivante : Exemple : CREATE SNAPSHOT LOG <schéma.><table> ; COMPLETE : la requête du snapshot est réexécutée à la date prévue par le rafraîchissement. FORCE : un FAST si cela est possible, sinon un COMPLETE ( cas : absence du journal de table). Le moment de la mise à jour de lʹinstantané est contrôlé par la clause START WITH. Il est aussi possible de lancer une procédure utilitaire pour démarrer immédiatement le rafraîchissement de la table.
Mise à jour de l'instantané La création d’un instantané sous‐tend la création d’une table correspondant au schéma de la requête et dont l’extension est formée avec les données fournies par lees tables de base.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
169
CREATE SNAPSHOT Ouvrier_Exp PCTFREE 10 PCTUSED 100 TABLESPACE TS_utilisateur REFRESH FAST ON COMMIT AS SELECT * FROM T1 WHERE age > 50;
La mise à jour d’une table de base référée par un instantané FAST est faite au moment spécifié dans la clause REFRESH soit à chaque commit. Cette mise à jour est réalisée à partir d’un journal spécial associé à la table de l’instantané.
Mise à jour de l’instantané Figure 8.21a
Avec les versions plus récentes, par exemple Iracle 9i, la notion d’instantané est remplacée par celle équivalente de vue matérialisée ou vue concrète. Exemple : La vue matérialisée sera mise à jour dans 15 jours à compter de la date de sa création. CREATE MATERIALIZED VIEW Ouvrier_Exp
REFRESH FAST NEXT SYSDATE + 15 AS SELECT * FROM T1 WHERE age > 50;
8.18 Langage procédural PL/SQL
L’ajout des déclencheurs et des procédures stockés dans une base de données relationnelle a fait apparaître la nécessité d’avoir un SQL enrichi doté des structures de contrôle communes aux langages procéduraux. La proposition de la société Oracle pour la spécification des triggers consiste à donner un caractère procédural à SQL*Plus dans le cadre du langage PL/SQL16. Ce langage implémente la ʺprocéduralitéʺ permettant ainsi une manipulation très souple des tuples des tables de base. Ce langage peut être utilisé à diverses fins : écriture des déclencheurs de Developer, écriture des procédures, des fonctions et des packages internes qui sont exécutés automatiquement ou sur appel par le noyau du SGBD.
Principales caractéristiques du PL/SQL Le PL/SQL est un langage procédural qui étend la puissance de traitement du SQL. Il est portable à l’intérieur de la gamme de produits dʹOracle. Ainsi, un concepteur peut mettre au point une requête SQL complexe avec l’interpréteur interactif et ensuite lʹinsérer dans un déclencheur ou dans une application en L3G sans y apporter de changement majeur.
26, fabella, 68 18, paul, 50 35 claude, 56
26, fabella, 68 18, paul, 50 35 claude, 56
35 claude, 56
Table Ouvrier_Exp (instantané) Table Ouvrier de base Journal de Ouvrier_Exp
321 Ajout d’un tuple
Mise à jour
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
170
Le langage PL/SQL a une structure de bloc avec les structures de contrôle habituelles : les affectations, les alternatives, les itérations et les appels aux procédures, fonctions et packages. Toutes les clauses SQL*Plus peuvent être insérées dans un bloc PL/SQL. Chaque bloc est exécuté par le noyau comme une transaction indivisible. Les procédures SQL sont stockées dans le serveur et sont appelées par un déclencheur ou par un autre bloc. Elles peuvent aussi être exécutées en ligne avec le système SQL*Plus.
Bloc PL/SQL Un bloc est nommé si la clause BEGIN est étiquetée, sinon il est dit bloc anonyme. Les clauses du langage qui sont admissibles dans un bloc sont les suivantes : ‐ Les variables, les constantes et les curseurs sont déclarés dans une section spéciale du bloc identifiée par le mot clé DECLARE. ‐ L’affectation se fait par valeur et non par référence (par pointeur). ‐ La structure de recherche alternative est implémentée par le IF ... ... END IF. Chaque élément de lʹalternative peut être formé dʹun bloc PL/SQL. ‐ L’itération est implémentée par quelques structures : FOR, LOOP et WHILE. ‐ Les fonctions arithmétiques habituelles sont permises. ‐ Le branchement inconditionnel autorisé par lʹusage dʹune étiquette placée entre guillemets français : <<label >>. Le traitement des exceptions est géré au moyen des exceptions déclarées explicitement dans le bloc PL/SQL ou définies implicitement dans le langage. Le traitement de lʹexception terminé, lʹexécution se poursuit avec le premier énoncé après la sortie du bloc courant.
Structure générale dʹun programme PL/SQL Voici la structure générale dʹun programme PL/SQL17; elle comprend un ou plusieurs blocs, dont chacun est constitué de la partie DECLARE, BEGIN et EXCEPTION. Seule la partie BEGIN est obligatoire. [DECLARE]
Déclarations des variables, constantes, exceptions et curseurs hôtes
BEGIN Clauses SQL et PL/SQL (lʹemboîtement des blocs est possible)
[EXCEPTION] Traitement des exceptions
END; Figure 8.22
Section DECLARE du bloc PL/SQL Cette section contient la déclaration des variables du langage hôte et des constantes. Voici quelques types disponibles : NUMBER, CHAR, DATE, BOOLEAN et RECORD. Le type peut être spécifié par un attribut du schéma de la BD : attribut%TYPE (par exemple
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
171
: Empl.nom_famille%TYPE). L’initialisation de la variable peut être faite dans la déclaration. Une seule variable est déclarée et initialisée par énoncé.
DECLARE salaire NUMBER (5,2); taux Empl.tarif%TYPE; /*var. taux a le type de Empl.tarif*/ mat varchar2(10); bonus NUMBER(2) := 10; nom VARCHAR(35); CURSOR Recherche IS SELECT E.nom,E.tauxH FROM Empl as E WHERE noEmpl = ʹP762ʹ;
erreur5 EXCEPTION; /*déclar.label de lʹexception */ BEGIN OPEN Recherche; ‐‐ ouverture du curseur et calcul de la clause WHILE Recherche%FOUND FETCH Recherche INTO nom, taux; IF taux > 20 THEN RAISE erreur5; ELSE UPDATE Empl set tauxH = tauxH * 1.05 WHERE CURRENT of Recherche; END LOOP; CLOSE Recherche; EXEPTION WHEN erreur5 THEN ... END;
Figure 8.23
Section de traitement du bloc PL/SQL Cette partie de la procédure contient les énoncés de traitement selon la syntaxe de PL/SQL. Les clauses PL/SQL comprennent toute clause SQL et les structures de contrôle standards pour l’alternative, la répétition et l’affectation. Le traitement manipule des données de la BD et en permet le stockage dans une table de base ou dans une table temporaire existante ou créée par la procédure. Le langage n’a pas d’ordre d’affichage direct. Les données obtenues par une clause SELECT doivent être placées dans des variables PL/SQL.
Affichage au terminal pour des fins de mise au point Il est possible d’utiliser une procédure d’un package pour un affichage (lecture) élémentaire au terminal/station client. Cette opération est implémentée par le SGBD en créant un pipe entre la procédure et le SGBD. Les données placées dans le pipe peuvent être lues et affichées au terminal standard de sortie par un autre appel au package DBMS_OUTPUT.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
172
Pour l’affichage correct il faut activer le paramètre SERVEROUTPUT à ON. SQL> SET SERVEROUTPUT ON ‐‐ dans le contexte du module SQL*Plus Les procédures sont les suivantes :
DBMS_OUTPUT.PUT_LINE ( exp |constante | attribut) DBMS_OUTPUT.GET_LINE ( var_PL/SQL)
Remarque sur la syntaxe des variables Toute variable d’un bloc PL/SQL qui n’est pas un attribut spécifié dans le dictionnaire de la BD, et qui est une variable PL/SQL de réception de données dans le FETCH et le SELECT INTO, doit à ce titre, être déclarée dans le bloc PL/SQL considéré comme le langage hôte. Cependant, il ne faut pas préfixer une telle variable par les deux points (:), car le bloc PL/SQL est exécuté par le serveur et les variables PL/SLQ sont déclarées et connues au moment de lʹexécution de la clause SQL.
DECLARE article CHAR (3); qte Ventes.qteTot%TYPE; bonus NUMBER :=0 ; ‐‐ initialisation BEGIN SELECT Ventes.article, qteTot INTO article, qte ‐‐ curseur implicite FROM Ventes Where nom = ʹJacquesʹ; ‐‐ 1 seul tuple attendu bonus := 10; INSERT INTO ACHAT_JACQUES VALUES (article, qte, bonus); EXCEPTION WHEN NO_DATA_FOUND THEN RAISE_APPLICATION_ERROR(‐20400, ʹpas de tuple trouvéʹ); END;
La procédure RAISE_APPLICATION_ERROR() est fournie par une librairie (Package) STANDARD du SGBD et sʹexécute sur le serveur. Si la même procédure est lancée et que lʹerreur ‐20200 doit être traitée non pas par le handler de Oracle, mais pour une exception de la procédure, il faut faire appel à la directive de compilation PRAGMA :
EXEC SQL EXECUTE DECLARE ... tauxNul EXCEPTION; PRAGMA EXECEPTION_INIT(tauxNul, +100);‐‐ +100 ‐>no_data_found BEGIN ... EXCEPTION
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
173
WHEN tauxNul THEN INSERT INTO ...; END;
Ce bloc est transmis par la station‐client au serveur et lʹerreur ‐20200 sera appariée à lʹexception tauxNul et sera lʹobjet du traitement associé et défini par l’application en remplacement de celui prévu par Oracle pour ces exceptions utilisateurs.
Variable hôte des langages L3G Toute variable utilisée dans une clause SQL qui n’est pas un attribut ni un nom de relation est une variable hôte et doit être déclarée dans la procédure ou le bloc. Dans le cas de SQLFORMS, la variable sera préfixée par : pour identifier un champ de la forme. C’est ainsi que l’interprète SQL est informé qu’il s’agit d’une variable du langage hôte, en lʹoccurrence le C. La structure itérative dʹune procédure PL/SQL se manifeste sous plusieurs formes : LOOP, FOR, WHILE, ... et avec une syntaxe relativement simple : Exemple
DECLARE compteur NUMBER(3,0) := 0; BEGIN LOOP INSERT INTO Temp VALUES (compteur, ʹokʹ); compteur := compteur + 1; IF compteur = 100 THEN EXIT ‐‐ ou EXIT WHEN compteur = 100; END IF; END LOOP; END;
Figure 8.24
Boucle FOR Une boucle FOR permet de répéter lʹexécution dʹun bloc dʹénoncés un nombre déterminé de fois.
FOR <indice> IN [REVERSE] <n1> .. <n2> LOOP <liste dʹénoncés> END LOOP;
Lʹindice est une variable locale qui prend chaque valeur une seule fois, soit dans lʹordre croissant, soit dans lʹordre inverse. La valeur initiale est n1, la valeur finale incluse est n2. Lʹindice nʹa pas besoin dʹêtre déclaré, par défaut il est du type NUMBER. Lʹindice ne peut pas prendre une nouvelle valeur dans la boucle par affectation, mais peut être utilisé dans les expressions de la boucle. Notez quʹil nʹy a pas de valeur dʹincrément dans la clause. Elle pourrait être simulée par la fonction MOD(nb, k) exécutée dans le corps de lʹitération. Par exemple, pour insérer
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
174
un tuple dans la table Temp à chaque 5 tuples des 50 fournis par lʹusager, il suffira dʹutiliser la division par MODULO 5.
DECLARE nom CHAR(45); . . . FOR k in 5 ..50 LOOP nom := &nom_i; ‐‐nom à fournir interactivement age := &age_i; ‐‐idem matricule := &matricule_i;‐‐ idem IF MOD(k, 5) = 0 THEN INSERT INTO Temp VALUES (matricule, nom, age); ELSE NULL; END IF; END LOOP; END;
Figure 8.25 Ce bloc SQL anonyme comporte des paramètres préfixés par le & qui présuppose quʹil est exécuté dans lʹenvironnement de SQL*Plus.
Section des exceptions du bloc PL/SQL En PL/SQL, le traitement des erreurs (les exceptions) est géré par des exceptions prédéfinies ou par celles spécifiées par une application (RAISE erreur5 ). À la fin du traitement de l’erreur, le traitement se poursuit avec l’instruction qui suit immédiatement le bloc courant. L’exemple ci‐dessus illustre le traitement d’une erreur nommée erreur5.
BEGIN IF qte > 99 THEN RAISE erreur5; ... END IF; IF ...THEN END IF; EXCEPTION WHEN erreur5 THEN
RAISE_APPLICATION_ERROR(‐21400, ʹERREURʹ); ... <‐‐reprise du traitement avec le bloc situé après celui de lʹexception libellée ‘erreur5’ ... END;
Figure 8.26
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
175
Exemple dʹun bloc PL/SQL Ce bloc n’utilise pas de curseur. Il doit donc traiter un seul tuple, sinon il y aura erreur du type en activant lʹexception TOO_MANY_ROWS. Cette exception est lʹune parmi plusieurs prédéfinies. Pour traiter plusieurs tuples, il faudrait utiliser un curseur. Par exemple, la recherche du tuple décrivant la transaction de vente faite par le client ʹTremblayʹ permet de trouver son numéro de client et de la placer dans une variable num_empl du même type que celui déclaré dans le dictionnaire de BD pour l’attribut noEmpl. Si plusieurs tuples sont trouvés avec le critère ʹTremblayʹ , il y a une erreur et celle‐ci est traitée par le segment EXCEPTION qui inscrira un indicateur spécial dans la table Temp et validera l’opération.
Exceptions prédéfinies Parmi ces exceptions prédéfinies mentionnons : TOO_MANY_ROWS, NO_DATA_FOUND, INVALID_CURSOR, etc. Voici un exemple dʹune procédure PL/SQL dʹune application dans laquelle il y a vérification du nombre de tuples retournés dans la réponse à une clause SELECT . DECLARE salaire_empl number(5,2); num_empl Empl.noEmpl%TYPE; ‐‐ type copié du DD erreur2 EXCEPTION; BEGIN SELECT noEmpl, salaire INTO num_empl, salaire_empl FROM EmplWHERE nom = ʹTremblayʹ; IF salaire_empl > 100 000 THEN RAISE erreur2; INSERT INTO Temp VALUES (num_empl,ʹnumero de tremblayʹ); DELETE FROM Empl WHERE nom = ʹtremblayʹ; . . . EXCEPTION WHEN erreur2 THEN INSERT INTO MSG_PILE VALUES(ʹaucun Tremblay riche’); WHEN TOO_MANY_ROWS OR NO_DATA_FOUND THEN INSERT INTO MSG_PILE VALUES (ʹaucun or trop de richesʹ); WHEN OTHERS THEN NULL; END; Figure 8.27 Lʹexception TOO_MANY_ROWS est prédéfinie dans le langage PL/SQL et nʹa donc pas à être déclarée dans le bloc PL/SQL. Dʹautres erreurs prédéfinies sont aussi utiles avec le PL/SQL :
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
176
CURSOR_ALREADY_OPEN INVALIDE_CURSOR PROGRAM_ERROR TIME_OUT_ON_RESOURCE LOGIN_DENIED NOT_LOGGED_IN Ces blocs PL/SQL sont utilisés dans le contexte de SQL*Plus. Il peut être aussi implémenté comme déclencheur de BD et de FORMS. Auquel cas, les clauses COMMIT et ROLLBACK sont inadmissibles, de même que dans les procédures stockées dans le dictionnaire du SGBD. 8.19 Base de données réactive
La base de données réactive est une fonctionnalité de système implémentée dans les nouvelles versions de SGBD (DB2‐V2, Oracle, INFORMIX).
Figure 8.28 Le précurseur en la matière fut le SGBD SYBASE. Avec une telle base, un événement (E) particulier se produit lorsquʹune condition (C) est vérifiée. Il en découle immédiatement une suite dʹactions (A) qui sont implémentées avec le DML du système ou un langage procédural comme le PL/SQL. Les règles ECA sont formulées avec un langage procédural dont le pouvoir dʹexpression peut être plus ou moins complexe selon les SGBD. Par exemple, avec le système Oracle, les événements sont prédéfinis et les conditions et les actions sont spécifiées par le langage procédural PL/SQL utilisé pour lʹécriture des triggers. Avec DB2, le langage est le C.
Les événements déclencheurs définis au préalable Dans une base de données, une action dʹinsertion, de suppression ou de modification est un événement qui se passe au niveau de la table ou du tuple. De même, la suppression dʹune suite de tuples qui vérifient un prédicat de sélection est un événement qui a lieu au niveau du tuple ou au niveau de la table, selon le choix effectué. Lorsque lʹévénement est pris en considération au niveau du tuple, une action peut être déclenchée
Application : Insert into X… Delete X … where Update X .. set …
Base de données : Table X
Trigger
Trigger
Trigger
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
177
immédiatement après ou avant chaque suppression du tuple. Par contre, si lʹévénement est déclaré au niveau de la table, une action ne sera déclenchée quʹà la toute fin des suppressions par lʹordre DELETE. Lorsquʹun événement est détecté par le SGBD, une autre condition peut être formulée pour contraindre davantage le déclenchement de lʹaction (WHEN).
Définition du déclencheur (trigger) Le trigger correspond à une logique de traitement plus ou moins complexe implémentée dans Oracle par une procédure PL/SQL qui est exécutée automatiquement lorsquʹun événement (E) comme une insertion, une mise à jour ou une suppression se produit dans une table de la BD. Une telle procédure (A) est appelée un trigger de BD parce quʹelle est liée à la BD et lancée lorsque survient un événement, et cela, indépendamment de lʹapplication. La procédure la plus simple est composée dʹune clause SQL, une autre plus complexe comprendra un ou plusieurs blocs PL/SQL.
Propriétés du trigger Un trigger est défini une seule fois et est accessible par tous les événements de la BD; il est rangé au niveau du serveur et est normalement actif pour tous les utilisateurs de la BD. Sur le plan conceptuel, il correspond à une factorisation de procédures normalement lancées par chaque application pour assurer la cohérence de la BD. En ramenant celles‐ci à la BD, on augmente la productivité des concepteurs et on a une meilleure garantie de cohérence. Un événement qui active un trigger est associé à une application qui utilise une relation de la BD, soit directement, soit par lʹentremise dʹune vue relationnelle. La création dʹun trigger exige que lʹutilisateur ait le privilège de créer des triggers et quʹil soit propriétaire des tables auxquelles il se réfère. Autrement, il doit avoir le privilège ALTER ANY TABLE. Une même table ne peut avoir quʹun seul type de trigger, cʹest‐à‐dire quʹil est impossible de définir deux triggers AFTER‐INSERT pour une même table. Toutefois, il peut y avoir plusieurs triggers du type différent par table, cʹest‐à‐dire autant quʹil y a de combinaisons de temps et d’événements.
(BEFORE‐INSERT), (BEFORE‐UPDATE), (BEFORE‐DELETE), (AFTER‐INSERT), (AFTER‐UPDATE) et (AFTER‐DELETE).
Trigger et contrainte dʹintégrité Un trigger est différent dʹune contrainte dʹintégrité définie dans un schéma parce que cette dernière est d’abord une assertion toujours vraie et satisfaite depuis lʹinstant de sa définition dans le schéma. Une contrainte peut être désactivée, mais en redevenant active elle validera toutes les données. Une contrainte dʹintégrité est par défaut toujours active, sauf si elle est désactivée par la clause DISABLE. Lorsquʹune contrainte redevient active, il faut que toutes les données dʹune relation satisfassent la contrainte réactivée. Elle est, par essence, rétrospective. Le trigger est créé à lʹétat actif, mais il peut
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
178
être désactivé ou activé par la suite selon les besoins. Cʹest avec la commande ALTER TRIGGER {Enable|Disable} que le statut du trigger est modifié. Lorsquʹil est réactivé, il ne sʹapplique que sur les données qui seront ajoutées ou modifiées à partir de ce moment. Le trigger ne sʹapplique donc pas rétrospectivement.
Utilité des triggers de BD Leur usage est multiple dans la gestion et la cohérence dʹune BD. Ils servent notamment à calculer les attributs dérivés, à bloquer des transactions invalides, à renforcer la sécurité, à propager les mises à jour dans une BD distribuée, à conserver un audit des accès aux relations pour les statistiques, à implémenter la réplication (ou duplication ) synchrone des tables locales et à contrôler les effets de la dénormalisation de la BD.
Création dʹun trigger de base de données La création dʹun trigger de BD se fait généralement avec lʹéditeur de SQL*PLUS. Ici, il sʹagit du trigger T5 qui est lancé lorsquʹil y a une mise à jour de lʹattribut ville dans la table Usine et que lʹinsertion est postérieure au 25 novembre 1994. Toute mise à jour dʹun autre attribut dans la table Usine ne déclenchera pas le trigger. CREATE TRIGGER D5 AFTER INSERT OF ville ON Usine WHEN (:new.dateEmbauche > To_Date(ʹ25‐11‐1994ʹ,ʹDD‐MM‐YYYYʹ)) DECLARE . . . BEGIN . . . (énoncés SQL ou bloc PL/SQL) EXCEPTION . . . END; . <‐ le point termine lʹédition du bloc composé avec l’outil SQL*Plus.Lancement par la commande SQPLPlus : Run . Sinon, il faut terminer le bloc par une barre oblique droite, soit / . Pour supprimer un trigger du dictionnaire, il suffit de faire la commande suivante :
DROP TRIGGER T5; Pour réaliser cette opération, il faudra que lʹutilisateur soit le DBA ou encore quʹil ait les droits de suppression des triggers.
Structure dʹun trigger ECA Une structure de trigger est composée de trois parties : ‐ Un événement déclencheur (E) : une action externe sur une table ou sur un tuple qui déclenche le trigger; par exemple, lʹajout ou la suppression dʹun ou de plusieurs tuples. ‐ Une condition de déclenchement (C) : cʹest une expression booléenne qui doit être évaluée à TRUE pour que le trigger se déclenche, sinon son déclenchement est inhibé; ‐Une action du trigger (A) : cʹest une procédure PL/SQL (cʹest‐à‐dire un bloc PL/SQL anonyme ou pas, qui ne contient pas un commit, rollback ou savepoint).
CREATE OR REPLACE TRIGGER nom_trigger AFTER INSERT OR UPDATE OF attribut ON table WHEN (condition de déclenchement )
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
179
DECLARE . . . BEGIN (actions avec les données) EXCEPTION . . . END; /
Figure 8.29 Le trigger de table ne se déclenchera quʹaprès la fin dʹun ordre SQL dʹajout ou de mise à jour dʹun ou de plusieurs tuples. Le trigger de table est caractérisé par lʹabsence de FOR EACH ROW. Avant de lancer les actions du trigger, il y aura une vérification supplémentaire dʹune condition de déclenchement dont la formulation repose sur un ou plusieurs attributs du ou des tuples mis à jour.
Succès ou échec dʹun trigger de BD Lorsquʹun trigger est lancé sur le serveur et qui se termine sans traitement dʹexception, il est alors exécuté avec succès et lʹévénement qui lʹa déclenché se poursuit correctement. Sʹil sʹagissait dʹune insertion, cela veut dire quʹelle est complétée. Dans le cas contraire, une exception est activée et éventuellement traitée (autrement que par une action NULL) et lʹaction à lʹorigine du déclenchement du trigger est automatiquement défaite (équivalent au ROLLBACK) par le SGBD. Le ROLLBACK est fait par la procédure RAISE_APPLICATION_ERROR() du package DMS_STANDARD. À noter quʹil ne peut pas y avoir un ROLLBACK explicite dans le bloc dʹun trigger.
Type du trigger de BD (portée du trigger) Deux types de triggers peuvent être définis en ce qui concerne la BD : Trigger dʹénoncé (DML statement trigger) : cʹest un trigger lancé une seule fois lors de lʹexécution de lʹénoncé DML qui touche une ou plusieurs tables. Ce type de trigger est utile lorsque la nature de lʹaction à exécuter ne dépend pas dʹune valeur dʹattribut du tuple ajouté ou qui est déjà présent. Par exemple, lorsquʹil sʹagit de calculer une nouvelle moyenne sur un attribut de la table T dans laquelle il y a des ajouts effectués par un seul INSERT, il suffit de créer un trigger d’énoncé AFTER INSERT pour que lʹévénement de fin d’insertion déclenche le calcul de la nouvelle moyenne. Trigger de tuple (row trigger) : il sʹagit dʹun trigger exécuté autant de fois quʹil y a de tuples à insérer, à modifier ou à supprimer dans une table par un DML (FOR EACH ROW). Dans ce cas, la nature de lʹaction du trigger dépend de la valeur dʹun attribut de chaque tuple inséré, modifié ou supprimé. Un tel trigger peut utiliser les variables :new et :old pour référer à la nouvelle ou à l’ancienne valeur. Un trigger peut être activé au temps BEFORE ou AFTER lʹoccurrence de lʹévénement associé au DML. Si un trigger AFTER échoue et que lʹexception nʹest pas traitée, alors il y a un rollback automatique pour annuler toutes les actions faites sur la table ou sur le tuple par lʹordre DML. Voici deux exemples de création dʹun trigger :
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
180
CREATE TRIGGER TR2 AFTER INSERT ON Usines FOR EACH ROW BEGIN ... . . . bloc PL/SQL EXCEPTION . . . END; CREATE TRIGGER TR1 BEFORE INSERT OR DELETE ON Usines BEGIN ... bloc PL/SQL END;
Figure 8.30
Usages des triggers événementiels Dans le cas dʹun trigger du type BEFORE, lʹaction de ce trigger est exécutée, soit avant lʹexécution dʹune action sur une table, soit (avant lʹaction) sur chaque tuple dʹune table. Il est utile pour calculer une (ou des) valeur à partir des tuples existants. La valeur calculée sera par la suite ajoutée dans lʹinsertion à compléter dans la table. Ce type de trigger sera aussi utilisé pour vérifier si lʹaction du DML sur la table doit être amorcée ou complétée de façon à éviter de faire un rollback après quʹune action aura été exécutée. Exemple : Une transaction est inscrite dans la table Contrats si le numéro du certificat dʹautorisation correspond au montant du contrat. Le déclenchement se fera pour chaque ajout ou modification dʹun tuple dans la table Contrats :
CREATE TRIGGER Verif BEFORE INSERT OR UPDATE ON Contrats FOR EACH ROW DECLARE no_certif_v real; montant_v number (5,2); annulation EXCEPTION; confirmation EXCEPTION; BEGIN SELECT montant INTO montant_v FROM Certificats WHERE no_certif = :new.no_certif; if montant_v != :new.montant THEN RAISE annulation; else RAISE confirmation; end if; EXCEPTION When annulation THEN Raise_Application_Error(‐20523,ʹautorisation inadéquateʹ);
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
181
When confirmation THEN NULL;‐‐ action complétée When NO_DATA_FOUND THEN NULL; ‐‐exception prédéfinie When OTHERS THEN Raise_Application_Error(‐20600,ʹerreur non traitéeʹ); END;
Modèle dʹexécution des triggers dʹune BD Comme une BD a plusieurs relations et que chacune peut avoir plusieurs triggers, le SGBD doit avoir un protocole dʹexécution des divers triggers qui peuvent être associés au même événement et quant à la même table. Voici le modèle de traitement des triggers par Oracle : 1‐ Début exécution du trigger BEFORE de table (1er tuple à traiter); 2‐ Pour chaque tuple traité par lʹordre ci‐dessus 2.1 exécution, sʹil est présent, du trigger BEFORE de tuple; 2.2 verrouillage de chaque tuple et vérification des contraintes dʹintégrité (verrou maintenu jusquʹà la fin de la transaction); 2.3 exécution, si présent, du trigger de tuple AFTER; 3‐ Exécution de toute contrainte dʹintégrité différée; 4‐ Exécution du trigger AFTER de table; Si une des étapes échoue, le ROLLBACK est implicite (donc automatique) lorsquʹil sʹagit dʹun trigger de BD et une exception est communiquée à lʹenvironnement du client. Lʹexécution dʹun trigger BEFORE peut entraîner celle dʹun autre trigger AFTER qui sera déclenché lors de la référence par le trigger original BEFORE. Le dernier trigger lancé est celui de table AFTER et suit la vérification des contraintes dʹintégrité définies par le CHECK.
Activation dʹun trigger Par défaut et juste après sa spécification par le DBA, un trigger est actif. Il peut cependant être désactivé par la suite avec la commande ALTER TRIGGER DISABLE :
ALTER TRIGGER VERIFMODEL DISABLE ; ALTER TRIGGER VERIFMODEL ENABLE;
Variables de corrélation NEW et OLD Avec un trigger de tuples, le dernier tuple touché par un énoncé DML est désigné par deux variables de tuple appelées respectivement :NEW et :OLD. Ces variables sont dites de corrélation parce quʹelles permettent de se reporter au tuple courant dans son ancien état ou dans son nouvel état. La nouvelle valeur (new) qui affecte un attribut dʹun tuple (soit new[.attribut] et lʹancienne (soit old[.attribut]) sont référencées respectivement par :new.quantite_vendue et :old.quantite_vendue.
Insert INTO Transporteur (mat, nom, vol) VALUES (‘f8’, ‘ailes du nord’, 25) ;
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
182
Figure 8.32 Les deux points (:) préfixe une variable de corrélation, ils sont une directive au compilateur de PL/SQL indiquant que la variable tuple :new nʹest pas déclarée dans le bloc PL/SQL et quʹil ne faut pas chercher à la traduire puisquʹelle correspond à une des deux variables de tuple gérées par le système. Toutefois, tout usage de lʹune de ces variables de corrélation à lʹintérieur dʹun bloc PL/SQL exige le préfixage par les deux points (:). CREATE OR REPLACE TRIGGER valid_qte AFTER UPDATE OF qte_V ON
INVENTAIRE FOR EACH ROW DECLARE ERREUR1 exception; BEGIN IF :NEW.qte_v > :OLD.qte_v THEN RAISE ERREUR1; END IF; EXCEPTION WHEN ERREUR1 THEN RAISE_APPLICATION_ERROR (‐20001, ʹerreur: nouvelle quantité plus grande que la précédenteʹ); END ;
Ce trigger est exécuté autant de fois quʹil y a de tuples mis à jour dans la table Inventaire par lʹénoncé de mise à jour associé. Si le trigger échoue , cʹest‐à‐dire si une exception nʹest pas traitée correctement ou si le bloc lance un appel à la procédure stockée RAISE_APPLICATION_ERROR( ), la transaction est lʹobjet dʹun rollback automatique et le contrôle est passé à la procédure appelée et, finalement, à lʹenvironnement appelant, notamment SQL*PLUS ou SQLFORMS ou à une application C. La procédure stockée RAISE_APPLICATION_ERROR() a deux arguments : un nombre négatif entre ‐20 000 et ‐20 999 et une chaîne de caractères dont la longueur est inférieure à 512 octets. Le rôle de cette procédure est de passer à lʹenvironnement appelant (ex. Developer 2000 et L3G) un numéro dʹerreur et un message de manière à les afficher pour lʹutilisateur. Ainsi, si une application SQLFORMS effectue une mise à jour incorrecte, lʹaction liée à la BD sera défaite par le trigger valid_qte et le numéro dʹerreur et le message seront interceptés et traités correctement à la façon de SQLFORMS.
:new
‘f8’, ‘ailes du nord’, 25Table Transporteur : ‘f8’, ‘ailes du nord’, 25
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
183
Validation dʹun attribut de tuple en insertion Pour valider le fait que la valeur dʹun attribut doit avoir une valeur préexistante dans la BD, il suffira de définir un trigger BEFORE FOR EACH ROW. Par exemple, le tuple à insérer doit utiliser un patronyme absent dans la BD, sinon le tuple nʹest pas accepté .
CREATE OR REPLACE TRIGGER TRIG_INSERT5 BEFORE INSERT OF nom ON Patient FOR EACH ROW DECLARE nbpat integer; BEGIN SELECT count(*) INTO nbpat FROM Patient WHERE nom = :new.nom; ‐‐si 0 alors nom absent IF nbpat = 0 THEN NULL else RAISE_APPLICATION_ERROR ( ‐20249, ʹpatient existant dans la BDʹ); END IF ; END ;
8.20 Problème et résolution de la mutation des tables (Oracle) Voici une base de données simple formée par deux tables Employe et Projet entre lesquelles il y a une association matérialisée par une contrainte référentielle. Un trigger défini sur une table et qui est lancé suite à une mise à jour ou à un INSERT sur la dite table génère une erreur de table en mutation. La seule façon de corriger ce problème est de passer par un package et de créer une variable globale au niveau du serveur. Exemple :
Employe(nas*, nom, classe) Projet (noP*, site, budget, nas)
Create table Employe(
nas integer primary key, nom varchar2(40) not null, classe char(1) not null);
CREATE table Projet(
noP integer primary key not null, site varchar2(40) not null, budget number(7,2) not null, nas integer, constraint fk_ProjetEmploye Foreign key (nas) References Employe(nas));
SQL> SELECT * FROM Employe;
nas nom classe
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
184
1 Gagné A 2 Michaud A 3 Landry A
noP site budget nas 100 Québec 20000 1 101 Québec 40000 2 102 Montréal 5000 3
Il y a une contrainte de mise à jour définie sur la table projet qui limite la somme totale des budgets des projets en cours au même site à 90 000.00$. La seule définition d’un trigger BEFORE Update sur la table Projet génère une erreur de table en mutation en raison de la portée du trigger qui doit vérifier que les projets de Québec et pour cela utiliser la variable de tuples :NEW.
Create or Replace trigger ProjetQcLim BEFORE Update on Projet for each row Declare total number(8,2); Begin Select Sum(budget) INTO total FROM Projet Where site = ʹQuébecʹ; if total + :NEW.budget > 90000.00 THEN Raise_Application_Error(‐20600,ʹBuget total dépassé pour Québecʹ); end if; end;
Déclencheur créé.
SQL> UPDATE Projet set budget = 20000.00 WHERE no_p= 100; ERREUR à la ligne 1 : ORA‐04091: table AGAMACHE.PROJET en mutation,déclencheur/fonction ne peut la voir ORA‐06512: à ʺAGAMACHE.PROJETQcLIMʺ, ligne 4 ORA‐04088:erreur lors dʹexécution du déclencheur ʹAGAMACHE.PROJETQCLIMʹ SELECT * FROM projet;
noP site budget nas 100 Québec 20000 1 101 Québec 40000 2 102 Montréal 5000 3
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
185
Aucune mise à jour effectuée. Pour corriger cette situation et pouvoir effectuer la mise à jour, il suffit d’utiliser une variable globale définie dans un package qui sera par la suite rangé au niveau du serveur. /* création de la variable globale via un package */ Create package Var_globale as v_site varchar2(40); end; /* initialisation de la variable globale */ Create or Replace trigger initSite BEFORE UPDATE on Projet for each row BEGIN var_globale.v_site := :NEW.site; Exception When NO_DATA_FOUND THEN Raise_Application_Error(‐20500, ʹpas de projet à Qcʹ); end; /* vérification du dépassement de la limite pour un même site */ Create OR Replace trigger controle_budget AFTER UPDATE on Projet Declare v_total number(8,2); BEGIN Select sum(budget) INTO v_total From Projet Where site = var_globale.v_site; if v_total > 90000.00 THEN Raise_Application_Error(‐20300, ʹpas de MAJʹ); end if; end; ‐‐ Mise à jour du budget du projet no_p = 100
Update Projet set budget = 60000.00 WHERE no_p= 100;
Mise à jour refusée par le trigger :
nop site budget nas 100 Québec 20000 1 101 Québec 40000 2 102 Montréal 5000 3
La transaction est annulée automatiquement et les modifications effectuées sont défaites par le gestionnaire de transaction.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
186
8.21 Trigger INSTEAD OF Si la définition d’une vue comporte une jointure, elle ne peut pas être mise à jour par les ordres Insert, Update et Delete. Il est possible de contourner cette difficulté au moyen du trigger Instead Of qui vient à la rescousse de la vue et fournir une procédure de remplacement capable de faire le travail autrement impossible. Pour ce faire, le nouveau trigger doit faire directement et précisément sur les tables de base les multiples actions sous‐tendues par l’ordre d’origine sur la vue. Par exemple l’insertion d’un tuple au moyen d’une vue définie par une jointure est rendue possible par le noyau en interceptant la mise à jour sur la vue et en effectuant les insertions appropriées sur les deux tables référées par la vue. Create table Projet (
noP number(4) primary key, pDesc varchar2(40)); Create table Empl( mat number(4) primary key, nom varchar2(40), salaire number (7,2), noP number(4), constraint fk_Projet foreign key (no_p) references Projet (noP)) ;
La vue définie avec la jointure est la suivante :
Create View v_E_P as Select E.mat, E.nom, P.no_p, P.p_desc From Projet P, Empl E Where P.noP = E.noP;
Voici une insertion effectuée à travers la vue v_E_P : Insert INTO v_E_P VALUES (50,‘Legendre’, 625, ‘colliger des stats’) ; (dml de l’application) À l’exécution, une erreur sera signalée pour indiquer l’impossibilité de faire directement cette insertion via la vue relationnelle. Par contre, une telle mise à jour peut être faite par le trigger Instead Of associé à la vue relationnelle v_E_P. Create or REPLACE trigger inst_emp_projet_INSERT instead of Insert on v_E_P for each row
Declare c number;
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
187
Begin SELECT count(*) INTO C ‐‐ absence du tuple à ajouter ? FROM Empl Where Empl.mat = :NEW.mat; if c = 0 THEN Insert INTO Empl ( mat, nom) VALUES ( :NEW.mat, :NEW.nom); end if; Select count(*) INTO c FROM Projet ; if c = 0 THEN Insert INTO Projet (noP, pDesc) VALUES (:NEW.no_p, :NEW.pDesc); end if; end;
SQL> Insert INTO v_E_P VALUES ( 50, ʹLegendreʹ, 625, ʹcolliger des statsʹ) ; 1 row created. – 1 tuple par la vue, mais 2 tuples par le trigger. SQL> SELECT * FROM empl; MAT NOM SALAIRE NO_P ‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐ 50 Legendre SQL> SELECT * FROM projet;
NOP PDESC ‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ 625 colliger des stats
En résumé, le trigger Instead Of permet de contourner la difficulté soulevée par la vue relationnelle définie avec une jointure et cela, en fournissant au noyau du SGBD les règles précises pour répercuter l’opération sur les tables de base. 8.22 Générateur de nombres séquentiels
Le déclencheur est souvent utilisé pour générer un nombre séquentiel qui est souvent utile aux applications. Ce générateur monotone avec mémoire permet, par exemple, de numéroter séquentiellement des pièces, des commandes et des transactions. Le nombre est généré à partir dʹune valeur initiale et sa valeur croît à chaque émission jusquʹà une limite supérieure. Le mécanisme utilisé par Oracle pour générer un nombre séquentiel se comporte comme une fonction, avec une mémoire créée avec un nom particulier fourni par lʹapplication qui active le générateur. Elle peut être vue comme une pseudo‐table à un tuple, dotée de pseudo‐colonnes, notamment la NEXTVAL. La pseudo‐table ne peut
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
188
pas être référencée directement dans la sous‐clause FROM. A chaque référence, le générateur fournit la valeur suivante sans utiliser de verrou. Ce générateur est très utile dans un trigger pour générer automatiquement un numéro de lot, ou de facture ou encore un numéro dʹordre. La séquence est créée par la commande suivante : CREATE SEQUENCE nbseq INCREMENT BY 1 start WITH 1 MAXVALUE 999; Le générateur est un objet de la base qui peut être partagé pour autant que le propriétaire accorde les droits dʹaccès aux autres utilisateurs.
GRANT SELECT on nbseq to utilisateur5;
Le générateur peut être supprimé par la commande : DROP SEQUENCE nbseq;
Exploitation concurrente du même générateur par deux applications La pseudo‐colonne CURRVAL fournit toujours la dernière valeur générée et obtenue suite à la dernière requête effectuée par cette application indépendamment des autres. Un inconvénient de ce type de génération est la perte éventuelle dʹun entier lorsque lʹapplication qui lʹa demandé ne lʹutilise pas. En perdant ce nombre, il y a des trous dans la suite des entiers stockés dans la BD.
Figure 8.33
Application A : Select nbseq.NEXTVAL Into nb; Select nbseq.CURVAL Into nb; Select nbseq.NEXTVAL Into nb;
43
Dictionnaire Nbseq 42
99‐
Dernière valeur générée
43
Application B : Select nbseq.NEXTVAL Into val;
4445
1
2
3
4
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
189
Privilège pour la création dʹun générateur La création dʹune séquence est possible si lʹutilisateur a le privilège CREATE sequence. La séquence correspond à une table dont la définition est rangée dans le dictionnaire et dont les données sont chargées dans lʹespace de table SYSTEM. Pour obtenir une valeur de la séquence et lʹaffecter à une variable maph (matricule du patient hospitalisé), il suffit dʹexécuter dans un trigger lʹénoncé SQL ci‐dessous qui fait référence à un autre séquenceur nommé, par exemple, nbseq. Pour générer un nombre et le ranger dans une variable ou dans un attribut du tuple courant, on utilisera la clause SQL suivante :
SELECT nbseq.NEXTVAL INTO :new.maph FROM Dual; Lʹinsertion de la valeur est faite dans le tuple courant (:new), quelle que soit la table ciblée par lʹinsertion, et écrase toute autre valeur préalablement affectée à lʹattribut maph. Il est aussi possible dʹobtenir la valeur courante du générateur par lʹattribut CURRVAL.
SELECT nbseq2.CURRVAL INTO :new.maph FROM Dual;
Stockage des triggers dans le dictionnaire Les triggers de BD spécifiés par lʹadministrateur de la BD sont rangés dans une table du dictionnaire (table USER_TRIGGERS) dont le schéma est donné ci‐dessous. Un trigger peut être supprimé par son propriétaire ou par le DBA qui a le privilège DELETE ANY TRIGGER.
Schéma de la table USER_TRIGGERS du dictionnaire TRIGGER_NAME NOT NULL VARCHAR2(30) TRIGGER TYPE VARCHAR2(16) TRIGGERING_EVENT VARCHAR2(26) TABLE_OWNER NOT NULL VARCHAR2(30) TABLE_NAME NOT NULL VARCHAR2(30) REFERENCING_NAMES VARCHAR2(87) WHEN_CLAUSE VARCHAR2(2000) STATUS VARCHAR2(8) DESCRIPTION VARCHAR2(2000) TRIGGER_BODY LONG
Figure 8.34 Ainsi, pour connaître le corps du trigger tr34, son propriétaire peut interroger une table du dictionnaire de données à laquelle il a accès.
SELECT TRIGGER_BODY FROM USER_TRIGGERS WHERE trigger_name= ʹtr34ʹ;
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
190
Prédicats spéciaux pour les triggers de BD Lorsquʹun trigger est déclenché, il est possible de connaître la nature de lʹordre DML associé de même que lʹattribut visé par lʹaction. Prédicat Prédicat(attribut) Evaluation INSERTING Inserting(nom) IF Inserting(nom) THEN … Vrai si le DML déclencheur est un Insert. UPDATING Updating(Salaire) Vrai si le salaire est modifié DELETING Vrai si le tuple est l’objet d’une suppression dans la table. Ces prédicats spéciaux simplifient la programmation des triggers en permettant de faire un test sur la nature de lʹopération en cours. Soit le trigger Trig_ajout qui lance une insertion suite à un ajout fait dans la table Inscription.
CREATE OR REPLACE TRIGGER TRIG_AJOUT AFTER INSERT OR UPDATE OF Statut ON Inscription FOR EACH ROW BEGIN /* traitement dʹune nouvelle inscription dans la table du même nom lorsquʹil sʹagit dʹune insertion */ IF INSERTING THEN UPDATE TABLE_INSC SET nbInsc = nbInsc + 1 WHERE TableIns.code = :new.code; END IF; END;
À noter que le trigger est associé à plusieurs ordres DML réunis dans une condition logique disjonctive : INSERT OR UPDATE.
Privilèges pour la création dʹun trigger Un utilisateur peut créer un trigger dans son schéma sʹil a le privilège système approprié : CREATE TRIGGER. De plus, il pourrait créer un trigger dans un autre schéma appartenant à un autre compte, sʹil avait le privilège système CREATE ANY TRIGGER.
Modification de la définition dʹun trigger de BD La spécification du corps dʹun trigger ne peut pas être modifiée; elle doit être remplacée ou supprimée et finalement créée à nouveau. Si lʹon supprime un trigger par la commande DROP TRIGGER nom_trigger , il y a aussi suppression de tous les grants ou droits accordés à ce trigger. Par la suite, après la création du trigger modifié, il faudra accorder à nouveau les privilèges au trigger. Pour éviter de perdre les grants du trigger à remplacer, il faut utiliser lʹoption REPLACE du CREATE ou encore utiliser CREATE OR REPLACE.
CREATE OR REPLACE TRIGGER TR60 AFTER UPDATE OF Qte_d ON Production FOR EACH ROW
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
191
DECLARE . . . BEGIN . . . EXCEPTION . . . END; . <‐‐ le point pour quitter lʹéditeur de PL/SQL ou un / pour quitter et exécuter le trigger
Certains paramètres dʹun trigger peuvent être changés après la création. Ainsi, lʹactivation ou la désactivation dʹun trigger particulier ou de tous les triggers dʹune table est possible par la commande ALTER :
ALTER TRIGGER TR35 ENABLE; ALTER TABLE USINES DISABLE ALL TRIGGERS;
Voici un trigger plus complexe qui vérifie la clé étrangère dʹune relation ATELIER chaque fois quʹil y a ajout ou modification dʹun tuple dans la table ATELIER. La vérification de la clé est faite pour valider une contrainte dʹintégrité spécifiée dans le schéma de la relation. Lʹusage dʹun curseur dans ce trigger doit se faire avec un paramètre parce que lʹexécution du SELECT ne garantit pas le retour dʹun seul tuple de la BD. Normalement, si le prédicat nʹutilise pas une clé primaire, il y aura plusieurs tuples dans la réponse. Dans un tel cas, les tuples de la réponse sont logés dans un espace associé à un curseur Oracle créé dans l’espace mémoire du serveur. Les tuples sont ramenés successivement à la station client par un ordre FETCH. En ce faisant, une ou des variables locales au bloc PL/SQL sont utilisées pour référer aux valeurs des attributs.
Create or REPLACE trigger valid_clef_etrang AFTER INSERT or UPDATE on ATELIER FOR EACH ROW WHEN (new.noUsine is not NULL) /*condition supplémentaire de déclenchement du trigger */ /* avant lʹinsertion ou la modification dʹun tuple concernant un atelier, il y a une validation de sa clé étrangère, soit son noUsine dans la table parent USINES. */ DECLARE v_noAtel integer; atelier_invalide exception; atelier_valide exception; erreur_piegee exception; PRAGMA EXCEPTION_INIT (erreur_piegee, ‐4091); /*erreur interne Oracle traitée par lʹapplication via lʹexception erreur_piegee) dn est le paramètre du curseur*/
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
192
CURSOR curs_temp (dn NUMBER) IS SELECT noAtelier FROM USINES WHERE noAtelier = dn FOR UPDATE;
BEGIN OPEN curs_temp (:new.no_atelier); /* du tuple qui a déclenché le trigger */ FETCH curs_temp INTO v_noAtel; IF curs_temp%NOTFOUND THEN RAISE atelier_invalide; ELSE RAISE atelier_valide; END IF; EXCEPTION WHEN atelier_invalide THEN RAISE_application_error( ‐20010, ʹno atelier inexistantʹ); CLOSE curs_temp; WHEN atelier_valide THEN CLOSE curs_temp; WHEN erreur_piegee THEN NULL;/* erreur et rien faire */ WHEN OTHERS THEN Message(ʹautre erreurʹ); END;
Figure 8.35
Trigger pour la station client Oracle (Developer/2000) En sus des triggers définis au niveau de la BD, il est fort utile aussi de pouvoir spécifier certains triggers au niveau du logiciel client. Dans un tel cas, le trigger sʹexécute immédiatement dès lors que survient lʹévénement déclencheur chez le client, et non en différé comme cʹest le cas pour certains triggers de la BD qui ne se déclenchent quʹau moment du COMMIT de la transaction en cours. Pour certaines applications client‐serveur, les triggers de BD peuvent faire double emploi, si un trigger identique a déjà été exécuté dans la station. Lʹassurance que le trigger du client sera toujours exécuté avec la même spécification ne relève pas normalement de la compétence du DBA.
Signature et trigger client Il serait possible de mettre à contribution une condition de déclenchement préalable (clause WHEN) pour éviter dʹexécuter un trigger de BD, si une variable dʹétat représente la garantie (relative cependant) que le trigger correspondant a été exécuté par la station client. Une autre façon serait de calculer une signature (par exemple, celle de type DES ou RSA) pour chaque trigger dʹune application et de les stocker au niveau du serveur. Chaque fois quʹune application demande une action dʹinsertion ou de modification dans la BD, elle transmet aussi la signature que le client calcule à partir de la version actuelle du trigger de la station. Le serveur peut donc la comparer avec celle qui est stockée et, sʹil y a une différence, le trigger de la BD sera exécuté. Ainsi, toute validation faite au niveau de la station par un trigger identique à celui du serveur ne sera pas refaite
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
193
inutilement. Bien entendu, tous les triggers ne peuvent pas être implémentés de cette façon, car certains sont propres à la BD et devront nécessairement être exécutés uniquement par le serveur. Voici un autre exemple dʹun trigger de validation plus simple qui nʹutilise pas un curseur, mais plutôt la variable de corrélation new.attribut pour se référer à la nouvelle valeur de lʹattribut COULEUR dont le type est VARCHAR2 :
CREATE TRIGGER TRIGCOUL AFTER INSERT ON TPLAN FOR EACH ROW DECLARE couleur_valide exception; BEGIN IF :new.COULEUR = ʹrougeʹ THEN RAISE couleur_valide; ELSIF :new.COULEUR = ʹblancʹ THEN RAISE couleur_valide; ELSE RAISE_APPLICATION_ERROR (‐20401,ʹcouleur invalideʹ); END IF; EXCEPTION WHEN couleur_valide THEN NULL ; WHEN OTHERS THEN Message(ʹautre erreurʹ); END;
Figure 8.36
8.23 Synonyme
Les différents objets gérés par le SGBD sont identifiés par des noms qui peuvent être plus ou moins complexes. Par exemple, une table pour les transactions de vente de la première campagne de publicité faite par lʹagence Martin au cours du deuxième trimestre 96 est nommée Ventes_pub_Martin.trim2_1996. Il est possible dʹassocier à ce nom un synonyme plus court et peut être plus significatif pour les utilisateurs comme par exemple Martin2_96. Avec la définition de ce synonyme rangé dans le dictionnaire, il devient possible de référer à la table source par lʹun ou lʹautre des noms.
CREATE SYNONYM Martin2_96 FOR Ventes_pub_Martin.trim2_1996; Ce synonyme est rendu disponible automatiquement à tout utilisateur qui entre en ligne avec le numéro de compte de la personne qui lʹa créé. Le DBA a lʹautorisation de créer de tels synonymes qui sont privés par défaut, i.e. que leur portée est limitée à un schéma. Au contraire, un synonyme est créé public pour quʹil soit accessible à tous les schémas. Un synonyme PUBLIC est créé par la commande CREATE PUBLIC SYNONYM. CREATE [PUBLIC] SYNONYM promotion97 FOR laval_trim3_1997;
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
194
Tout objet de la BD peut être renommé par un synonyme afin dʹen faciliter la référence, notamment la table, la vue, le lien de BD distante, lʹindex et la procédure. Les synonymes qualifiés de PUBLIC sont accessibles à toutes les applications. Le mécanisme des synonymes est un moyen simple et efficace de rendre des changements dans les métadonnées transparents aux applications et de simplifier la formulation des requêtes DML pour les utilisateurs. Ainsi, tout changement au niveau du dictionnaire qui consiste à changer le nom des tables, des index, des séquences…, exigera par voix de conséquence un changement du nom de lʹobjet dans toutes les applications opérationnelles. Par contre, si les applications utilisent des synonymes pour référer aux tables, aux index..., les changements de nom des objets de la base nʹont plus dʹimpacts sur les applications. 8.24 Droits d’accès et compte‐utilisateur
La sécurité dʹune base de données concerne est assurée en premier par un contrôle rigoureux des accès aux différents objets dʹune base de données. Une base, rappelons‐le, comprend des objets que sont les tables, les espaces de données, les vues, les triggers, le schéma, les synonymes, les liens entre les bases, les rôles, les comptes‐utilisateurs, les séquences, les procédures et les packages. Le DBA est celui qui possède tous les privilèges dès la création de la base. Cʹest à sa guise que certains privilèges sont propagés aux utilisateurs selon leurs besoins et que dʹautres, comme celui de créer des comptes lui demeurent exclusifs. Il y a des privilèges de système et des privilèges dʹobjet.
Création d’un compte‐utilisateur Le premier niveau de sécurité est celui de la création du compte pour entrer en ligne avec lʹinstance du SGBD. Cette communication doit être authentifiée par un mot de passe fourni par lʹutilisateur et vérifié par le SGBD ou le système dʹexploitation hôte. La création des comptes est faite par le DBA soit avec lʹutilitaire SQLDBA, soit directement avec la commande CREATE. Par exemple :
CREATE USER jbouchard IDENTIFIED BY qc1998fre; Par défaut, cʹest le serveur qui vérifie la validité du mot de passe en consultant une table du dictionnaire réservée aux mots de passe. Dʹautre part, la vérification peut être prise en charge par le système dʹexploitation hôte qui assure quʹun numéro de compte pour utiliser le SGBD est aussi protégé par le même mot de passe que celui qui protège le compte du système dʹexploitation :
CREATE USER OPS$jbouchard IDENTIFIED EXTERNALLY;
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
195
La sécurité peut aussi être augmentée pour protéger les communications entre les clients et le serveur. Il est possible de spécifier au serveur de recevoir et de déchiffrer les mots de passe de chaque client.
Suppression dʹun compte Le DBA ou un compte qui possède le privilège de supprimer un compte peut enlever lʹaccès à la BD à un compte tout en conservant ou non les différents objets que ce compte a créés lors des sessions de travail antérieures. Exemples : Cas 1 : Supprimer un utilisateur du SGBD inscrit dans le dictionnaire
DROP USER jbouchard; Cette commande inhibera lʹaccès à la BD pour ce compte si ce dernier nʹa pas créé dʹobjets dans la BD. Dans le cas contraire, la commande ne pourra pas supprimer le compte. Cas 2 : Supprimer un compte qui a déjà créé des objets comme un schéma, des tables, des triggers, ... La suppression d’un utilisateur entraîne aussi celle des ses objets créés.
DROP USER jbouchard CASCADE; De cette façon, tous les objets qui appartiennent au schéma de cet utilisateur sont aussi supprimés de la base de données. Cas 3 : Suppression de la capacité dʹun compte à communiquer avec le SGBD sans détruire les objets de son schéma. Pour ce faire, il suffit de lui retirer son privilège de création dʹune session.
REVOKE CREATE SESSION FROM jbouchard; Le privilège CREATE SESSION est essentiel à un compte utilisateur pour entrer en communication avec la BD. Un utilisateur pourrait avoir un mot de passe pour entrer en ligne avec le SGBD, mais ne pas pouvoir exploiter la BD parce quʹil nʹa pas le privilège de créer une session. Un privilège de système est le droit de créer, de supprimer ou de modifier un objet qui est associé à lʹusage des ressources du système nécessaires pour le bon fonctionnement du SGBD. Parmi ces nombreux privilèges de système, il y a notamment les suivants : Création de tout cluster quelconque (ANY) Création dʹun lien de BD Modification de tout index Affectation de tout privilège Suppression de toute procédure Création de tout rôle Création de tout profil, fonction, ... Création dʹun utilisateur
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
196
Création et exploitation de séquence s Création dʹun espace de données Création de tout synonyme Création dʹun index Création d’une session Création de tables Les privilèges du type objet concernent les opérations qui sont autorisées quant aux objets du schéma. Ils sont aussi très nombreux. En général, ces privilèges sont associés aux opérations dʹinsertion, dʹajout, de suppression et de modification dʹun objet du schéma. Par exemple, pour que lʹutilisateur util5 puisse créer une table Usines, il doit avoir le privilège de créer son schéma et dʹy créer cette table. De plus, si le même utilisateur veut insérer des tuples, il devra avoir le privilège dʹinsertion et naturellement celui de suppression. Le propriétaire dʹun objet a automatiquement tous les droits sur cet objet. Il peut donc ajouter, supprimer des tuples et finalement supprimer la table. Pour que lʹutilisateur util8 ait aussi les droits dʹinsérer des tuples dans la table créée par util5, il nʹa pas besoin dʹavoir le privilège INSERT ANY TABLE, mais seulement celui dʹinsertion dans la table Usines, que le propriétaire peut lui accorder par la commande GRANT.
Ressources du système pour un utilisateur : notion de PROFILE Lorsquʹun utilisateur a les privilèges nécessaires pour exploiter une BD, il peut le faire sans limitation des ressources utilisées. Par exemple, lʹutilisateur peut rester en ligne un temps illimité ou être limité à un temps borné. Il pourra créer autant de tuples quʹil veut sans contrainte dʹespace ou être limité à un espace sur disque prédéfini. Ces contraintes sont spécifiées dans un profil associé à un utilisateur lors de la création de son compte. CREATE PROFILE usager_profil5 LIMIT
CONNECT_TIME 50 IDLE_TIME 10; CREATE USER jbouchard IDENTIFIED BY qc97fre PROFILE usager_profil5;
Si lʹutilisateur est déjà créé, il est possible dʹajouter le profil avec la commande ALTER.
ALTER USER jbouchard PROFILE usager_profil5; Il y a un profil par défaut qui est celui du serveur et qui est associé à tout utilisateur par défaut. Ce profil nʹest pas contraignant puisquʹil spécifie la valeur UNLIMITED pour les ressources qui nʹont pas été redéfinies dans un profil dʹutilisateur.
ALTER PROFILE usager_profil5;
Propagation des privilèges aux utilisateurs Si le seul créateur de tables dans le schéma est le DBA, il est alors le seul à pouvoir accorder des privilèges aux autres utilisateurs pour leur permettre de lire, dʹaugmenter, de supprimer et de modifier lʹextension dʹune table.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
197
GRANT SELECT (no_us, ville) ON Usines TO util6, util7; GRANT DELETE ON Usines TO util7;
Dans lʹexemple qui précède, les utilisateurs util6 et util7 peuvent dorénavant accéder à la table Usines et y faire des sélections avec nʹimporte quel attribut de la table pour obtenir les tuples. Toutefois, ces utilisateurs ne peuvent pas transmettre leur privilège à un autre, sauf si le privilège quʹils ont reçu est assorti de la clause WITH GRANT OPTION. Lorsquʹil sʹagit dʹun privilège de système, la clause WITH ADMIN OPTION accorde la même autorisation de transmettre :
GRANT SELECT ON Usines TO util6, util7 WITH GRANT OPTION; Il est aussi possible de limiter les attributs visibles pour une action liée à une table. Par exemple, supposons que lʹutilisateur util9 a les droits de lecture dʹune table Usines, mais que son droit de mise à jour est limité à celui dʹun attribut, soit la capacité. Dans ce cas, le grant spécifie lʹattribut qui peut être ciblé par la mise à jour :
GRANT SELECT, UPDATE(capacite) ON Usines TO util9; Le même résultat est obtenu en définissant une vue, Vue_capacite:
CREATE VIEW Vue_capacite AS SELECT capacite FROM Usines; Afin de permettre, la sélection et la mise à jour de la table Usines par lʹentremise de la vue, il faut accorder les privilèges SELECT et UPDATE à la vue.
GRANT SELECT, UPDATE ON Vue_capacite WITH CHECK OPTION; Lʹoption CHECK permet de limiter les mises à jour aux tuples qui sont visibles à travers la vue, cʹest‐à‐dire aux usines de la ville de Québec. De plus, lʹutilisateur ne pourra pas ajouter un tuple qui ne serait pas visible par la suite par lʹentremise de la même vue, comme lʹajout dʹune usine localisée à ʺMontréalʺ. Sans la clause CHECK OPTION, cet ajout serait possible.
Révocation des privilèges À tout moment, le DBA ou le propriétaire du compte qui a accordé le privilège peut le supprimer par la commande REVOKE. Supposons que lʹutilisateur util5 a accordé à util9 le privilège système de modifier nʹimporte quel index. Il peut le lui retirer par la commande suivante :
REVOKE CREATE ANY INDEX FROM util9;
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
198
À noter que la suppression dʹun privilège ne se propage pas aux autres utilisateurs qui lʹauraient acquis de lʹutilisateur util9. 8.25 Rôle
La notion de rôle est propre au système Oracle; elle est cependant une notion fort intéressante pour le DBA. Cela permet de simplifier la gestion des privilèges en regroupant ceux‐ci dans un rôle qui est accordé aux utilisateurs. Lʹintérêt de cette notion est quʹelle permet de modifier les privilèges dʹun groupe dʹutilisateurs en modifiant simplement le rôle. Chaque rôle a un nom et un contenu qui sont définis séparément. Par exemple, on aura : CREATE ROLE chefUsine NOT IDENTIFIED; ‐‐aucun mot de passe pour activer le rôle Dans lʹexemple qui précède le rôle est créé avec le nom chefUsine et ce rôle nʹest pas protégé par un mot de passe. Au contraire, dans lʹexemple ci‐dessous le chefUsine a une protection par un mot de passe. Par la suite, le rôle est enrichi avec les privilèges dont les utilisateurs ont besoin :
GRANT CREATE SESSION TO chefUsine IDENTIFIED BY 34y586j; GRANT SELECT, INSERT, UPDATE ON Usines TO chefUsine;
Le rôle chef_usine est aussi accordé aux utilisateurs par la commande GRANT :
GRANT chefUsine TO util2, util3, util9, util15; Un même utilisateur peut avoir plusieurs rôles. En effet, lʹassociation dʹun rôle à un utilisateur ne lʹactive pas toujours immédiatement. Cʹest par la commande SET ROLE quʹun rôle est activé pour un utilisateur :
SET ROLE chefUsine; Tous les rôles sont activés par la clause ALL.
SET ROLE ALL;
Rôle par défaut Le rôle par défaut est défini à la création dʹun compte dʹutilisateur. Par exemple, on aura:
CREATE USER util9 DEFAULT ROLE chefUsine; Si lʹon procède ainsi, chaque fois que lʹutilisateur util9 démarre une session de travail, le rôle chefUsine devient automatiquement actif.
Information sur les rôles Lʹinformation sur les rôles définis dans une BD est stockée dans deux tables que le DBA peut interroger avec une requête SQL. La table DBA_ROLES contient le nom de tous les
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
199
rôles définis dans la BD. La vue DBA_ROLE_PRIVS permet de connaître les rôles associés aux différents utilisateurs :
SELECT role FROM DBA_ROLES; ‐‐ lecture de la table du dictionnaire sur les rôles SELECT grantee, granted_role FROM DBA_ROLE_PRIVS;
Exercices du chapitre 8
1‐ Développer un trigger dʹinsertion de type BEFORE pour vérifier que tout ajout dans la relation Empl est refusé si le nombre dʹemployés dont le taux horaire est supérieur à 14.00 $ ne dépasse pas celui des employés dont le taux horaire est inférieur ou égal à 14.00 $ (voir l’instance de référence de BD3). 2‐ Voici une partie de la BD dʹun CEGEP, qui est composée de deux relations dont lʹune a une clé composée :
Eleve (mat*, nomE, adresCampus, moyE) Inscription (mat*, cours*, note)
Les requêtes ci‐dessous sont considérées typiques du traitement fait par le SGBD :
SELECT E.mat, E.nomE, I.cours, I.note FROM Eleve [as] E, Inscription [as] I WHERE E.mat = I.mat and I.note > 2.5;
SELECT cours, note FROM Inscription WHERE mat = ʹ9203498ʹ;
SELECT mat, note FROM Eleve E, Inscription I WHERE E.mat = I.mat and mat = ʹ92000000ʹ and cours = ʹift‐14678ʹ;
2.1 Créer le schéma des relations Élève et Inscription en spécifiant les clés primaire et étrangère. 2.2‐ Sur quels attributs faut‐il créer un index pour accélérer le calcul de la réponse aux requêtes ci‐dessus? Commenter votre réponse. 2.3‐ Formuler les commandes nécessaires pour créer les index appropriés. 3‐ Créer et accorder à lʹutilisateur du compte25 les privilèges de lecture et de mise à jour sur les relations Eleve et Inscription et, pour le compte31, les accès en lecture seulement sur la relation Eleve.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
200
4‐ Créer les comptes bd345 et bd124 en leur associant respectivement les mots de passe ʹbv3m67ʹ et ʹu82jvg7ʹ. Pour faire cet exercice vous devez avoir le privilège de création de comptes. 5‐ Désactiver la contrainte référentielle qui serait définie et activée dans la BD CEGEP. 6‐ Expliquer pourquoi il ne peut pas y avoir de rollback dans un trigger de BD stocké dans le dictionnaire? Suggestion : réfléchir au traitement dʹune cascade de triggers. 7‐ Développer un bloc PL/SQL pour augmenter de 15% le prix des articles en inventaire (BD1) qui ont une marge bénéficiaire. 8‐ Au moyen dʹune procédure PL/SQL, supprimer les ventes faites à ʹsergeʹ et qui sont inscrites dans la relation Ventes ( BD1). 9‐ Formuler un curseur PL/SQL qui donne le matricule des transporteurs qui nʹont pas de stocks de pièces de rechange (BD2). * Utiliser la base de données BD3 pour répondre aux questions suivantes. 10‐ Ajouter au moyen dʹune procédure PL/SQL, 20 000 tuples dans une relation EmplListe dont le schéma est identique à celui de Empl. Les données ajoutées doivent être conformes au type utilisé pour les attributs. Chaque tuple doit avoir une clé différente et un nom fictif (pas nécessairement significatif) qui peut être formé par la concaténation du numéro de lʹemployé et dʹun nom répétitif quelconque. Les employés peuvent partager le même taux horaire. Le numéro de lʹemployé est généré au moyen dʹun compteur initialisé au départ à 10 et augmenté progressivement à lʹintérieur de lʹitération PL/SQL. 11‐ Créer un index, nom_idx, avec le nom de lʹemployé. Effectuer une recherche de quelques noms dʹemployés et noter le temps de recherche. Supprimer lʹindex et effectuer à nouveau la recherche avec les mêmes employés. Notez‐vous une différence dans le temps de réponse? 12‐ Formuler quelques requêtes et utiliser la commande EXPLAIN PLAN pour faire afficher le plan dʹexécution de la requête traitée avec et sans index. Noter les différences dans le plan dʹexécution généré par le module dʹoptimisation. 13‐ Supprimer les 20 000 tuples ajoutés dans la relation EmplListe. 14‐ Développer un petit schéma de la base relationnelle avec des données hiérarchiques significatives. Par exemple, une relation qui représenterait le nas des abonnés successifs qui ont eu un même indicatif téléphonique : Abonne (indicatif, abonne_precedent, abonne_courant, date_abonne_courant). Formuler une requête hiérarchique pour afficher, depuis une date précise, les abonnés successifs dʹun indicatif particulier.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
201
Répondre aux questions ci‐dessous en utilisant, sʹil y a lieu, le schéma de la base de données BD3, Dotation des ressources humaines. Le schéma de cette base est le suivant :
Empl (noEmpl*, nom, tauxH) Postes (noPoste*, description) Assignations (noPoste*, noEmpl*, debut, quart)
N.B. Lʹattribut debut est de type Date. 15‐ Afficher le nom des employés et la date du début de leur assignation (mandat de travail) aux projets qui débutent après le 10 janvier 1992. 16‐ Augmenter le taux horaire de tous les employés de 10 %. 17‐ Lister le nom des mécaniciens par ordre alphabétique descendant. 18‐ Afficher la moyenne du taux horaire pour les employés qui travaillent la nuit. 19‐ Modifier la description du poste ʹj12ʹ pour celui de chef. 20‐ Ajouter une colonne à la table Employe, soit l’attribut diplôme du type char(20). 21‐ Lister le contenu de la table Employe et ensuite attribuer le diplôme ‘dec’ (diplôme dʹétudes collégiales) à tous les employés. Afficher son nouveau schéma de relation(table). 22‐ Créer une vue Personnel_V1 avec la table des employés. Elle est composée du numéro et du nom des employés dont le no‐empl est supérieur à la série ʹP400ʹ. 23‐ Lister les employés de la série P400 en ordre alphabétique descendant en utilisant la vue relationnelle Personnel_V1. 24‐ Ajouter une nouvelle employée, Megy Ryan, P600, à la liste des employés et lister la table des employés. 25‐ Afficher pour chaque matricule, le numéro de poste et la date du début. 26‐ Créer une vue V1992 pour afficher les attributs nom et noPoste des employés embauchés depuis le 1 février 1992. 27‐ Formuler les ordres DML pour insérer lʹemployé ʹBeaumontʹ, embauché depuis le 12 janvier 1988 et qui travaille le soir. Il titularise le poste ʹj75ʹ et, à l’instar des autres employés, son taux horaire est celui prévu par la convention collective, soit 15.00 $.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
202
28‐ Expliquer pourquoi il est possible dʹinsérer, par lʹentremise de la vue V1992, les informations concernant lʹemployé ʹBeaumontʹ ? Est‐il possible de relire le tuple inséré par lʹentremise de la même vue ? 29‐ Modifier la vue V1992 pour interdire lʹinsertion de tout employé embauché après le 1 février.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
203
INDEX
A
Activation dʹun trigger, 181 ADD_MONTHS (d, n), 68 ALL, 52, 79 ALTER PROFILE, 196 ALTER TABLE, 85 ALTER TABLE, 104 ALTER TRIGGER, 191 ANY, 52 AVG(), 80
B
B*‐arbre, 19 B*‐arbre, 125 B*‐arbre, 135 B‐arbre, 133 B‐arbre, 16 base de données réactive, 176 BD1, 21 BD2, 22 BD3, 23 BEGIN, 170 bloc anonyme, 170 bloc PL/SQL, 171 boucle FOR, 173
C
CASCADE, 30, 157 CAST, 46 CHANGE, 106 CHECK, 197 CHECK OPTION, 157 clé étrangère, 146 clé étrangère, 28 clés étrangères, 143 cluster, 148 Cluster avec Hashing explicite, 152 Cluster indexé, 152
clustering index, 134 COALESCE(), 77 COLUMN, 106 COMMIT, 192 comptes‐utilisateurs, 194 concaténation, 64 Condition, 35 conversion des types, 71 conversions automatiques, 47 create [unique] index, 137 CREATE CLUSTER, 151 CREATE FORCE VIEW, 155 CREATE INDEX, 134 CREATE INDEX BITMAP, 136 Create MATERIALIZED, 163 CREATE OR REPLACE view, 154 CREATE PROFILE, 196 CREATE ROLE, 198 CREATE SEQUENCE, 188 CREATE SESSION, 195 CREATE SYNONYM, 193 CREATE TABLE, 26, 103, 104 CREATE TABLESPACE, 20 CREATE TRIGGER, 178 create unique index, 139 CREATE USER, 194 Création des vues avec SQL, 164 Création dʹune table et le typage des attributs, 24
D
data wharehouse, 135 Date par défaut, 70 DB2‐V2, 176 DECLARE, 170 DECODE(), 76 définition dʹun trigger de BD, 190 DELETE FROM, 100 Densité de lʹ index, 133
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
204
DESCRIBE, 102 dictionnaire de données, 102 Différence, 99 Directives de SQL, 106 DISABLE, 146 DISTINCT, 79 Division relationnelle, 87 DROP TABLE, 105 DUAL, 63
E
échec dʹun trigger, 179 EDIT, 107 éditeur de ligne., 106 EXCEPTION, 170 exceptions, 174 Exercices, 111, 199 Exercices résolus, 108 Expression de table, 51 Expression de table partagée (voir DB2), 56
Expression incluant un appel de fonction, 44
Expression incluant une structure CASE, 45
expressions régulières, 37
F
Fonction dʹutilisateur, 74 fonctions scalaires, 61 Fonctions SQL, 61 Fonctions SQL dʹagrégation, 79 Formulation du GROUP BY avec une fonction, 82
Fouille hiérarchique, 57 Fragmentation des grands tuples et chaînage, 14
G
Générateur, 187 GET, 107 GRANT OPTION, 197 GRANT SELECT, 197
GROUP BY, 83 Groupement et vue relationnelle, 83
H
hashing, 132 HAVING count, 83
I
index, 138 Index, 130 Index à clés transformées, 134 Index bitmap, 135 index composé, 137 index concaténé, 141 Index inversé, 135 Indexation, 125, 143, 154, 168, 169, 176, 187, 193, 194, 198, 199
INFORMIX, 176 INSERT, 32, 99 INSERT INTO, 100 Instead Of, 186 INSTR(ch1,ch2,n,[i]), 64 Intersection, 98
J
Jointure droite, 96 Jointure externe gauche, 95 Jointure naturelle, 85 Jointure thêta, 86
L
LAST_DAY (d), 68 LENGTH (ch1), 65 LIST, 107 LTRIM(ch1[,setcar]), 64
M
Matérialisation dʹune vue, 161 max(), 80 Migration des tuples, 14 min(), 80 Mise à jour, 101
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
205
Mise à jour, 99 mise à jour et vue, 158 Mise à jour à travers une vue, 163 Modèle dʹexécution, 32 modèle dʹexécution trigger;, 181 MONTHS_BETWEEN (d1, d2), 68
N
NEW, 181 NO_DATA_FOUND, 175 NULLIF(), 79 NVL(), 76
O
optimiseur, 139 Optimiseur et index, 142 Oracle, 176 ORDER BY, 46
P
Page insérable, 13 PCTFREE, 12, 26, 144 PCTUSED, 12, 26 Performance, 145 Pertinence de lʹindexation, 147 PL/SQL, 169 Placement des données, 15 Placement par hashing (hash clustering), 150
placement regroupé des tuples, 133 Prédicat IS NUL, 41 Prédicats spéciaux, 190 PRIMARY KEY, 143 privilège système, 190 profil, 196 PROFIL, 196 programme PL/SQL, 170
R
RAISE_APPLICATION_ERROR( ), 182 Récursivité, 60 Récursivité des fonctions, 76
refresh, 163 Regroupement inconditionnel des tuples, 81
REPLACE, 83 REPLACE (ch1, si, par), 65 REPLACE()., 64 Requête corrélée, 54 Requête SQL, 31 RESTRICT, 157 REVOKE SELECT, 158 Rôle, 198 Rôle par défaut, 198 ROWID, 17 RTRIM(ch1[,setcar]), 64 RUN, 107
S
SAVE, 107 Schéma de la BD, 24 SELECT DISTINCT, 39 Sélection, 35 SET PAGESIZE, 106 SET BUFFER, 106 SET PAUSE, 106 signature Soundex, 66 SING(n), 77 snapshot, 168 Soundex, 66 SOUNDEX (ch1), 64 SPOOL, 106 SPOOL OFF, 106 SQL, 5 SQL*Plus, 6 SQLPLUS, 7 star schema, 135 START, 107 Stockage des triggers, 189 Structure générale de page, 11 SUBSTR (ch1, m, n), 64 Suppression, 100 Suppression d’un index, 140 Suppression de la vue, 157 Suppression du temps, 72
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
206
SYBASE, 176 syntaxe des variables, 172 SYSDATE, 68, 73
T
table en mutation, 184 TO_CHAR(), 62 TO_CHAR(d, masque[, nlsp])), 71 TO_DATE(ch1,masque[,nlsp])), 71 TO_NUMBER(ch1,masque[,nlsp]), 71 TOO_MANY_ROWS, 175 transaction, 21, 101 TRANSLATE(ch1,s,c), 64 Tri, 46 trigger, 177 Trigger de tuple, 179 Trigger dʹénoncé, 179 Trigger Instead Of, 186 TRUNC(), 25, 62, 73 TRUNCATE TABLE, 100 TRUNCATE TABLE, 100 Types internes dʹOracle, 25
U
Union, 98 UPDATE, 101 utilisation des index, 140
V
valeurs dupliquées, 130 Variable de table, 155 variable hôte, 173 Variables de corrélation, 181 vue et droits dʹaccès;Ainsi, un usager Paul qui nʹest pas le propriétaire de la vue peut cependant lʹutiliser pour avoir accès aux ventes enregistrées après le 1 janvier 1995. Il doit auparavant obtenir du propriétaire de la table le droit de sélection que ce dernier formulera ainsi:, 156
vue matérialisée, 169 vue relationnelle, 154
W
with grant option, 99 WITH GRANT OPTION, 197
Références 1 COREY, M., ABBEY, M., DECHICHIO, D. J., Tuning Oracle Version 7.x, Oracle Press, ISBN‐0‐07‐881181‐3, 1995. 2 RODGERS, U., ORACLE, A Database Developer’s Guide, Yourdon Press Computing Series, ISBN 0‐13‐488925‐8, 1991, 250p. 3 MIRANDA, Serge, L’art des bases de données; comprendre et évaluer SQL, Eyrolles, vol., 1990. 4 DATE, C. J., A Guide to the SQL Standard, Addison‐Wesley, 1988, ISBN 0‐201‐05777‐8. 5 LUSARDI, F., The Database Expert Guide to SQL, McGraw‐Hill, 1988, ISBN 0‐07‐039002‐9 6 ORACLE CORP., SQL*Plus User’s Guide and Reference, version 3.1, Cooperative Development Environment, ISBN‐5142‐31‐1192, 1992. 7 KOCH, George, ORACLE, the Complete Reference, version 5 and 6, Osborne McGraw‐Hill, 1991, ISBN 0‐07‐881635‐1. 8 LONEY, K., ORACLE Data Handbook, Osborne‐Oracle Press, 1994, ISBN 0‐07‐881182‐1.
Chapitre 8 Indexation et vue relationnelle
© André Gamache, Département d’informatique et de génie logiciel, Faculté des sciences et de génie, Université Laval, Québec, Qc, Canada, G1K 7P4. Courriel : [email protected]
207
9 ODELL, Margaret K., Robert C. Russell, U. S. Patents 1261167, 1918. 10 RUSSELL, R., U.S. Patents 1435663, 1922. 11 BAIRD, Gus, référence citée par Joe CELKO dans son livre SQL avancé publié par Thomson, 1997 12 PHILIPS, Laurence, Metaphone, Computer Language, 1990. 13 ORACLE, CORP., Oracle7, SQL Language Reference Manual, Cooperative Server Technology, 1992, ISBN 778‐70‐1292.. 14 CORRIGAN, Peter, GURRY, M., ORACLE Performance Tuning, O’Reilly Associates, 1994, ISBN 1‐56592‐048‐1. 15 DATE, C. J., An Introduction to Database Systems, 6ed. edition, The Systems Programming Series, Addison‐Wesley, 1995, ISBN 0‐201‐54329‐X. 16 MAREE, C., LEDANT, G., SQL 2; Initiation et programmation, Armand Colin, 2e édition, ISBN 2‐200‐21411‐1, 1994. 17GAMACHE, A. Introduction au langage PL/SQL, Département dʹinformatique, Université Laval, 1997, 63p. (Disponible aussi sur le WEB).