Bases Données Avancées
Bases Données Avancées
Bases Données Avancées
Faculté Pluridisciplinaire
Nador
Support de Cours
Bases de données avancées
Langage SQL & PL /SQL sous Oracle
Filière : SMI-S6
Introduction
Introduction
I. Le bloc PL/SQL
II. Types des données
III. Structures de contrôle
IV. Curseurs
V. Exceptions
VI. Procédures et fonctions
2
Introduction
SQL (sigle de Structured Query Language, en français langage de requête structurée) est
un langage informatique normalisé servant à exploiter des bases de données relationnelles. La
partie langage de définition des données permet de créer et de modifier l'organisation des
données dans la base de données.
Outre le langage de définition des données, la partie langage de manipulation des données de
SQL permet de rechercher, d'ajouter, de modifier ou de supprimer des données dans les bases
de données relationnelles, la partie langage de contrôle de transaction permet de commencer
et de terminer des transactions, et la partie langage de contrôle des données permet d'autoriser
ou d'interdire l'accès à certaines données à certaines personnes.
Les instructions de manipulation du contenu de la base de données commencent par les mots
clés SELECT, UPDATE, INSERT ou DELETE qui correspondent respectivement aux
opérations de recherche de contenu, modification, ajout et suppression. Divers mots clés tels
que FROM, JOIN et GROUP permettent d'indiquer les opérations d'algèbre relationnelle à
effectuer en vue d'obtenir le contenu à manipuler.
Les mots clés GRANT et REVOKE permettent d'autoriser des opérations à certaines
personnes, d'ajouter ou de supprimer des autorisations. Tandis que les mots clés COMMIT et
ROLLBACK permettent de confirmer ou annuler l'exécution de transactions.
SQL a été Créé en 1974, normalisé depuis 1986, le langage est reconnu par la grande majorité
des systèmes de gestion de bases de données relationnelles (abrégé SGBDR) du marché.
Microsoft Access
Microsoft SQL Server
MySQL
Oracle
3
I. SQL langage de définition de données
Avant d’aborder la création des tables avec les commandes SQL, voici un petit lexique entre
le modèle relationnel et SQL :
relation table
attribut colonne
tuple ligne
Un nom d’objet (table, base, colonne contrainte, vue, etc…) doit avoir les caractéristiques
suivantes :
- Ne pas dépasser 128 caractères
- Commencer par une lettre
- Etre composé de lettre, de chiffre, du caractère ‘_’
- Ne pas être un mot réservé en SQL
- Pas de caractères accentués
Il faut noter que les noms d’objets sont insensibles à la casse.
4
Types de données :
Les principaux types de données ainsi que leurs limites sont décrits dans le tableau suivant :
5
Pour créer une table en SQL, il existe l’instruction CREATE TABLE dans laquelle sont
précisés pour chaque colonne de la table : son intitulé, son type de donnée et une ou plusieurs
contraintes.
Exemple :
Considérons le schéma relationnel suivant :
COMMANDE(NoCommande, DateCommande, DateLivraison, #NoClient)
CLIENT (NoClient, NomClient, TypeClient)
CREATE TABLE Client (NoClient Integer, NomClient Varchar(25), TypeClient
VarChar(15) )
CONSTRAINT
DEFAULT
NOT NULL
UNIQUE
CHECK
Contrainte du domaine
On entend par domaine le type des attributs d’une table.
Il ne peut y avoir de comparaison entre deux valeurs d’attributs si ces derniers ne sont
pas définit sur le même domaine. Le SGBD-R se charge de vérifier la validité des valeurs
d’attributs.
6
Un enregistrement ne peut être inséré dans une table que si chaque champ de l’enregistrement
vérifie la contrainte d’intégrité de domaine de la colonne pour laquelle il est destiné.
7
Exemple : création de table avec contrainte
Voici un exemple permettant de voir la syntaxe d'une instruction de création de table avec
contraintes :
CREATE TABLE clients(
Nom char(30) NOT NULL,
Prenom char(30) NOT NULL,
Age integer CHECK (age < 100),
Email char(50) NOT NULL, CHECK (Email LIKE "%@%"));
Définition de clés
Grâce à SQL, il est possible de définir des clés, c'est-à-dire spécifier la (ou les) colonne(s)
dont la connaissance permet de désigner précisément un et un seul tuple (une ligne).
L'ensemble des colonnes faisant partie de la table en cours permettant de désigner de façon
unique un tuple est appelé clé primaire et se définit grâce à la clause PRIMARY KEY suivie
de la liste de colonnes, séparées par des virgules, entre parenthèses. Ces colonnes ne peuvent
alors plus prendre la valeur NULL et doivent être telles que deux lignes ne puissent avoir
simultanément la même combinaison de valeurs pour ces colonnes.
PRIMARY KEY (colonne1, colonne2, ...)
Lorsqu'une liste de colonnes de la table en cours de définition permet de définir la clé
primaire d'une table étrangère, on parle alors de clé étrangère, et on utilise la clause
FOREIGN KEY suivie de la liste de colonnes de la table en cours de définition, séparées par
des virgules, entre parenthèses, puis de la clause REFERENCES suivie du nom de la table
étrangère et de la liste de ses colonnes correspondantes, séparées par des virgules, entre
parenthèses.
FOREIGN KEY (colonne1, colonne2, ...)
REFERENCES Nom_de_la_table_etrangere(colonne1,colonne2,...)
Attachée à un champ "clé étrangère" cette contrainte garantit que toute valeur prise par ce
champ appartienne à l’ensemble des valeurs de la clé primaire.
L’option ON DELETE CASCADE permet de supprimer une ligne d’une table ainsi que toutes
les lignes liées dans une autre table.
Exemple :
CREATE TABLE Client
(NoClient Integer,
NomClient VARCHAR(25) NOT NULL,
TypeClient VARCHAR(15) NOT NULL,
CONSTRAINT PK_Client PRIMARY KEY (NoClient),
CONSTRAINT CK_Client CHECK (TypeClient = "PARTICULIER " OR TypeClient
=" PROFESSIONNEL ") ;
9
update Correcte si l’instruction ne Correcte si la nouvelle clé
laisse pas étrangère référence un
d’enregistrements dans la enregistrement père
table fils ayant une clé existant
étrangère non référenciée
delete Correcte si aucun Correcte sans condition
enregistrement de la table
fils ne référence le ou les
enregistrements détruits
Delete cascade Correcte sans condition Correcte sans condition
L’option ON DELETE CASCADE permet de supprimer une ligne d’une table ainsi que tous
les lignes liées dans une autre table.
Exemple :
On suppose que le contenu des tables CLIENT et COMMANDE est limité aux lignes
suivantes:
CLIENT
NoClient NomClient TypeClient
1 DUBOIS PROFESSIONNEL
2 DELAGE PARTICULIER
3 DUPONT PROFESSIONNEL
COMMANDE
N°Commande DateLivraison DateCommande NoClient
101 15/12/1999 3
102 17/12/1999 18/12/1999 1
103 17/12/1999 22/12/1999 1
10
La suppression du client Numéro 1 dans la table CLIENT pourrait :
- soit entraîner la suppression des commandes 102 et103 dans la table COMMANDE si
l’option ON DELETE CASCADE est mentionnée,
- soit entraîner un refus de suppression de ce client si l’option ON DELETE CASCADE n’est
pas mentionnée ; dans ce cas il faut préalablement supprimer les commandes 102 et 103 dans
la table COMMANDE pour ensuite pouvoir supprimer le client Numéro 1.
- soit entraîner la suppression de ce client et de vider le champ NoClient pour les commandes
102 et103 dans la table COMMANDE si l’option ON DELETE SET NULL est mentionnée.
Ajout :
Modification :
11
II. SQL langage de manipulation de données
II.1 Insertion de données
Pour insérer une ou plusieurs lignes dans une seule table, SQL offre l’instruction INSERT
INTO. Lorsque l’on connait directement les valeurs à insérer, on utilise l’instruction INSERT
INTO…VALUES.
On peut également insérer dans une table, le résultat d’une requête SELECT, auquel cas
plusieurs lignes peuvent être insérer à la fois.
INSERT INTO <nom de table> (colonne, …) SELECT…
Exemple :
INSERT INTO clients (num_client, nom_client, prenom_client) VALUES (10,
‘morabit’,’ ahmed’) ;
Pour que num_client reçoie un entier qui s’incrémente automatiquement, il suffit de créer,
avant la première insertion, une séquence pour cette table.
MINVALUE 1
MAXVALUE 99
START WITH 1
INCREMENT BY 1
12
II.2 Modification de données
Pour modifier la valeur d’une ou plusieurs colonnes, d’une ou plusieurs lignes, d’une seule
table, on utilise l’instruction UPDATE…SET…WHERE.
Exemple :
Pour supprimer une ou plusieurs lignes dans une seule table, on utilise la syntaxe suivante :
Exercice :
II.4 Interrogation
Pour afficher les données sous forme de tableau dont les colonnes portent un intitulé. On
utilise l’instruction suivante :
Base utilisée :
PILOTE(NUMPIL, NOMPIL, PRENOMPIL, ADRESSE, SALAIRE, PRIME)
AVION(NUMAV, NOMAV, CAPACITE, LOCALISATION)
VOL(NUMVOL,#NUMPIL,#NUMAV, DATE_VOL, HEURE_DEP, HEURE_ARR,
VILLE_DEP, VILLE_ARR).
On suppose qu'un vol, référencé par son numéro NUMVOL, est effectué par un unique pilote,
de numéro NUMPIL, sur un avion identifié par son numéro NUMAV.
Rappel de la syntaxe :
SELECT [DISTINCT] <nom de colonne>[, <nom de colonne>]…
FROM <nom de table>[, <nom de table>]…
[WHERE <condition>]
[GROUP BY <nom de colonne>[, <nom de colonne>]…
[HAVING <condition avec calculs verticaux>]]
[ORDER BY <nom de colonne>[, <nom de colonne >]…]
14
où table est le nom de la table à consulter. Le caractère * signifie qu'aucune projection n'est
réalisée, i.e. que tous les attributs de la table font partie du résultat.
IN (liste) : teste si la valeur d'une colonne coïncide avec l'une des valeurs de la liste.
15
Exemple : rechercher les avions de nom A310, A320, A330 et A340.
SELECT * FROM AVION WHERE NOMAV IN ('A310', 'A320', 'A330', 'A340');
BETWEEN v1 AND v2 : teste si la valeur d'une colonne est comprise entre les
valeurs v1 et v2 (v1 <= valeur <= v2).
Exemple : quels sont les noms des pilotes qui gagnent entre 3.000 et 5.000 ?
SELECT NOMPIL FROM PILOTE WHERE SALAIRE BETWEEN 3000 AND 5000
Exemple : quels sont les noms des avions différents de A310, A320, A330 et A340 ?
SELECT NOMAV FROM AVION WHERE NOMAV NOT IN
('A310','A320','A330','A340');
Opérateurs logiques : AND et OR. Le AND est prioritaire et les parenthèses doivent être
utilisé pour modifier l’ordre d’évaluation.
Exemple : quels sont les vols au départ de Marseille desservant Paris ?
SELECT * FROM VOL WHERE VILLE_DEP = 'MARSEILLE' AND
VILLE_ARR ='PARIS' ;
Requête paramétrées : les paramètres de la requête seront quantifiés au moment de
l’exécution de la requête. Pour cela, il suffit dans le texte de la requête de substituer aux
différentes constantes de sélection des symboles de variables qui doivent systématiquement
commencer par le caractère &. Si la constante de sélection est alphanumérique, on peut
spécifier ‘&var’ dans la requête, l’utilisateur n’aura plus qu’à saisir la valeur
Exemple : quels sont les vols au départ d'une ville et dont l'heure d'arrivée est inférieure à une
certaine heure ?
SELECT * FROM VOL WHERE VILLE_DEP = '&1' AND HEURE_ARR < &2
Lors de l'exécution de cette requête, le système demande à l'utilisateur de lui indiquer une
ville de départ et une heure d'arrivée.
16
II.4.1.3 Calculs horizontaux
Des expressions arithmétiques peuvent être utilisées dans les clauses WHERE et SELECT.
Ces expressions de calcul horizontal sont évaluées puis affichées et/ou testées pour chaque
tuple appartenant au résultat.
Exemple : quels sont les pilotes qui avec une augmentation de 10% de leur prime gagnent
moins de 5.000 € ? Donner leur numéro, leurs revenus actuel et simulé.
SELECT NUMPIL, SALAIRE+PRIME, SALAIRE + (PRIME*1.1) FROM
PILOTE WHERE SALAIRE + (PRIME*1.1) < 5000
Expression d’un calcul :
Les opérateurs arithmétiques +, -, *, et / sont disponibles en SQL. De plus, l’opérateurs ||
(concaténation de chaînes de caractères: C1 || C2 correspond à concaténation de C1 et C2).
Enfin, les opérateurs + et - peuvent être utilisés pour ajouter ou soustraire un nombre de jour à
une date. L'opérateur - peut être utilisé entre deux dates et rend le nombre de jours entre les
deux dates arguments.
Les fonctions disponibles en SQL dépendent du SGBD. Sous ORACLE, les fonctions
suivantes sont disponibles :
ABS(n) la valeur absolue de n ;
FLOOR(n) la partie entière de n ;
POWER(m, n) m à la puissance n ;
TRUNC(n[, m]) n tronqué à m décimales après le point décimal. Si m est négatif, la
troncature se fait avant le point décimal ;
ROUND(n [, d]) arrondit n à dix puissance -d ;
CEIL(n) entier directement supérieur ou égal à n ;
MOD(n, m) n modulo m ;
SIGN(n) 1 si n > 0, 0 si n = 0, -1 si n < 0 ;
SQRT(n) racine carrée de n (NULL si n < 0) ;
GREATEST(n1, n2,…) la plus grande valeur de la suite ;
LEAST(n1, n2,…) la plus petite valeur de la liste ;
17
NVL(n1, n2) permet de substituer la valeur n2 à n1, au cas où cette dernière est une valeur
nulle ;
LENGTH(ch) longueur de la chaîne ;
SUBSTR(ch, pos [, long]) extraction d'une sous-chaîne de ch à partir de la position pos en
donnant le nombre de caractères à extraire long ;
INSTR(ch, ssch [, pos [, n]]) position de la sous-chaîne dans la chaîne ;
UPPER(ch) mise en majuscules ;
LOWER(ch) mise en minuscules ;
INITCAP(ch) initiale en majuscules ;
LPAD(ch, long [, car]) complète ch à gauche à la longueur long par car ;
RPAD(ch, long [, car]) complète ch à droite à la longueur long par car ;
LTRIM(ch, car) élague à gauche ch des caractères car ;
RTRIM(ch, car) élague à droite ch des caractères car ;
TRANSLATE(ch, car_source, car_cible) change car_source par car_cible ;
TO_CHAR(nombre, ch) convertit un nombre ou une date en chaîne de caractères ;
TO_NUMBER(ch) convertit la chaîne en numérique ;
ASCII(ch) code ASCII du premier caractère de la chaîne ;
CHR(n) conversion en caractère d'un code ASCII ;
TO_DATE(ch[, fmt]) conversion d'une chaîne de caractères en date ;
ADD_MONTHS(date, nombre) ajout d'un nombre de mois à une date ;
MONTHS_BETWEEN(date1, date2) nombre de mois entre date1 et date2 ;
LAST_DAY(date) date du dernier jour du mois ;
NEXT_DAY(date, nom du jour) date du prochain jour de la semaine ;
SYSDATE date du jour.
ATTENTION : Tous les SGBD n'évaluent pas correctement les expressions arithmétiques
que si les valeurs de ses arguments ne sont pas NULL. Pour éviter tout problème, il convient
d'utiliser la fonction NVL (décrite ci avant) qui permet de substituer une valeur par défaut aux
valeurs nulles éventuelles.
Exemple : l’attribut Prime pouvant avoir des valeurs nulles, la requête donnant le revenu
mensuel des pilotes toulousains doit se formuler de la façon suivante.
SELECT NUMPIL, NOMPIL, SALAIRE + NVL(PRIME,0) FROM PILOTE
18
WHERE ADRESSE = 'TOULOUSE'
19
Exemple : combien de destinations sont desservies au départ de Bordeaux ?
SELECT COUNT (DISTINCT VILLE_ARR) FROM VOL WHERE
VILLE_DEP = 'BORDEAUX'
Ecrire les scripts SQL qui permettent d’obtenir les informations suivantes.
1. Informations sur les employés dont la fonction est "MANAGER" dans les
départements 20 et 30.
2. Liste des employés qui n'ont pas la fonction "MANAGER" et qui ont été embauchés
en 81.
3. Liste des employés ayant un "M" et un "A" dans leur nom.
4. Liste des employés ayant deux "A" dans leur nom
5. Liste des employés ayant une commission
6. Liste des employés travaillant à "DALLAS"
7. Noms et dates d'embauche des employés embauchés avant leur manager, avec le nom
et la date d'embauche du manager
8. Noms et dates d'embauche des employés embauchés avant 'BLAKE'
9. Employés embauchés le même jour que 'FORD'
10. Employés ayant le même manager que 'CLARK'
11. Employés embauchés avant tous les employés du département 10
12. Employés ayant le même job et même manager que 'TURNER'
13. Employés de département 'RESEARCH' embauchés le même jour que quelqu'un du
département 'SALES'.
14. Employés gagnant plus que leur manager
15. Liste des noms des employés avec les salaires tronqués au millier
Les jointures permettent d’extraire des données issues de plusieurs tables. La clause FROM
contient tous les noms de tables à fusionner. La clause WHERE exprime le critère de jointure
sous forme de condition.
ATTENTION : si deux tables sont mentionnées dans la clause FROM et qu’aucune jointure
n’est spécifiée, le système effectue le produit cartésien des deux relations (chaque tuple de la
20
première est mis en correspondance avec tous les tuples de la seconde), le résultat est donc
faux car les liens sémantiques entre relations ne sont pas utilisés. Donc respectez et vérifiez la
règle suivante.
Si n tables apparaissent dans la clause FROM, il faut au moins (n – 1) opérations de jointure.
La forme générale d'une requête de jointure est :
SELECT colonne,…FROM table1, table2… WHERE <condition>
où <condition> est de la forme attribut1 attribut2.
Exemple : quel est le numéro et le nom des pilotes résidant dans la ville de localisation de
l’avion n° 33 ?
SELECT NUMPIL, NOMPIL FROM PILOTE, AVION WHERE ADRESSE =
LOCALISATION AND NUMAV = 33
Les noms des colonnes dans la clause SELECT et WHERE doivent être uniques. Ne pas
oublier de lever l’ambiguïté à l’aide d’alias ou en préfixant les noms de colonnes.
Exemple : donner les noms des pilotes faisant des vols au départ de Marseille sur des Airbus
?
SELECT DISTINCT NOMPIL FROM PILOTE, VOL, AVION WHERE
VILLE_DEP = 'MARSEILLE' AND NOMAV LIKE 'A%' AND
PILOTE.NUMPIL = VOL.NUMPIL AND VOL.NUMAV = AVION. NUMAV
Alias de nom de table : se place dans la clause FROM après le nom de la table.
Exemple : quels sont les avions localisés dans la même ville que l'avion numéro 103 ?
SELECT AUTRES.NUMAV, AUTRES.NOMAV
FROM AVION AUTRES, AVION AV103
WHERE AV103.NUMAV = 103
AND AUTRES.NUMAV <> 103
AND AV103.LOCALISATION = AUTRES.LOCALISATION
Dans cette requête l’alias AV103 est utilisée pour retrouver l'avion de numéro 103 et l’alias
AUTRES permet de balayer tous les tuples de AVION pour faire la comparaison des
localisations.
21
FROM VOL AUTRES, VOL VOLIT100
WHERE VOLIT100.NUMVOL = ‘IT100’ AND
VOLIT100.VILLE_ARR = AUTRES.VILLE_DEP
La requête recherche les vols dont la ville de départ correspond à la ville d’arrivée du vol
IT100 puis ne conserve que les villes d’arrivée de ces vols.
Exemple : quels sont les noms des pilotes gagnant plus que le salaire moyen des pilotes ?
SELECT NOMPIL FROM PILOTE WHERE SALAIRE > (SELECT
AVG(SALAIRE) FROM PILOTE)
2. Deuxième cas :
Le résultat de la sous-requête est formé d'une liste de valeurs. Dans ce cas, le résultat de la
comparaison, dans la clause WHERE, peut être considéré comme vrai :
soit si la condition doit être vérifiée avec au moins une des valeurs résultats de la
sous-requête. Dans ce cas, la sous-requête doit être précédée du comparateur suivi de
ANY (remarque : = ANY est équivalent à IN) ;
soit si la condition doit être vérifiée pour toutes les valeurs résultats de la sous
requête, alors la sous-requête doit être précédée du comparateur suivi de ALL.
Exemple : quels sont les noms des pilotes en service au départ de Marseille ?
SELECT NOMPIL FROM PILOTE WHERE NUMPIL IN (SELECT
DISTINCT NUMPIL FROM VOL WHERE VILLE_DEP = 'MARSEILLE');
Exemple : quels sont les numéros des avions localisés à Marseille dont la capacité est
supérieure à celle de l’un des appareils effectuant un Paris-Marseille ?
SELECT NUMAV FROM AVION WHERE LOCALISATION =
‘MARSEILLE’ AND CAP > ANY (SELECT DISTINCT CAP FROM AVION
WHERE NUMAV = ANY (SELECT DISTINCT NUMAV FROM VOL
WHERE VILLE-DEP = ‘PARIS’ AND VILLE_ARR = ‘MARSEILLE’) )
22
Exemple : quels sont les noms des pilotes Marseillais qui gagnent plus que tous les pilotes
parisiens ?
SELECT NOMPIL
FROM PILOTE
WHERE ADRESSE = 'MARSEILLE' AND SALAIRE > ALL
(SELECT DISTINCT SALAIRE
FROM PILOTE
WHERE ADRESSE = 'PARIS')
Exemple : donner le nom des pilotes Marseillais qui gagnent plus qu'un pilote parisien.
SELECT NOMPIL
FROM PILOTE
WHERE ADRESSE = 'MARSEILLE' AND SALAIRE > ANY
(SELECT SALAIRE
FROM PILOTE
WHERE ADRESSE = 'PARIS')
3. Troisième cas :
Le résultat de la sous-requête est un ensemble de colonnes.
le nombre de colonnes de la clause WHERE doit être identique à celui de la clause
SELECT de la sous-requête. Ces colonnes doivent être mises entre parenthèses,
la comparaison se fait entre les valeurs des colonnes de la requête et celle de la sous-
requête deux à deux,
il faut que l’opérateur de comparaison soit le même pour tous les attributs concernés.
Exemple : rechercher le nom des pilotes ayant même adresse et même salaire que Dupont.
SELECT NOMPIL
FROM PILOTE
WHERE NOMPIL <> 'DUPONT'
AND (ADRESSE, SALAIRE) IN
(SELECT ADRESSE, SALAIRE
FROM PILOTE
WHERE NOMPIL = 'DUPONT')
23
II.4.1.7 Tri des résultats
Il est possible avec SQL d'ordonner les résultats. Cet ordre peut être croissant (ASC) ou
décroissant (DESC) sur une ou plusieurs colonnes ou expressions.
ORDER BY expression [ASC | DESC],…
L'argument de ORDER BY peut être un nom de colonne ou une expression basée sur une ou
plusieurs colonnes mentionnées dans la clause SELECT.
Exemple : En une seule requête, donner la liste des pilotes Marseillais par ordre de salaire
décroissant et par ordre alphabétique des noms.
SELECT NOMPIL, SALAIRE
FROM PILOTE
WHERE ADRESSE = 'MARSEILLE'
ORDER BY SALAIRE DESC, NOMPIL
Exemple : Les requêtes suivantes ne peuvent pas être formulées avec IS NULL.
Quels sont les pilotes n’effectuant aucun vol ? Quelles sont les villes de départ dans lesquelles
aucun avion n’est localisé ?
Pour formuler des requêtes de type “ un élément n’appartient pas à un ensemble donné ”, trois
techniques peuvent être utilisées. La première consiste à utiliser une jointure imbriquée avec
NOT IN. La sous-requête est utilisée pour calculer l’ensemble de recherche et le bloc de
niveau supérieur extrait les éléments n’appartenant pas à cet ensemble.
24
Une deuxième approche fait appel au prédicat NOT EXISTS qui s’applique à un bloc
imbriqué et rend la valeur vrai si le résultat de la sous-requête est vide (et faux sinon). Il faut
faire très attention à ce type de requête car NOT EXISTS ne dispense pas d’exprimer des
jointures. Ce danger est détaillé à travers l’exemple suivant.
La classification permet de regrouper les lignes d'une table dans des classes d’équivalence ou
sous-tables ayant chacune la même valeur pour la colonne de la classification. Ces classes
25
forment une partition de l'extension de la relation considérée (i.e. l’intersection des classes est
vide et leur union est égale à la relation initiale).
Exemple : considérons la relation VOL illustrée par la figure 1. Partitionner cette relation sur
l’attribut NUMPIL consiste à regrouper au sein d’une même classe tous les vols assurés par le
même pilote. NUMPIL prenant 4 valeurs distinctes, 4 classes sont introduites. Elles sont
mises en évidence dans la figure 2 et regroupent respectivement les vols des pilotes n° 100,
102, 105 et 124.
NUMVOL NUMPIL … NUMVOL NUMPIL …
IT100 100 IT100 100
AF101 100 AF101 100
IT101 102 IT101 102
BA003 105 IT305 102
BA045 105 BA003 105
IT305 102 BA045 105
AF421 124 BA047 105
BA047 105 BA087 105
BA087 105
AF421 124
En SQL, l’opérateur de partitionnement s’exprime par la clause GROUP BY qui doit suivre
la clause WHERE (ou FROM si WHERE est absente). Sa syntaxe est :
GROUP BY colonne1, [colonne2,…]
Les colonnes indiquées dans SELECT, sauf les attributs arguments des fonctions agrégatives,
doivent être mentionnées dans GROUP BY.
Il est possible de faire un regroupement multi-colonnes en mentionnant plusieurs colonnes
dans la clause GROUP BY. Une classe est alors créée pour chaque combinaison distincte de
valeurs de la combinaison d’attributs.
En présence de la clause GROUP BY, les fonctions agrégatives s'appliquent à l'ensemble des
valeurs de chaque classe d’équivalence.
26
Dans ce cas, le résultat de la requête comporte une ligne par numéro de pilote présent dans
la relation VOL.
HAVING condition
La condition permet de comparer une valeur obtenue à partir de la sous-table à une constante
ou à une autre valeur résultant d'une sous-requête.
27
GROUP BY NUMPIL
HAVING COUNT(NUMVOL) > 5
Exemple : quelles sont les villes à partir desquelles le nombre de villes desservies est le plus
grand ?
SELECT VILLE_DEP
FROM VOL
GROUP BY VILLE_DEP
HAVING COUNT(DISTINCT VILLE_ARR) >= ALL
(SELECT COUNT(DISTINCT VILLE_ARR)
FROM VOL
GROUP BY VILLE_DEP)
Même si les clauses WHERE et HAVING introduisent des conditions de sélection mais elles
sont différentes. Si la condition doit être vérifiée par chaque tuple d’une classe d’équivalence,
il faut la spécifier dans le WHERE. Si la condition doit être vérifiée globalement pour la
classe, elle doit être exprimée dans la clause HAVING.
Exemple : supposons que l’on souhaite intégrer dans la base des informations sur la
répartition géographique des villes desservies. La figure 3 illustre l’organisation hiérarchique
des valeurs des différentes localisations à répertorier.
Europe
France
Espagne
28
La relation LIEU est ajoutée au schéma initial de la base pour représenter de manière “ plate ”
la structure arborescente donnée ci-dessus. Cette relation a le schéma suivant :
LIEU (LIEUSPEC, #LIEUGEN).
L’attribut LIEUGEN est défini sur le même domaine que LIEUSPEC. C’est donc une clef
étrangère référençant la clef primaire de sa propre relation.
L'extension de cette relation, correspondant à la hiérarchie précédente, est la suivante :
LIEUSPEC LIEUGEN
EUROPE NULL
FRANCE EUROPE
ESPAGNE EUROPE
PACA FRANCE
ILE-DE-FRANCE FRANCE
MARSEILLE PACA
AIX PACA
CASSIS PACA
PARIS ILE-DE-FRANCE
29
Le parcours de bas en haut est spécifié en faisant précéder la colonne représentant un “ père ”
par le mot-clef PRIOR. Il peut s'exprimer aussi de deux façons :
CONNECT BY <fils> = PRIOR <père> ou CONNECT BY PRIOR <père> = <fils>
Remarque : avec la représentation relationnelle plate, le <fils> dans une structure hiérarchique
correspond à la clef primaire de la relation, le <père> à la clef étrangère. Lorsque la valeur de
l’attribut <fils> est la racine de la hiérarchie, la valeur de l’attribut <père> est une valeur nulle
: il s’agit d’un cas exceptionnel où l’on admettra une valeur nulle pour une clef étrangère.
Exemple : donner la liste des villes desservies en France en dehors de la région AQUITAINE.
SELECT LIEUSPEC
FROM LIEU
CONNECT BY PRIOR LIEUSPEC = LIEUGEN
AND LIEUGEN <> ‘AQUITAINE’
START WITH LIEUGEN = ‘FRANCE’
ORDER BY LEVEL
Dès qu’un nœud ne vérifiant pas la condition donnée est trouvé, le sous-arbre dont ce nœud
est racine est éliminé de la recherche, donc la région AQUITAINE et toutes les villes de cette
région n’appartiendront pas au résultat.
ORACLE associe une numérotation aux nœuds d'une arborescence : c'est la notion de niveau
(LEVEL). En fait le premier nœud répondant à la requête est considéré de niveau 1, ses fils
30
(dans le cas d'une recherche de haut en bas) ou son père (dans le cas d'une recherche de bas en
haut) sont considérés de niveau 2, et ainsi de suite.
Cette numérotation peut être utilisée pour classer les résultats (clause ORDER BY LEVEL)
et surtout pour en donner une meilleure présentation. Celle-ci s'obtient en utilisant la fonction
LPAD qui permet une indentation.
Exemple : pour obtenir la liste des villes desservies en France mais avec présentation plus
claire, nous pouvons formuler la requête comme suit :
SELECT LPAD('-', 2*LEVEL,' ')|| LIEUSPEC
FROM LIEU
CONNECT BY PRIOR LIEUSPEC = LIEUGEN
START WITH LIEUGEN = ‘FRANCE’
ORDER BY LEVEL
|| est le symbole de concaténation de deux chaînes de caractères.
La fonction LPAD permet d'ajouter à la chaîne '-', un nombre d'espaces égal au double du
niveau, devant chaque valeur de LIEUSPEC pour mieux visualiser la division géographique.
Exemple : quels sont les numéros des pilotes qui conduisent tous les avions de la compagnie?
Pour l'exprimer en SQL avec la première technique exposée, cette requête est traduite de la
manière suivante : “ Quels sont les pilotes qui conduisent autant d'avions que la compagnie en
possède ? ”.
SELECT NUMPIL
FROM VOL
31
GROUP BY NUMPIL
HAVING COUNT(DISTINCT NUMAV) =
(SELECT COUNT(NUMAV)
FROM AVION)
Le comptage dans la clause HAVING permet pour chaque pilote de dénombrer les appareils
conduits. L’oubli du DISTINCT rend la requête fausse (on compterait alors le nombre de
vols assurés).
Cette technique de paraphrasage ne peut être utilisée que si les deux ensembles dénombrés
sont parfaitement comparables.
La deuxième forme d'expression des divisions est plus délicate à formuler. Elle se base sur
une double négation de la requête et utilise la clause NOT EXISTS. Au lieu d’exprimer
qu’un tuple t doit être associé à tous les tuples du diviseur pour appartenir au résultat, on
recherche t tel qu’il n’existe aucun tuple du diviseur qui ne lui soit pas associé. Rechercher
des tuples associés ou non, signifie concrètement effectuer des opérations de jointure.
Exemple : quels sont les numéros des pilotes qui conduisent tous les avions de la compagnie?
Pour l'exprimer en SQL, cette requête est traduite par : "Existe-t-il un pilote tel qu'il n'existe
aucun avion de la compagnie qui ne soit pas conduit par ce pilote ?".
SELECT NUMPIL
FROM PILOTE P
WHERE NOT EXISTS (SELECT *
FROM AVION A
WHERE NOT EXISTS
(SELECT *
FROM VOL
WHERE VOL.NUMAV = A.NUMAV AND VOL.NUMPIL = P.NUMPIL))
Détaillons l’exécution de cette requête. Pour chaque pilote examiné par le 1er bloc, les
différents tuples de AVION sont balayés au niveau du 2ème bloc et pour chaque avion, les
conditions de jointure du 3ème bloc sont évaluées. Supposons que les trois relations de la base
sont réduites aux tuples présentés dans la figure suivante.
32
NUMPIL … NUMAV … NUMVOL NUMPIL NUMAV
Considérons le pilote n° 100. Parcourons les tuples de la relation AVION. Pour l’avion n° 10,
le 3ème bloc retourne un résultat (le vol IT100), NOT EXISTS est donc faux et l’avion n° 10
n’appartient pas au résultat du 2ème bloc. L’avion n° 21 n’étant jamais piloté par le pilote
100, le 3ème bloc ne rend aucun tuple, le NOT EXISTS associé est donc évalué à vrai. Le
2ème bloc rend donc un résultat non vide (l’avion n°21) et donc le NOT EXISTS du 1er bloc
est faux. Le pilote n° 100 n’est donc pas retenu dans le résultat de la requête.
Pour le pilote 101 et l’avion 10, il existe un vol (AF214). Le 3ème bloc retourne un résultat,
NOT EXISTS est donc faux. Pour le même pilote et l’avion n° 21, le 3ème bloc restitue un
tuple et à nouveau NOT EXISTS est faux. Le 2ème bloc rend donc un résultat vide ce qui fait
que le NOT EXISTS du 1er bloc est évalué à vrai. Le pilote 101 fait donc partie du résultat
de la requête. Le processus décrit est à nouveau exécuté pour le pilote 102 et comme il ne
pilote pas l’avion 21, le 2ème bloc retourne une valeur et la condition du 1er bloc élimine ce
pilote du résultat.
33
Une modification directe de la base (INSERT, UPDATE ou DELETE) ne sera validée que
par la commande COMMIT, ou lors de la sortie normale.
En cours de transaction, seul l'utilisateur ayant effectué les modifications les voit. Ce
mécanisme est utilisé par les systèmes de gestion de bases de données pour assurer l'intégrité
de la base en cas de fin anormale d'une tâche utilisateur : il y a
automatiquement ROLLBACK des transactions non terminées.
ORACLE est un système transactionnel qui assure la cohérence des données en cas de mise à
jour de la base, même si plusieurs utilisateurs lisent ou modifient les mêmes données
simultanément.
ORACLE utilise un mécanisme de verrouillage pour empêcher deux utilisateurs d'effectuer
des transactions incompatibles et régler les problèmes pouvant survenir.
ORACLE permet le verrouillage de certaines unités (table ou ligne) automatiquement ou sur
demande de l'utilisateur. Les verrous sont libérés en fin de transaction.
Il est possible de se mettre en mode "validation automatique", dans ce cas, la validation est
effectuée automatiquement après chaque commande SQL de mise à jour de données. Pour se
mettre dans ce mode, il faut utiliser l'une des commandes suivantes :
SET AUTOCOMMIT IMMEDIATE ou SET AUTOCOMMIT ON
34
Exemple :
Exemple : pour éviter que certains utilisateurs aient accès aux salaires et prime des pilotes, la
vue suivante est définie à leur intention et ils n’ont pas de droits sur la relation PILOTE.
CREATE VIEW RESTRICT_PIL
AS
SELECT NUMPIL, NOMPIL, PRENOM_PIL, ADRESSE
FROM PILOTE
35
Exemple : pour épargner aux utilisateurs la formulation d’une requête complexe, une vue est
définie par les développeurs pour consulter la charge horaire des pilotes. Sa définition est la
suivante :
CREATE VIEW CHARGE_HOR (NUMPIL, NOM, CHARGE)
AS
SELECT P.NUMPIL, NOMPIL, SUM(HEURE_ARR – HEURE_DEP)
FROM PILOTE P, VOL
WHERE P.NUMPIL = VOL.NUMPIL
GROUP BY P.NUMPIL, NOMPIL
Lorsque cette vue est créée, les utilisateurs peuvent la consulter simplement par :
SELECT * FROM CHARGE_HOR
Un utilisateur ne s’intéressant qu’aux pilotes parisiens dont la charge excède un seuil de 40
heures formulera la requête suivante.
SELECT *
FROM CHARGE_HOR C, PILOTE P
WHERE C.NUMPIL = P.NUMPIL AND CHARGE > 40
AND ADRESSE = ‘PARIS’
Lorsque le système évalue une requête formulée sur une vue, il combine la requête de
l’utilisateur et la requête de définition de la vue pour obtenir le résultat.
Lorsqu'une vue est utilisée pour effectuer des opérations de mise à jour, elle est soumise à des
contraintes fortes. En effet pour que les mises à jour, à travers une vue, soient
automatiquement répercutées sur la relation de base associée, il faut impérativement que :
- la vue ne comprenne pas de clause GROUP BY.
- la vue n'ait qu'une seule relation dans la clause FROM. Ceci implique que dans une vue
multi-relation, les jointures soient exprimées de manière imbriquée.
Lorsque la requête de définition d’une vue comporte une projection sur un sous ensemble
d’attributs d’une relation, les attributs non mentionnés prendront des valeurs nulles en cas
d’insertion à travers la vue.
Exemple : définir une vue permettant de consulter les vols des pilotes habitant Bayonne et de
les mettre à jour.
CREATE VIEW VOLPIL_BAYONNE
AS
SELECT *
36
FROM VOL
WHERE NUMPIL IN
(SELECT NUMPIL
FROM PILOTE
WHERE ADRESSE = ‘BAYONNE’)
La vue précédente permet la consultation uniquement des vols vérifiant la condition donnée
sur le pilote associé. Il est également possible de mettre à jour la relation VOL à travers cette
vue mais l’opération de mise à jour peut concerner n’importe quel vol (sans aucune
condition).
Par exemple supposons que le pilote n° 100 habite Paris, l’insertion suivante sera réalisée
dans la relation VOL à travers la vue, mais le tuple ne pourra pas être visible en consultant la
vue.
INSERT INTO VOLPIL_BAYONNE
(NUMVOL,NUMPIL,NUMAV,VILLE_DEP)
VALUES (‘IT256’, 100, 14, ‘PARIS’)
Si la clause WITH CHECK OPTION est présente dans l’ordre de création d’une vue, la
table associée peut être mise à jour, avec vérification des conditions présentes dans la requête
définissant la vue. La vue joue alors le rôle d'un filtre entre l'utilisateur et la table de base, ce
qui permet la vérification de toute condition et notamment des contraintes d'intégrité.
Exemple : définir une vue permettant de consulter et de les mettre à jour uniquement les vols
des pilotes habitant Bayonne.
CREATE VIEW VOLPIL_BAYONNE
AS
SELECT *
FROM VOL
WHERE NUMPIL IN
(SELECT NUMPIL
FROM PILOTE
WHERE ADRESSE = ‘BAYONNE’)
WITH CHECK OPTION
L’ajout de la clause WITH CHECK OPTION à la requête précédente interdira toute
opération de mise à jour sur les vols qui ne sont pas assurés par des pilotes habitant Bayonne
et l’insertion d’un vol assuré par le pilote n° 100 échouera.
37
Exemple : définir une vue sur PILOTE, permettant la vérification de la contrainte de domaine
suivante : le salaire d'un pilote est compris entre 3.000 et 5.000.
CREATE VIEW DPILOTE
AS
SELECT *
FROM PILOTE
WHERE SALAIRE BETWEEN 3000 AND 5000
WITH CHECK OPTION
L’insertion suivante va alors échouer.
INSERT INTO DPILOTE (NUMPIL,SALAIRE) VALUES(175,7000)
Exemple : définir une vue sur vol permettant de vérifier les contraintes d'intégrité
référentielle en insertion et en modification.
CREATE VIEW IMVOL
AS
SELECT *
FROM VOL
WHERE NUMPIL IN (SELECT NUMPIL FROM PILOTE)
AND NUMAV IN (SELECT NUMAV FROM AVION)
WITH CHECK OPTION
De manière similaire à une relation de la base, une vue peut être
- consultée via une requête d'interrogation ;
- décrite structurellement grâce à la commande DESCRIBE ou en interrogeant la table
système ALL_VIEWS ;
- détruite par l'ordre DROP VIEW <nom_vue>.
38
La gestion des utilisateurs : à qui on associe des espaces de stockage (tablespaces)
dans lesquels se trouveront leurs objets (table, index, séquences …).
La gestion des privilèges : qui permettent de donner des droits sur la base de données
(privilèges systèmes) et sur les données de la base (privilèges objets).
La gestion des rôles : qui regroupent des privilèges système ou objets affectés par la
suite à un ou plusieurs utilisateurs.
Un utilisateur est identifié au niveau de la base par son nom et peut se connecter puis accéder
aux objets de la base sous réserve d’avoir reçu un certain nombre de privilèges.
Un schéma est une collection nommée (du nom de l’utilisateur qui en est en propriétaire)
d’objets (tables, vues, séquences, index, procédures, …).
Les types d’utilisateurs, leurs fonctions et leur nombre peuvent varier d’une base à une autre.
Néanmoins, pour chaque base de données en activité, on peut classifier les utilisateurs de la
manière suivante :
39
Suppression d’un utilisateur:
DROP USER <nom_id> [CASCADE]
CASCADE suppression des objets de l’utilisateur (Nécessaire sinon de les supprimer avant).
Lorsqu'un utilisateur est créé avec l'instruction CREATE USER, il ne dispose encore d'aucun
droit car aucun privilège ne lui a encore été assigné. Il ne peut même pas se connecter à la
base. Il faut donc lui assigner les privilèges nécessaires. Il doit pouvoir se connecter, créer des
tables, des vues, des séquences.
[IDENTIFIED BY mot_passe]
ANALYZE
40
AUDIT
CLUSTER
ALTER ANY CLUSTER Modifier tous les clusters dans la base de données.
DROP ANY CLUSTER Supprimer tous les clusters dans la base de données.
DATABASE
DATABASE LINK
INDEX
ALTER ANY INDEX Modidier tous les index dans la base de données.
DROP ANY INDEX Supprimer tous les index dans la base de données.
PRIVILEGE
PROCEDURE
41
PROFILE
CREATE PUBLIC
Créer des database links publics.
DATABASE LINK
PUBLIC SYNONYM
ROLE
ALTER ANY ROLE Modifier tous les roles dans la base de données.
DROP ANY ROLE Supprimer tous les roles dans la base de données.
GRANT ANY ROLE Grant tous les roles dans la base de données.
ROLLBACK SEGMENT
CREATE ROLLBACK
Créer des rollback segments.
SEGMENT
ALTER ROLLBACK
Modifier des rollback segments.
SEGMENT
SESSION
SEQUENCE
42
CREATE SEQUENCE crée une sequence dans son schema.
CREATE ANY SEQUENCE Créer toutes les sequences dans tous les schemas.
ALTER ANY SEQUENCE Modifier toutes les sequence dans tous les schémas.
DROP ANY SEQUENCE Supprimer toutes les sequence dans tous les schémas.
SELECT ANY SEQUENCE Reference toutes les sequence dans tous les schémas.
SNAPSHOT
ALTER SNAPSHOT Modifier tous les snapshots dans tous les schémas.
DROP ANY SNAPSHOT Supprimer tous les snapshots dans tous les schémas.
SYNONYM
CREATE SYNONYM Créer tous les synonyms dans tous les schémas.
DROP ANY SYNONYM Supprimer tous les synonyms dans tous les schémas.
SYSTEM
TABLE
CREATE TABLE Créer des tables ou des indexs dans son propre schéma
CREATE ANY TABLE Créer des tables dans tous les schémas.
43
Interroger toutes les tables, vues, ou clichés dans
SELECT ANY TABLE
tous les schémas.
TABLESPACE
TRANSACTION
TRIGGER
44
Enable, disable, ou compile toutes les trigger dans
ALTER ANY TRIGGER
tous les schémas.
DROP ANY TRIGGER Supprimer toutes les trigger dans tous les schémas.
USER
VIEW
DROP ANY VIEW Supprimer toutes les view dans tous les schémas.
Et il en va de même pour tous les autres privilèges qui lui sont assignés.
L'ensemble de ces privilèges peuvent être assignés au sein d'une même commande
45
Exemple :
Hicham peut créer des tables dans son schéma et peut retransmettre ce privilège à un tiers.
Un utilisateur d’ORACLE peut être supprimé à tout moment ou se voir démuni de certains
privilèges. La commande correspondante est :
REVOKE privilègesystème | ALL PRIVILEGES
FROM utilisateur | PUBLIC
Exemple :
REVOKE CREATE SESSION FROM Hicham, Ahmed;
Hicham et Ahmed ne peuvent plus se connecter à la base.
REVOKE ALL PRIVILEGES FROM Ahmed;
Commande incorrect car Ahmed n’a pas reçu tous les privilèges système.
Privilèges objets :
Les privilèges objets sont relatifs aux données de la base et aux actions sur les objets (table,
vue, séquence, procédure). Chaque type d’objet a différents privilèges associés comme
l’indique le tableau suivant : Permettent les manipulations sur des objets spécifiques. Les
privilèges SELECT, INSERT, UPDATE, DELETE.
46
Privilège table vue séquence programme PL/SQL
ALTER ×
DELETE × ×
EXECUTE ×
INDEX ×
INSERT × ×
REFERENCES ×
SELECT × × ×
UPDATE × ×
L’instruction GRANT permet d’attribuer un (ou plusieurs) privilèges à un (ou plusieurs) objet
à un (ou plusieurs) bénéficiaire. L’utilisateur qui exécute cette commande doit avoir lui-même
le droit de transmettre ces privilèges sauf s’il s’agit de ses propres objets pour lesquels il
possède automatiquement les privilèges avec l’option GRANT OPTION. La syntaxe est la
suivante :
ON SCHEMA.nomobjet
TO utilisateur | PUBLIC
WITH GRANT OPTION: permet de donner aux bénéficiaires le droit de retransmettre les
privilèges reçus à une tierce personne.
47
Une liste de colonnes peut être indiquée dans l'instruction afin de restreindre davantage les
droits sur une table
L'utilisateur peut modifier la table SMI.ETUDIANTS mais uniquement les colonnes note et
moy.
Exemple :
ON Hicham.Pilote
TO Ahmed
Pour pouvoir révoquer un privilège objet, il faut détenir au préalable cette permission ou
avoir reçu le privilège système ANY OBJECT PRIVILEGE. Il n’est pas possible d’annuler un
privilège objet qui a été accordé avec l’option WITH GRANT OPTION.
ON objet
Exemple:
48
Principes généraux appliqués aux privilèges
Un utilisateur possède automatiquement tous les privilèges sur un objet qui lui
appartient.
Un utilisateur ne peut pas donner plus de privilèges qu'il n'en a reçus.
S'il n'a pas reçu le privilège avec l'option WITH GRANT OPTION, un utilisateur ne
peut pas assigner à son tour ce même privilège.
III.3.3 rôles
Un Rôle permet :
49
Modification de rôle
Exemple:
Exercice :
1. Créer un utilisateur service_client.
2. Attribuer les droits nécessaires à l’utilisateur pour pouvoir :
Créer les tables
Modifier les tables
Supprimer les tables
50
Partie II : Langage PL/SQL
Introduction :
PL/SQL est un langage structuré en blocs, constitués d'un ensemble d'instructions SQL. Il est
préférable de travailler avec un bloc PL /SQL plutôt qu’avec une suite d’instructions SQL
susceptibles d’encombrer le trafic réseau. En effet, un bloc PL/SQL donne lieu à un seul
échange sur le réseau entre le client et le serveur. Tandis que chaque instruction SQL donne
lieu à l’envoi d’un message du client vers le serveur suivi de la réponse du serveur vers le
client.
I. Bloc PL/SQL :
Chaque bloc PL/SQL peut être constitué de 3 sections :
• Une section facultative de déclaration et initialisation de types, variables et constantes
• Une section obligatoire contenant les instructions d'exécution
• Une section facultative de gestion des erreurs
Exemple :
Un bloc PL/SQL peut être représenté de la façon suivante :
DECLARE
Chaine VARCHAR2(15) := 'Hello World' ;
BEGIN
DBMS_OUTPUT.PUT_LINE(Chaine ) ;
EXCEPTION
When OTHERS then
Null ;
END ;
51
Le mot clé BEGIN détermine le début de la section des instructions exécutables.
Le mot clé END; indique la fin de la section des instructions exécutables.
La section déclarative (facultative) d'un bloc débute par le mot clé DECLARE. Elle contient
toutes les déclarations des variables qui seront utilisées localement par la section exécutable,
ainsi que leur éventuelle initialisation.
Une variable Chaine est déclarée de type VARCHAR2(15) et initialisée avec la valeur 'Hello
World'.
Dans la section exécutable, cette variable est transmise à la fonction
DBMS_OUTPUT.PUT_LINE() pour être affichée à l'écran.
Toute variable doit être déclarée avant d’être utilisée dans la section exécutable.
La section de gestion des erreurs (facultative) débute par le mot clé EXCEPTION. Elle
contient le code exécutable mis en place pour la gestion des erreurs. Lorsqu'une erreur
intervient dans l'exécution, le programme est stoppé et le code erreur est transmis à cette
section.
Les erreurs doivent être interceptées avec le mot clé WHEN suivi du code erreur ciblé. Ici, le
code OTHERS définit toutes les erreurs non interceptées individuellement par les clauses
WHEN précédentes.
52
Le mot-clé DEFAULT : permet d'initialiser des variables sans utiliser l'opérateur
d'affectation comme dans l'exemple suivant :
La directive INTO d’une requête : consiste à utiliser une commande SQL SELECT
qui définit une valeur de la base de données comme suit :
SELECT brevet
INTO v_brevet
FROM Pilote
WHERE nom = ‘Gratien Viel’;
Attribut %TYPE : est utilisé pour déclarer une variable en fonction d’une définition
de colonne de base de données.
Exemple:
DECLARE
v_brevet Pilote.brevet%TYPE;
v_brevet est une variable PLSQL de même type que la colonne brevet de la table Pilote.
Utile pour garantir la compatibilité des affectations.
Exemple :
DECLARE
piloteRec Pilote%ROWTYPE;
Les noms et les types de données des champs de l’enregistrement sont issus des colonnes de
la table Pilote.
Remarque : comme SQL, PL/SQL n'est pas sensible à la casse. Pour lui les expressions
suivantes sont équivalentes :
NOM_VARIABLE NUMBER ;
53
Nom_Variable Number ;
nom_variable number ;
I. 2 La section exécution
Délimitée par les mots clé BEGIN et END; elle contient les instructions d'exécution du bloc
PL/SQL, les instructions de contrôle et d'itération, l'appel des procédures et fonctions,
l'utilisation des fonctions natives, les ordres SQL, etc. Chaque instruction doit être suivi du
terminateur d'instruction ’;’. PL/SQL ne permet pas les commandes SQL langage de
définition comme la création de table, cependant, il permet tous les autres types de
commandes SQL (insert, delete, update, commit ...).
La commande select (sauf si imbriqué) est toujours utilisé avec into, pour affecter les
attributs retrouvés aux variables PL/SQL.
PL/SQL supporte une vaste gamme de types de données scalaires pour définir des variables et
des constantes. A la différence des types de données composites, les types de données
scalaires n'ont pas de composantes accessibles. Ces types de données tombent dans l'une des
catégories suivantes :
Booléen
Date/heure
Caractère
Nombre
54
Chaque catégorie va maintenant être étudiée de plus prées.
Booléen
Le type de données BOOLEAN, qui ne prend pas de paramètres, est utilisé pour stocker une
valeur binaire, TRUE ou FALSE. Ce type de donnée peut aussi stocker la non-valeur NULL.
Date/Heure
Le type de données DATE, qui ne prend aucun paramètre, est utilisé pour stocker des valeurs
de dates. Ces valeurs de date incluent l'heure lorsqu'elles sont stockées dans une colonne de la
base de données. Les valeurs par défaut pour le type de données DATE sont les suivantes :
Date : premier jour du mois courant
Heure : minuit
Caractère
Le type de données caractère inclut CHAR, VARCHAR2, LONG, RAW et LONG RAW.
CHAR est destinée aux données de type caractère de longueur fixe et VARCHAR2 stocke des
données de type caractère de longueur variable.
LONG stocke des chaines de longueur variable ; RAW et LONG RAW stockent des données
binaires ou des chaines d'octets. Les types de données CHAR, VARCHAR2 et RAW prennent
un paramètre optionnel pour spécifier la longueur : type(max_len).
Nombre
NUMBER(10,2)
Une variable déclarée de cette manière a un maximum de 10 chiffres et l'arrondi se fait à deux
décimales.
55
II.2 Types de données composées
Les deux types de données composites de PL/SQL sont TABLE et RECORD. Le type de
donnée TABLE permet à l'utilisateur de définir un tableau PL/SQL. Le type de données
RECORD permet d'aller au-delà de l'attribut de variable %ROWTYPE ; avec ce type, on peut
spécifier des champs définis par l'utilisateur et des types de données pour ces champs.
Tableaux
Les variables de type TABLE permettent de définir et de manipuler des tableaux dynamiques
(car définis sans dimension initiale). Un tableau est composé d’une clé primaire (de type
BINARY_INTEGER) et d’une colonne (de type scalaire, TYPE, ROWTYPE ou RECORD)
pour stocker chaque élément. La syntaxe générale pour déclarer un type de tableau et une
variable tableau est la suivante :
[INDEX BY BINARY_INTEGER];
nomTableau nomTypeTableau ;
Exemple:
DECLARE
INDEX BY BINARY_INTEGER ;
INDEX BY BINARY_INTEGER;
INDEX BY BINARY_INTEGER;
tab_brevets brevets_tytab ;
tab_nomPilotes nomPilotes_tytab ;
tab_pilotes pilotes_tytab ;
56
Constructions de tableaux : Après leur déclaration, les tableaux sont disponibles pour le
stockage d'information. Pour stocker de l'information dans les tableaux définis dans l'exemple
précédent, il suffit de référencer le tableau avec une valeur numérique. Cela est illustré dans
l'exemple suivant :
BEGIN
Tab_brevets(-1) :=’PL-1’ ;
Tab_brevets(-2) :=’PL-2’ ;
tab_Pilotes(0).brevet :=’Pl-0’ ;
END ;
Enregistrements
Bien que la directive %ROWTYPE permet de déclarer une structure composée de colonnes de
tables, elle ne convient pas à des données personnalisées. Le type de données RECORD
définit des données personnalisées (l’équivalent de struct en C). La syntaxe générale pour
déclarer un RECORD est la suivante :
Exemple:
DECLARE
57
r_FGLFS avionAirbus_rec ;
BEGIN
r_unA320.nomAvion :=’A320-200’ ;
r_unA320.nbHVol :=2500 ;
Les types RECORD ne peuvent pas être stockés dans une table. En revanche, il est possible
qu’un champ d’un RECORD soit lui-même un RECORD, ou soit déclaré avec les directives
%TYPE ou %ROWTYPE.
III. Curseurs
Un curseur est une zone de mémoire utilisée par Oracle pour récupérer les résultats de
requêtes SQL qui retourne plusieurs enregistrements. Il existe trois types de curseur :
1. Curseurs implicites : créés et gérés en interne par le serveur Oracle afin de traiter les
instructions du MLD de SQL (INSERT, UPDATE et DELETE). Ce curseur porte le nom
de SQL et il est exploitable après avoir exécuté l’instruction. La commande qui suit le
LMD remplace l’ancien curseur par un nouveau. Les attributs de curseurs implicites
permettent de connaitre un certain nombre d’informations qui ont été renvoyées après
l’instruction du LMD et qui peuvent être utiles au programmeur.
SQL%ROWCOUNT : nombre de lignes affectées par la dernière instruction LMD.
SQL%FOUND : Booléen valant TRUE si la dernière instruction LMD affecte au moins un
enregistrement.
SQL%NOTFOUND : Booléen valant TRUE si la dernière instruction LMD n’affecte aucun
enregistrement.
Exemple:
DECLARE
g_pilotesAFDetruits NUMBER ;
BEGIN
58
DELETE FROM Pilote WHERE compa=’AF’;
g_pilotesAFDetruits :=SQL%ROWCOUNT ;
DBMS_OUTPUT.PUT_LINE(g_pilotesAFDetruits) ;
END ;
2. Curseurs explicites : déclarés explicitement par le programmeur, ils sont associés à une
seule requête SELECT. Ils se déclarent comme suit :
CURSOR nom_ curseur IS SELECT col1, col2, ... FROM nom_table WHERE ...;
Exemple:
CURSOR zone IS SELECT brevet, nbhVol, comp FROM Pilote WHERE
comp=’AF’;
Pour exploiter les résultats d’un curseur, on utilise les instructions suivantes:
OPEN nomCurseur : Ouverture du curseur (chargement des lignes).
FETCH nomCurseur INTO listeVariables | nomRECORD : Positionnement sur la
ligne suivante et chargement de l’enregistrement courant dans une ou plusieurs
variables.
CLOSE nomCurseur : Ferme le curseur. L’exception INVALID_CURSOR se
déclenche si des accès au curseur sont opérés après sa fermeture.
Les curseurs explicites peuvent avoir les attributs suivants :
nomCurseur%NOTFOUND : Retourne TRUE si le dernier FETCH n’a retourné aucun
enregistrement.
nomCurseur%FOUND : Retourne TRUE si le dernier FETCH a retourné un enregistrement.
nomCurseur%ISOPEN : Retourne TRUE si le curseur est ouvert, FALSE sinon.
nomCurseur%ROWCOUNT : Retourne le nombre d’enregistrements trouvés jusqu’à
présent.
Parcours d’un curseur : suivant le traitement à effectuer sur le curseur à parcourir,
on peut utiliser une structure répétitive tant que (while), répéter (loop), pour (for) et à
l’aide d’une condition généralement nomCurseur%NOTFOUND ou
nomCurseur%FOUND.
59
la condition composée : EXIT WHEN nomCurseur%NOTFOUND OR
nomCurseur%NOTFOUND IS NULL.
60
END LOOP;
CLOSE Curseur1;
END;
3. Curseurs dynamiques : Un curseur dynamique est un curseur qui n’est pas associé à
une seule requête SQL. Dans ce cas, lors de sa déclaration, il faudra utiliser le mot
réservé REF.
nomCurDynamique nomTypeCurDynamiq;
Un REF CURSOR est vu comme un pointeur sur une zone mémoire dans le serveur
(contenant le résultat d’une requête).
Il peut retourner des valeurs ou non.
L’ouverture d’un curseur dynamique est commandée par l’instruction OPEN FOR
requête.
La lecture du curseur s’opère toujours avec l’instruction FETCH.
BEGIN
OPEN zone FOR SELECT brevet, nom, FROM Pilote WHERE NOT (comp= 'AF ');
FETCH zone INTO var1, var2;
WHILE (zone%FOUND) LOOP
DBMS_OUTPUT.PUT_LINE('nom: ' || var2 || ' (' || var1 || '). ');
FETCH zone INTO var1, var2;
END LOOP;
CLOSE zone;
END;
61
Exemple : Curseur typé
DECLARE
TYPE ref_zone IS REF CURSOR RETURN Pilote%ROWTYPE ;
zone ref_zone ;
enreg zone%ROWTYPE ;
BEGIN
OPEN zone FOR SELECT * FROM Pilote WHERE NOT (comp= 'AF ');
FETCH zone INTO enreg;
WHILE (zone%FOUND) LOOP
DBMS_OUTPUT.PUT_LINE('nom: ' || enreg.nom || ' (' || enreg.comp || '). ');
FETCH zone INTO enreg;
END LOOP;
CLOSE zone;
END;
IV Structures de contrôle
Tout langage procédural a des structures de contrôle qui permettent de traiter l'information
d'une manière logique en contrôlant le flot des informations. Les structures disponibles au
sein de PL/SQL incluent :
62
END IF;
2. IF-THEN-ELSE
IF condition THEN
instructions;
ELSE
instructions;
END IF;
3. IF-THEN-ELSIF
IF condition1 THEN
instructions;
ELSIF condition2
instructions;
ELSE
instructions;
END IF;
Exemple:
DECLARE
v_telephone CHAR(14) NOT NULL:=’06-76-85-14-89’;
BEGIN
IF SUBSTR(v_telephone,1,2)=’06’ THEN
DBMS_OUTPUT.PUT_LINE(‘ C ‘’ est un portable’) ;
ELSE
DBMS_OUTPUT.PUT_LINE(‘ C ‘’ est un fixe’) ;
END IF ;
END ;
Structure CASE : comme l’instruction IF, la structure CASE permet d’exécuter une
séquence d’instructions en fonction de différentes conditions.la structure CASE est
utile lorsqu’il faut évaluer une même expression et proposer plusieurs traitements pour
diverses conditions.
En fonction de la nature de l’expression et des conditions, une des deux écritures
suivantes peut être utilisée :
63
1)
CASE variable
WHEN expr1 THEN instructions1;
WHEN expr2 THEN instructions2;
…
WHEN exprN THEN instructionsN;
[ELSE instructionsN+1];
END CASE;
2)
CASE
WHEN condition1 THEN instructions1;
WHEN condition 2 THEN instructions2;
…
WHEN condition N THEN instructionsN;
[ELSE instructionsN+1];
END CASE;
Exemple 1:
DECLARE
grade CHAR(1) ;
appraisal VARCHAR2(20) ;
BIGIN
Appraisal :=
CASE grade
WHEN ’A’ THEN ‘Excellent’;
WHEN ‘B’ THEN ‘Very good’;
WHEN ‘C’ THEN ‘Good’;
ELSE ‘No such grade’
END CASE ;
DBMS_OUTPUT.PUT_LINE(‘grade:’ || grade || ‘appraisal:’ ||
appraisal);
END;
Exemple 2:
DECLARE
64
grade CHAR(1) ;
appraisal VARCHAR2(20) ;
BIGIN
Appraisal :=
CASE
WHEN grade=’A’ THEN ‘Excellent’;
WHEN grade IN (‘B’,’C’) THEN ‘Good’;
ELSE ‘No such grade’
END case;
DBMS_OUTPUT.PUT_LINE(‘grade:’ || grade || ‘appraisal:’ ||
appraisal);
END;
Exemple :
DECLARE
v_somme NUMBER(4) :=0 ;
v_entier NUMBER(3) :=1 ;
BEGIN
WHILE (v_entier <=100) LOOP
v_somme := v_somme + v_entier ;
65
v_entier := v_entier +1 ;
END LOOP ;
DBMS_OUTPUT.PUT_LINE(‘somme=’ || v_somme);
END;
Exemple :
DECLARE
v_somme NUMBER(4) :=0 ;
v_entier NUMBER(3) :=1 ;
BEGIN
LOOP
v_somme := v_somme + v_entier ;
v_entier := v_entier +1 ;
EXIT WHEN v_entier >100;
END LOOP ;
DBMS_OUTPUT.PUT_LINE(‘somme=’ || v_somme);
END;
Cette structure doit être utilisée quand il n’est pas nécessaire de tester la condition avec les
données initiales avant d’exécuter les instructions contenues dans la boucle.
66
Structure pour : célèbre pour les parcours de vecteurs, tableaux et matrices en tout
genre, la structure pour se caractérise par la connaissance a priori du nombre
d’itérations que le programmeur souhaite faire effectuer pour son algorithme. La
syntaxe est la suivante :
FOR compteur IN [REVERSE] valeurInf..valeurSup LOOP
Instructions ;
END LOOP ;
A la première itération le compteur reçoit automatiquement la valeur initiale (valeurInf).
Après chaque itération le compteur est incrémenté (ou décrémenté si l’option REVERSE a
été choisie). La sortie de la boucle est automatique après l’itération correspondant à la valeur
finale du compteur (valeurSup). La déclaration de la variable compteur n’est pas obligatoire.
Exemple :
DECLARE
v_somme NUMBER(4) :=0 ;
BEGIN
FOR v_entier IN 1..100 LOOP
v_somme := v_somme + v_entier ;
END LOOP ;
DBMS_OUTPUT.PUT_LINE(‘somme=’ || v_somme);
END;
V Exceptions
Afin d’éviter qu’un programme s’arrête à la première erreur (requête ne retournant aucune
ligne, valeur incorrecte à écrire dans la base, conflit de clé primaire, division par zéro…), il
est indispensable de prévoir tous les cas potentiels d’erreurs et d’associer à chacun de ces cas
la programmation d’une exception PL/SQL. Cette exception correspond à une valeur d’erreur
et est associée à un identificateur. Une fois levée, l’exception termine le corps principal des
instructions et renvoie au bloc EXCEPTION du programme en question. Deux mécanismes
peuvent déclencher une exception :
Une erreur Oracle se produit, l’exception associée est déclenchée automatiquement
(exemple du SELECT ne ramenant aucune ligne, ce qui déclenche l’exception ORA-
01403 d’identificateur NO_DATA_FOUND).
67
Le programmeur désir dérouter volontairement (par l’intermédiaire de l’instruction
RAISE) son programme dans le bloc des exceptions sous certaines conditions.
L’exception est ici manuellement déclenchée et peut appartenir à l’utilisateur (dans
l’exemple suivant PILOTE_TROP_JEUNE) ou être prédéfinie au niveau d’ORACLE
(division par zéro d’identificateur ZERO_DIVIDE qui sera automatiquement déclenchée.
BEGIN
…..
IF (…) THEN RAISE PILOTE TROP JEUNE;
SELECT … INTO ….FROM…;
…
EXCEPTION
WHEN NO DATA FOUND THEN
Instructions –A
WHEN ZERO DIVIDE THEN
Instructions –B
WHEN PILOTE TROP JEUNE THEN
Instructions –C
WHEN OTHERS THEN
Instructions –D
END;
Si aucune erreur ne se produit, le bloc est ignoré et le traitement se termine.
La syntaxe générale d’un bloc d’exception est la suivante. Il est possible de grouper plusieurs
exceptions pour programmer le même traitement. La dernière entrée (OTHERS) doit être
toujours placée en fin du bloc d’erreurs.
EXCEPTION
WHEN exception1 [OR exception2 …] THEN
Instructions;
WHEN exception3 [OR exception4 …] THEN
Instructions;
WHEN OTHERS [OR exception2 …] THEN
Instructions;
Si une anomalie se produit, le bloc EXCEPTION s’exécute.
Si le programme prend en compte l’erreur dans une entrée WHEN…, les instructions
de cette entrée sont exécutées et le programme se termine.
68
Si l’exception n’est pas prise en compte dans le bloc EXCEPTION, il existe une
section OTHERS où des instructions s’exécutent.
CURSOR_ALREADY_OPEN ORA-06511
DUP_VAL_ON_INDEX ORA-00001
INVALID_CURSOR ORA-01001
INVALID_NUMBER ORA-01722
LOGIN_DENIED ORA-01017
NO_DATA_FOUND ORA-01403
NOT_LOGGED_ON ORA-01012
PROGRAM_ERROR ORA-06501
STORAGE_ERROR ORA-06500
TIMEOUT_ON_RESOURCE ORA-00051
TOO_MANY_ROWS ORA-01422
TRANSACTION_BACKED_OUT ORA-00061
VALUE_ERROR ORA-06502
ZERO_DIVIDE ORA-01476
Déclenchement
Une exception utilisateur ne sera pas levée de la même manière qu’une exception interne. Le
programme doit explicitement dérouter le traitement vers le bloc des exceptions par la
69
directive RAISE. L’instruction RAISE permet également de déclencher des exceptions
prédéfinies. Dans l’exemple suivant, programmons deux exceptions :
70