4. Chapitre 1
Notes de cours
1.1 Introduction au PL/SQL
1.1.1 PL/SQL
Le PL de PL/SQL signifie Procedural Language. Il s’agit d’une extension proc´durale du SQL permettant d’effectuer
e
des traitements complexes sur une base de donn´es. Les possibilit´s offertes sont les mˆmes qu’avec des langages
e e e
imp´ratifs (instructions en s´quence) classiques.
e e
Ecrivez-le dans un ´diteur dont vous copierez le contenu dans SQL+. Un script ´crit en PL/SQL se termine obliga-
e e
toirement par un /, sinon SQL+ ne l’interpr`te pas. S’il contient des erreurs de compilation, il est possible d’afficher les
e
messages d’erreur avec la commande SQL+ : SHOW ERRORS.
1.1.2 Blocs
Tout code ´crit dans un langage proc´dural est form´ de blocs. Chaque bloc comprend une section de d´claration
e e e e
de variables, et un ensemble d’instructions dans lequel les variables d´clar´es sont visibles.
e e
La syntaxe est
DECLARE
/∗ d e c l a r a t i o n de v a r i a b l e s ∗/
BEGIN
/∗ i n s t r u c t i o n s a e x e c u t e r ∗/
END;
1.1.3 Affichage
Pour afficher le contenu d’une variable, les proc´dures DBMS OUTPUT.PUT() et DBMS OUTPUT.PUT LINE() prennent
e
en argument une valeur ` afficher ou une variable dont la valeur est ` afficher. Par d´faut, les fonctions d’affichage
a a e
sont desactiv´es. Il convient, ` moins que vous ne vouliez rien voir s’afficher, de les activer avec la commande SQL+
e a
SET SERVEROUTPUT ON.
1.1.4 Variables
Une variable se d´clare de la sorte :
e
nom type [ : = initialisation ] ;
L’initisation est optionnelle. Nous utiliserons les mˆmes types primitifs que dans les tables. Par exemple :
e
SET SERVEROUTPUT ON
DECLARE
c varchar2 ( 1 5 ) := ’ H e l l o World ! ’ ;
BEGIN
DBMS_OUTPUT . PUT_LINE ( c ) ;
END;
/
Les affectations se font avec la syntaxe variable := valeur ;
3
5. 1.1.5 Traitements conditionnels
Le IF et le CASE fonctionnent de la mˆme fa¸on que dans les autres langages imp´ratifs :
e c e
IF /∗ c o n d i t i o n 1 ∗/ THEN
/∗ i n s t r u c t i o n s 1 ∗/
ELSE
/∗ i n s t r u c t i o n s 2 ∗/
END IF ;
voire
IF /∗ c o n d i t i o n 1 ∗/ THEN
/∗ i n s t r u c t i o n s 1 ∗/
ELSIF /∗ c o n d i t i o n 2 ∗/
/∗ i n s t r u c t i o n s 2 ∗/
ELSE
/∗ i n s t r u c t i o n s 3 ∗/
END IF ;
Les conditions sont les mˆmes qu’en SQL. Le switch du langage C s’impl´mente en PL/SQL de la fa¸on suivante :
e e c
CASE /∗ v a r i a b l e ∗/
W E /∗ v a l e u r 1 ∗/ THEN
HN
/∗ i n s t r u c t i o n s 1 ∗/
W E /∗ v a l e u r 2 ∗/ THEN
HN
/∗ i n s t r u c t i o n s 2 ∗/
...
W E /∗ v a l e u r n ∗/ THEN
HN
/∗ i n s t r u c t i o n s n ∗/
ELSE
/∗ i n s t r u c t i o n s par d ´ f a u t ∗/
e
END CASE;
1.1.6 Traitements r´p´titifs
e e
LOOP ... END LOOP ; permet d’impl´menter les boucles
e
LOOP
/∗ i n s t r u c t i o n s ∗/
END LOOP ;
L’instruction EXIT WHEN permet de quitter une boucle.
LOOP
/∗ i n s t r u c t i o n s ∗/
EXIT W E /∗ c o n d i t i o n ∗/ ;
HN
END LOOP ;
La boucle FOR existe aussi en PL/SQL :
FOR /∗ v a r i a b l e ∗/ IN /∗ i n f ∗/ . . /∗ sup ∗/ LOOP
/∗ i n s t r u c t i o n s ∗/
END LOOP ;
Ainsi que la boucle WHILE :
WHILE /∗ c o n d i t i o n ∗/ LOOP
/∗ i n s t r u c t i o n s ∗/
END LOOP ;
Est-il possible, en bidouillant, d’impl´menter une boucle DO ... WHILE ?
e
4
6. 1.2 Tableaux et structures
1.2.1 Tableaux
Cr´ation d’un type tableau
e
Les types tableau doivent ˆtre d´finis explicitement par une d´claration de la forme
e e e
TYPE /∗ t y p e ∗/ IS VARRAY ( /∗ t a i l l e ∗/ ) OF /∗ t y p e E l e m e n t s ∗/ ;
– type est le nom du type tableau cr´e par cette instruction
e
– taille est le nombre maximal d’´l´ments qu’il est possible de placer dans le tableau.
ee
– typeElements est le type des ´l´ments qui vont ˆtre stock´s dans le tableau, il peut s’agir de n’importe quel
ee e e
type.
Par exemple, cr´ons un type tableau de nombres indic´ de 1 ` 10, que nous appelerons numberTab
e e a
TYPE numberTab IS VARRAY ( 1 0 ) OF N M E
U B R;
D´claration d’un tableau
e
Dor´navant, le type d’un tableau peut ˆtre utilis´ au mˆme titre que NUMBER ou VARCHAR2. Par exemple, d´clarons
e e e e e
un tableau appel´ t de type numberTab,
e
DECLARE
TYPE numberTab IS VARRAY ( 1 0 ) OF N M E
U B R;
t numberTab ;
BEGIN
/∗ i n s t r u c t i o n s ∗/
END;
/
Allocation d’un tableau
La cr´ation d’un type tableau met ` disposition un constructeur du mˆme nom que le type cr´´. Cette fonction
e a e ee
r´serve de l’espace m´moire pour ce tableau et retourne l’adresse m´moire de la zone r´serv´e, il s’agit d’une sorte de
e e e e e
malloc. Si, par exemple, un type tableau numtab a ´t´ cr´e, la fonction numtab() retourne une tableau vide.
ee e
DECLARE
TYPE numberTab IS VARRAY ( 1 0 ) OF N M E
U B R;
t numberTab ;
BEGIN
t := numberTab ( ) ;
/∗ u t i l i s a t i o n du t a b l e a u ∗/
END;
/
Une fois cette allocation faite, il devient presque possible d’utiliser le tableau...
Dimensionnement d’un tableau
Le tableau retourn´ par le constructeur est vide. Il convient ensuite de r´server de l’espace pour stocker les ´l´ments
e e ee
qu’il va contenir. On utilise pour cela la m´thode EXTEND(). EXTEND s’invoque en utilisant la notation point´e. Par
e e
exemple,
DECLARE
TYPE numberTab IS VARRAY ( 1 0 ) OF N M E
U B R;
t numberTab ;
BEGIN
t := numberTab ( ) ;
t . EXTEND ( 4 ) ;
/∗ u t i l i s a t i o n du t a b l e a u ∗/
END;
/
5
7. Dans cet exemple, t.EXTEND(4) ; permet par la suite d’utiliser les ´l´ments du tableau t(1), t(2), t(3) et t(4).
ee
Il n’est pas possible ”d’´tendre” un tableau ` une taille sup´rieure ` celle sp´cifi´e lors de la cr´ation du type tableau
e a e a e e e
associ´.
e
Utilisation d’un tableau
On acc`de, en lecture et en ´criture, au i-`me ´l´ment d’une variable tabulaire nomm´ T avec l’instruction T(i).
e e e ee e
Les ´l´ments sont indic´s ` partir de 1.
ee e a
Effectuons, par exemple, une permutation circulaire vers la droite des ´l´ments du tableau t.
ee
DECLARE
TYPE numberTab IS VARRAY ( 1 0 ) OF N M E
U B R;
t numberTab ;
i number ;
k number ;
BEGIN
t := numberTab ( ) ;
t . EXTEND ( 1 0 ) ;
FOR i IN 1 . . 1 0 LOOP
t ( i ) := i ;
END LOOP ;
k := t ( 1 0 ) ;
FOR i in REVERSE 2 . . 1 0 LOOP
t ( i ) := t ( i − 1 ) ;
END LOOP ;
t ( 1 ) := k ;
FOR i IN 1 . . 1 0 LOOP
DBMS_OUTPUT . PUT_LINE ( t ( i ) ) ;
END LOOP ;
END;
/
1.2.2 Structures
Un structure est un type regroupant plusieurs types. Une variable de type structur´ contient plusieurs variables,
e
ces variables s’appellent aussi des champs.
Cr´ation d’un type structur´
e e
On d´finit un type structur´ de la sorte :
e e
TYPE /∗ nomType ∗/ IS RECORD
(
/∗ l i s t e d e s champs ∗/
);
nomType est le nom du type structur´ construit avec la syntaxe pr´c´dente. La liste suit la mˆme syntaxe que la
e e e e
liste des colonnes d’une table dans un CREATE TABLE. Par exemple, construisons le type point (dans IR2 ),
TYPE point IS RECORD
(
abscisse N M E ,
U BR
ordonnee N M E
U BR
);
Notez bien que les types servant ` d´finir un type structur´ peuvent ˆtre quelconques : variables scalaires, tableaux,
a e e e
structures, etc.
D´claration d’une variable de type structur´
e e
point est maintenant un type, il devient donc possible de cr´er des variables de type point, la r`gle est toujours la
e e
mˆme pour d´clarer des variables en PL/SQL, par exemple
e e
6
8. p point ;
permet de d´clarer une variable p de type point.
e
Utilisation d’une variable de type structur´
e
Pour acc´der ` un champ d’une variable de type structur´, en lecture ou en ´criture, on utilise la notation point´e :
e a e e e
v.c est le champ appel´ c de la variable structur´ appel´e v. Par exemple,
e e e
DECLARE
TYPE point IS RECORD
(
abscisse N M E ,
U BR
ordonnee N M E
U BR
);
p point ;
BEGIN
p . abscisse := 1 ;
p . ordonnee := 3 ;
DBMS_OUTPUT . PUT_LINE ( ’ p . a b s c i s s e = ’ | | p . abscisse | |
’ and p . ordonnee = ’ | | p . ordonnee ) ;
END;
/
Le script ci-dessous cr´e le type point, puis cr´e une variable t de type point, et enfin affecte aux champs abscisse
e e
et ordonnee du point p les valeurs 1 et 3.
7
9. 1.3 Utilisation du PL/SQL
Ce cours est une introduction aux interactions possibles entre la base de donn´es et les scripts PL/SQL.
e
1.3.1 Affectation
On place dans une variable le r´sultat d’une requˆte en utilisant le mot-cl´ INTO. Les instructions
e e e
SELECT champ_1 , . . . , champ_n INTO v_1 , . . . , v_n
F O ...
R M
affecte aux variables v 1, ..., v n les valeurs retourn´es par la requˆte. Par exemple
e e
DECLARE
num N M E
U B R;
nom V R H R 3 0 ) := ’ Poup´ e Batman ’ ;
A C A 2( e
BEGIN
SELECT numprod INTO num
F O PRODUIT
R M
W E E nomprod = nom ;
HR
DBMS_OUTPUT . PUT_LINE ( ’L ’ ’ a r t i c l e ’ | |
nom | | ’ a pour num´ro ’ | | num ) ;
e
END;
/
Prˆtez attention au fait que la requˆte doit retourner une et une une seule ligne, sinon, une erreur se produit `
e e a
l’ex´cution.
e
1.3.2 Tables et structures
Si vous ne tenez pas ` vous prendre la tˆte pour choisir le type de chaque variable, demandez-vous ce que vous
a e
allez mettre dedans ! Si vous tenez ` y mettre une valeur qui se trouve dans une colonne d’une table, il est possible de
a
vous r´f´rer directement au type de cette colonne avec le type nomTable.nomColonne%type. Par exemple,
ee
DECLARE
num PRODUIT . numprod%type ;
nom PRODUIT . nomprod%type := ’ Poup´ e Batman ’ ;
e
BEGIN
SELECT numprod INTO num
F O PRODUIT
R M
W E E nomprod = nom ;
HR
DBMS_OUTPUT . PUT_LINE ( ’L ’ ’ a r t i c l e ’ | |
nom | | ’ a pour num´ro ’ | | num ) ;
e
END;
/
Pour aller plus loin, il est mˆme possible de d´clarer une structure pour repr´senter une ligne d’une table, le type
e e e
porte alors le nom suivant : nomTable%rowtype.
DECLARE
nom PRODUIT . nomprod%type := ’ Poup´ e Batman ’ ;
e
ligne PRODUIT%rowtype ;
BEGIN
SELECT ∗ INTO ligne
F O PRODUIT
R M
W E E nomprod = nom ;
HR
DBMS_OUTPUT . PUT_LINE ( ’L ’ ’ a r t i c l e ’ | |
ligne . nomprod | | ’ a pour num´ro ’ | | ligne . numprod ) ;
e
END;
/
8
10. 1.3.3 Transactions
Un des m´canismes les plus puissants des SGBD r´cents r´side dans le syst`me des transactions. Une transaction
e e e e
est un ensemble d’op´rations “atomiques”, c’est-`-dire indivisible. Nous consid´rerons qu’un ensemble d’op´rations est
e a e e
indivisible si une ex´cution partielle de ces instructions poserait des probl`mes d’int´grit´ dans la base de donn´es.
e e e e e
Par exemple, dans le cas d’une base de donn´es de gestion de comptes en banque, un virement d’un compte ` un autre
e a
se fait en deux temps : cr´diter un compte d’une somme s, et d´biter un autre de la mˆme somme s. Si une erreur
e e e
survient pendant la deuxi`me op´ration, et que la transaction est interrompue, le virement est incomplet et le patron
e e
va vous assassiner.
Il convient donc de disposer d’un m´canisme permettant de se prot´ger de ce genre de d´sagr´ment. Plutˆt que
e e e e o
se casser la tˆte ` tester les erreurs ` chaque ´tape et ` balancer des instructions permettant de “revenir en arri`re”,
e a a e a e
nous allons utiliser les instructions COMMIT et ROLLBACK.
Voici le squelette d’un exemple :
/∗ i n s t r u c t i o n s ∗/
IF /∗ e r r e u r ∗/ THEN
ROLLBACK;
ELSE
C M I
O MT;
END;
Le ROLLBACK annule toutes les modifications faites depuis le d´but de la transaction (donc depuis le pr´c´dent
e e e
COMMIT), COMMIT les enregistre d´finitivement dans la base de donn´es.
e e
La variable d’environnement AUTOCOMMIT, qui peut ˆtre positionn´e ` ON ou ` OFF permet d’activer la gestion des
e e a a
transactions. Si elle est positionn´e ` ON, chaque instruction a des r´percussions imm´diates dans la base, sinon, les
e a e e
modifications ne sont effectives qu’une fois qu’un COMMIT a ´t´ ex´cut´.
ee e e
9
11. 1.4 Exceptions
Le m´canisme des exceptions est impl´ment´ dans la plupart des langages r´cent, notament orient´s objet. Cette
e e e e e
fa¸on de programmer a quelques avantages imm´diats :
c e
– obliger les programmeurs ` traiter les erreurs : combien de fois votre prof de C a hurl´ en vous suppliant
a e
de v´rifier les valeurs retourn´es par un malloc, ou un fopen ? La plupart des compilateurs des langages `
e e a
exceptions (notamment java) ne compilent que si pour chaque erreur potentielle, vous avez pr´par´ un bloc de
e e
code (´ventuellement vide...) pour la traiter. Le but est de vous assurer que vous n’avez pas oubli´ d’erreur.
e e
– Rattraper les erreurs en cours d’ex´cution : Si vous programmez un syst`me de s´curit´ de centrale
e e e e
nucl´aire ou un pilote automatique pour l’aviation civile, une erreur de m´moire qui vous afficherait l’´cran
e e e
bleu de windows, ou le message “Envoyer le rapport d’erreur ?”, ou plus simplement le fameux “Segmentation
fault” produirait un effet des plus mauvais. Certaines erreurs d’´xecution sont rattrapables, autrement dit, il est
e
possible de r´soudre le probl`me sans interrompre le programme.
e e
– Ecrire le traitement des erreurs ` part : Pour des raisons fiabilit´, de lisibilit´, il a ´t´ consid´r´ que
a e e ee ee
m´langer le code “normal” et le traitement des erreurs ´tait un style de programmation perfectible... Dans les
e e
langages ` exception, les erreurs sont trait´es ` part.
a e a
1.4.1 Rattraper une exception
Je vous ai menti dans le premier cours, un bloc en PL/SQL a la forme suivante :
DECLARE
/∗ d e c l a r a t i o n s ∗/
BEGIN
/∗ i n s t r u c t i o n s ∗/
EXCEPTION
/∗ t r a i t e m e n t d e s e r r e u r s ∗/
END;
Une exception est une “erreur type”, elle porte un nom, au mˆme titre qu’une variable a une identificateur, par
e
exemple GLUBARF. Lorsque dans les instructions, l’erreur GLUBARF se produit, le code du BEGIN s’interrompt et le
code de la section EXCEPTION est lanc´. On dit aussi que quand une exception est lev´e (raised) (on dit aussi jet´e
e e e
(thrown)), on la rattrape (catch) dans le bloc EXCEPTION. La section EXCEPTION a la forme suivante :
EXCEPTION
W E E1 THEN
HN
/∗ t r a i t e m e n t ∗/
W E E2 THEN
HN
/∗ t r a i t e m e n t ∗/
W E E3 THEN
HN
/∗ t r a i t e m e n t ∗/
W E OTHERS THEN
HN
/∗ t r a i t e m e n t ∗/
END;
On ´num`re les erreurs les plus pertinentes en utilisant leur nom et en consacrant ` chacune d’elle un traitement
e e a
particulier pour rattraper (ou propager) l’erreur. Quand un bloc est trait´, les WHEN suivants ne sont pas ´valu´s.
e e e
OTHERS est l’exception par d´faut, OTHERS est toujours v´rifi´, sauf si un cas pr´c´dent a ´t´ v´rifi´. Dans l’exemple
e e e e e ee e e
suivant :
DECLARE
/∗ d e c l a r a t i o n s ∗/
BEGIN
/∗ i n s t r u c t i o n s ∗/
C M I
O MT;
EXCEPTION
W E GLUBARF THEN
HN
ROLLBACK;
DBMS_OUTPUT . PUT_LINE ( ’GLUBARF e x c e p t i o n r a i s e d ! ’ ) ;
W E OTHERS THEN
HN
DBMS_OUTPUT . PUT_LINE ( ’SQLCODE = ’ | | SQLCODE ) ;
DBMS_OUTPUT . PUT_LINE ( ’SQLERRM = ’ | | SQLERRM ) ;
10
12. END;
Les deux variables globales SQLCODE et SQLERRM contiennent respectivement le code d’erreur Oracle et un message
d’erreur correspondant ` la derni`re exception lev´e. Chaque exception a donc, en plus d’un nom, un code et un
a e e
message.
1.4.2 Exceptions pr´d´finies
e e
Bon nombre d’exceptions sont pr´d´finies par Oracle, par exemple
e e
– NO DATA FOUND est lev´e quand la requˆte d’une instruction de la forme SELECT ... INTO ... ne retourne
e e
aucune ligne
– TOO MANY ROWS est lev´e quand la requˆte d’une instruction de la forme SELECT ... INTO ... retourne plusieurs
e e
lignes
– DUP VAL ON INDEX est lev´e si une insertion (ou une modification) est refus´e ` cause d’une contrainte d’unicit´.
e e a e
On peut enrichir notre exemple de la sorte :
DECLARE
num N M E
U B R;
nom V R H R 3 0 ) := ’ Poup´ e Batman ’ ;
A C A 2( e
BEGIN
SELECT numprod INTO num
F O PRODUIT
R M
W E E nomprod = nom ;
HR
DBMS_OUTPUT . PUT_LINE ( ’L ’ ’ a r t i c l e ’ | |
nom | | ’ a pour num´ro ’ | | num ) ;
e
EXCEPTION
W E NO_DATA_FOUND THEN
HN
DBMS_OUTPUT . PUT_LINE ( ’ Aucun a r t i c l e ne p o r t e l e nom ’
| | nom ) ;
W E TOO_MANY_ROWS THEN
HN
DBMS_OUTPUT . PUT_LINE ( ’ P l u s i e u r s a r t i c l e s p o r t e n t l e nom ’
| | nom ) ;
W E OTHERS THEN
HN
DBMS_OUTPUT . PUT_LINE ( ’ I l y a un g r o s p r o b l` m e . . . ’ ) ;
e
END;
/
SELECT numprod INTO num... l`ve une exception si la requˆte renvoie un nombre de lignes diff´rent de 1.
e e e
1.4.3 Codes d’erreur
Je vous encore menti, certaines exceptions n’ont pas de nom. Elle ont seulement un code d’erreur, il est conseill´
e
de se reporter ` la documentation pour les obtenir. On les traite de la fa¸on suivante
a c
EXCEPTION
W E OTHERS THEN
HN
IF SQLCODE = CODE1 THEN
/∗ t r a i t e m e n t ∗/
ELSIF SQLCODE = CODE2 THEN
/∗ t r a i t e m e n t ∗/
ELSE
DBMS_OUTPUT . PUT_LINE ( ’ J ’ ’ v o i s pas c ’ ’ que ca
peut et re . . . ’ ) ;
END;
C’est souvent le cas lors de violation de contraintes.
1.4.4 D´clarer et lancer ses propres exceptions
e
Exception est un type, on d´clare donc les exceptions dans une section DECLARE. Une exception se lance avec
e
l’instruction RAISE. Par exemple,
11
13. DECLARE
GLUBARF EXCEPTION;
BEGIN
RAISE GLUBARF ;
EXCEPTION
W E GLUBARF THEN
HN
DBMS_OUTPUT . PUT_LINE ( ’ g l u b a r f r a i s e d . ’ ) ;
END;
/
12
14. 1.5 Sous-programmes
1.5.1 Proc´dures
e
Syntaxe
On d´finit une proc´dure de la sorte
e e
CREATE OR REPLACE PROCEDURE /∗ nom ∗/ ( /∗ p a r a m e t r e s ∗/ ) IS
/∗ d e c l a r a t i o n d e s v a r i a b l e s l o c a l e s ∗/
BEGIN
/∗ i n s t r u c t i o n s ∗/
END;
les param`tres sont une simple liste de couples nom type. Par exemple, la procedure suivante affiche un compte `
e a
rebours.
CREATE OR REPLACE PROCEDURE compteARebours ( n N M E
U B R) IS
BEGIN
IF n >= 0 THEN
DBMS_OUTPUT . PUT_LINE ( n ) ;
compteARebours ( n − 1 ) ;
END IF ;
END;
Invocation
En PL/SQL, une proc´dure s’invoque tout simplement avec son nom. Mais sous SQL+, on doit utiliser le mot-cl´
e e
CALL. Par exemple, on invoque le compte ` rebours sous SQL+ avec la commande CALL compteARebours(20).
a
Passage de param`tres
e
Oracle permet le passage de param`tres par r´f´rence. Il existe trois types de passage de param`tres :
e ee e
– IN : passage par valeur
– OUT : aucune valeur pass´e, sert de valeur de retour
e
– IN OUT : passage de param`tre par r´f´rence
e ee
Par d´faut, le passage de param`tre se fait de type IN.
e e
CREATE OR REPLACE PROCEDURE incr ( val IN OUT N M E
U B R) IS
BEGIN
val := val + 1 ;
END;
1.5.2 Fonctions
Syntaxe
On cr´e une nouvelle fonction de la fa¸on suivante :
e c
CREATE OR REPLACE FUNCTION /∗ nom ∗/ ( /∗ p a r a m e t r e s ∗/ ) RETURN /∗ t y p e
∗/ IS
/∗ d e c l a r a t i o n d e s v a r i a b l e s l o c a l e s ∗/
BEGIN
/∗ i n s t r u c t i o n s ∗/
END;
L’instruction RETURN sert ` retourner une valeur. Par exemple,
a
CREATE OR REPLACE FUNCTION module ( a N M E , b N M E
U BR U B R) RETURN N M E IS
U BR
BEGIN
IF a < b THEN
RETURN a ;
ELSE
13
15. RETURN module ( a − b , b ) ;
END IF ;
END;
Invocation
Tout comme les proc´dures, l’invocation des fonctions ne pose aucun probl`me en PL/SQL, par contre, sous SQL+,
e e
c’est quelque peu particulier. On passe par une pseudo-table nomm´e DUAL de la fa¸on suivante :
e c
SELECT module ( 2 1 , 1 2 ) F O DUAL ;
R M
Passage de param`tres
e
Les param`tres sont toujours pass´s avec le type IN.
e e
14
16. 1.6 Curseurs
1.6.1 Introduction
Les instructions de type SELECT ... INTO ... manquent de souplesse, elles ne fontionnent que sur des requˆtes e
retourant une et une seule valeur. Ne serait-il pas int´ressant de pouvoir placer dans des variables le r´sultat d’une
e e
requˆte retournant plusieurs lignes ? A m´diter...
e e
1.6.2 Les curseurs
Un curseur est un objet contenant le r´sultat d’une requˆte (0, 1 ou plusieurs lignes).
e e
d´claration
e
Un curseur se d´clare dans une section DECLARE :
e
CURSOR /∗ nomcurseur ∗/ IS /∗ r e q u ˆ t e ∗/ ;
e
Par exemple, si on tient ` r´cup´rer tous les employ´s de la table EMP, on d´clare le curseur suivant.
a e e e e
CURSOR emp_cur IS
SELECT ∗ F O EMP ;
R M
Ouverture
Lors de l’ouverture d’un curseur, la requˆte du curseur est ´valu´e, et le curseur contient toutes les donn´es
e e e e
retourn´es par la requˆte. On ouvre un curseur dans une section BEGIN :
e e
OPEN /∗ nomcurseur ∗/ ;
Par exemmple,
DECLARE
CURSOR emp_cur IS
SELECT ∗ F O EMP ;
R M
BEGIN
OPEN emp_cur ;
/∗ U t i l i s a t i o n du c u r s e u r ∗/
END;
Lecture d’une ligne
Une fois ouvert, le curseur contient toutes les lignes du r´sultat de la requˆte On les r´cup`re une par une en
e e e e
utilisant le mot-cl´ FETCH :
e
FETCH /∗ nom curseur ∗/ INTO /∗ l i s t e v a r i a b l e s ∗/ ;
La liste de variables peut ˆtre remplac´e par une structure de type nom curseur%ROWTYPE. Si la lecture de la ligne
e e
´choue, parce qu’il n’y a plus de ligne ` lire, l’attribut %NOTFOUND prend la valeur vrai.
e a
DECLARE
CURSOR emp_cur IS
SELECT ∗ F O EMP ;
R M
ligne emp_cur%rowtype
BEGIN
OPEN emp_cur ;
LOOP
FETCH emp_cur INTO ligne ;
EXIT W E emp_cur%NOTFOUND ;
HN
DBMS_OUTPUT . PUT_LINE ( ligne . ename ) ;
END LOOP ;
/∗ . . . ∗/
END;
15
17. Fermeture
Apr`s utilisation, il convient de fermer le curseur.
e
CLOSE /∗ nomcurseur ∗/ ;
Compl´tons notre exemple,
e
DECLARE
CURSOR emp_cur IS
SELECT ∗ F O EMP ;
R M
ligne emp_cur%rowtype ;
BEGIN
OPEN emp_cur ;
LOOP
FETCH emp_cur INTO ligne ;
EXIT W E emp_cur%NOTFOUND ;
HN
DBMS_OUTPUT . PUT_LINE ( ligne . ename ) ;
END LOOP ;
CLOSE emp_cur ;
END;
/
Le programme ci-dessus peut aussi s’´crire
e
DECLARE
CURSOR emp_cur IS
SELECT ∗ F O EMP ;
R M
ligne emp_cur%rowtype ;
BEGIN
OPEN emp_cur ;
FETCH emp_cur INTO ligne ;
WHILE emp_cur%FOUND LOOP
DBMS_OUTPUT . PUT_LINE ( ligne . ename ) ;
FETCH emp_cur INTO ligne ;
END LOOP ;
CLOSE emp_cur ;
END;
Boucle FOR
Il existe une boucle FOR se chargeant de l’ouverture, de la lecture des lignes du curseur et de sa fermeture,
FOR ligne IN emp_cur LOOP
/∗ Traitement ∗/
END LOOP ;
Par exemple,
DECLARE
CURSOR emp_cur IS
SELECT ∗ F O EMP ;
R M
ligne emp_cur%rowtype ;
BEGIN
FOR ligne IN emp_cur LOOP
DBMS_OUTPUT . PUT_LINE ( ligne . ename ) ;
END LOOP ;
END;
/
16
18. 1.7 Curseurs parametr´s
e
1.7.1 Introduction
A votre avis, le code suivant est-il valide ?
DECLARE
N M E n := 1 4 ;
U BR
BEGIN
DECLARE
CURSOR C IS
SELECT ∗
F O PERSONNE
R M
W E E numpers >= n ;
HR
ROW C%rowType ;
BEGIN
FOR ROW IN C LOOP
DBMS_OUTPUT . PUT_LINE ( ROW . numpers ) ;
END LOOP ;
END;
END;
/
R´ponse : non. La requˆte d’un curseur ne peut pas contenir de variables dont les valeurs ne sont pas fix´es.
e e e
Pourquoi ? Parce que les valeurs des ces sont susceptibles de changer entre la d´claration du curseur et son ouverture.
e
Le rem`de est un curseur param´tr´.
e e e
1.7.2 D´finition
e
Un curseur param´tr´ est un curseur dont la requˆte contient des variables dont les valeurs ne seront fix´es qu’`
e e e e a
l’ouverture.
1.7.3 D´claration
e
On pr´cise la liste des noms et des type des param`tres entre parenth`ses apr`s le nom du curseur :
e e e e
CURSOR /∗ nom ∗/ ( /∗ l i s t e d e s p a r a m` t r e s ∗/ ) IS
e
/∗ r e q u ˆ t e ∗/
e
Par exemple, cr´eons une requˆte qui, pour une personne donn´e, nous donne la liste des noms et pr´noms de ses
e e e e
enfants :
CURSOR enfants ( numparent N M E
U B R) IS
SELECT ∗
F O PERSONNE
R M
W E E pere = numparent
HR
OR mere = numparent ;
1.7.4 Ouverture
On ouvre un curseur param´tr´ en passant en param`tre les valeurs des variables :
e e e
OPEN /∗ nom ∗/ ( /∗ l i s t e d e s p a r a m` t r e s ∗/ )
e
Par exemple,
OPEN enfants ( 1 ) ;
1.7.5 Lecture d’une ligne, fermeture
la lecture d’une ligne suit les mˆmes r`gles qu’avec un curseur non param´tr´.
e e e e
17
19. 1.7.6 Boucle pour
La boucle pour se charge de l’ouverture, il convient donc de placer les param`tre dans l’entˆte de la boucle,
e e
FOR /∗ v a r i a b l e ∗/ IN /∗ nom ∗/ ( /∗ l i s t e p a r a m` t r e s ∗/ ) LOOP
e
/∗ i n s t r u c t i o n s ∗/
END LOOP ;
Par exemple,
FOR e IN enfants ( 1 ) LOOP
DBMS_OUTPUT . PUT_LINE ( e . nompers | | ’ ’ | | e . prenompers ) ;
END LOOP ;
1.7.7 Exemple r´capitulatif
e
DECLARE
CURSOR parent IS
SELECT ∗
F O PERSONNE ;
R M
p parent%rowtype ;
CURSOR enfants ( numparent N M E
U B R) IS
SELECT ∗
F O PERSONNE
R M
W E E pere = numparent
HR
OR mere = numparent ;
e enfants%rowtype ;
BEGIN
FOR p IN parent LOOP
DBMS_OUTPUT . PUT_LINE ( ’ Les e n f a n t s de ’ | | p . prenom | |
’ ’ | | p . nom | | ’ s o n t : ’ ) ;
FOR e IN enfants ( p . numpers ) LOOP
DBMS_OUTPUT . PUT_LINE ( ’ ∗ ’ | | e . prenom
| | ’ ’ | | e . nom );
END LOOP ;
END LOOP ;
END;
/
18
20. 1.8 Triggers
1.8.1 Principe
Un trigger est une proc´dure stock´e qui se lance automatiquement lorsqu’un ´v´nement se produit. Par ´v´nement,
e e e e e e
on entend dans ce cours toute modification des donn´es se trouvant dans les tables. On s’en sert pour contrˆler ou
e o
appliquer des contraintes qu’il est impossible de formuler de fa¸on d´clarative.
c e
1.8.2 Classification
Type d’´v´nement
e e
Lors de la cr´ation d’un trigger, il convient de pr´ciser quel est le type d’´v´nement qui le d´clenche. Nous r´aliserons
e e e e e e
dans ce cours des triggers pour les ´v´nements suivants :
e e
– INSERT
– DELETE
– UPDATE
Moment de l’´xecution
e
On pr´cise aussi si le trigger doit ˆtre ´xecut´ avant (BEFORE) ou apr`s (AFTER) l’´v´nement.
e e e e e e e
Ev´nements non atomiques
e
Lors que l’on fait un DELETE ..., il y a une seule instruction, mais plusieurs lignes sont affect´es. Le trigger doit-il
e
ˆtre ex´cut´ pour chaque ligne affect´e (FOR EACH ROW), ou seulement une fois pour toute l’instruction (STATEMENT) ?
e e e e
– un FOR EACH ROW TRIGGER est ex´cut´ ` chaque fois qu’une ligne est affect´e.
e ea e
– un STATEMENT TRIGGER est ´xecut´e ` chaque fois qu’une instruction est lanc´e.
e e a e
1.8.3 Cr´ation
e
Syntaxe
On d´clare un trigger avec l’instruction suivante :
e
CREATE OR REPLACE TRIGGER nomtrigger
[ BEFORE | AFTER ] [INSERT | DELETE | UPDATE] ON nomtable
[ FOR EACH ROW | ]
DECLARE
/∗ d e c l a r a t i o n s ∗/
BEGIN
/∗ i n s t r u c t i o n s ∗/
END;
Par exemple,
SQL> CREATE OR REPLACE TRIGGER pasDeDeleteDansClient
2 BEFORE DELETE ON CLIENT
3 BEGIN
4 RAISE_APPLICATION_ERROR ( −20555 , ’Va t e f a i r e . . . ’ ) ;
5 END;
6 /
D´ clencheur cr´ ´ .
e ee
SQL> SELECT C U T( ∗ )
O N
2 F O CLIENT ;
R M
C U T( ∗ )
O N
−−−−−−−−−−
21
SQL> DELETE F O CLIENT ;
R M
19
21. DELETE F O CLIENT
R M
∗
ERREUR ` la ligne 1 :
a
ORA −20555: Va te faire . . .
ORA −06512: ` ”SCOTT.PASDEDELETEDANSCLIENT” , ligne 2
a
ORA −04088: erreur lors d ex´ cution du d´ clencheur ’SCOTT.PASDEDELETEDANSCLIENT ’
e e
SQL> SELECT C U T( ∗ )
O N
2 F O CLIENT ;
R M
C U T( ∗ )
O N
−−−−−−−−−−
21
L’instruction RAISE APPLICATION ERROR(code, message) l`ve une exception sans nom portant un code code et
e
un message d’erreur message. Vous remarquez que comme l’erreur a ´t´ lev´e avant la suppression, les donn´es sont
ee e e
toujours pr´sentes dans la table CLIENT. Le trigger a contrˆl´ une r`gle, et comme elle n’´tait pas respect´e, il a lanc´
e oe e e e e
une erreur.
Combinaisons d’´v´nements
e e
Il est possible, en s´parant les types d’´v´nement par le mot-cl´ OR, de d´finir un trigger d´clench´ par plusieurs
e e e e e e e
´v´nements. Les variables bool´ennes INSERTING, UPDATING et DELETING permettent d’identifier l’´v´nement qui a
e e e e e
d´clench´ le trigger.
e e
CREATE OR REPLACE TRIGGER afficheEvenement
BEFORE INSERT OR UPDATE OR DELETE ON CLIENT
FOR EACH ROW
BEGIN
IF INSERTING THEN
DBMS_OUTPUT . PUT_LINE ( ’ I n s e r t i o n dans CLIENT ’ ) ;
ELSIF UPDATING THEN
DBMS_OUTPUT . PUT_LINE ( ’ Mise a j o u r dans CLIENT ’ ) ;
ELSE
DBMS_OUTPUT . PUT_LINE ( ’ S u p p r e s s i o n dans CLIENT ’ ) ;
END IF ;
END;
1.8.4 Acc`s aux lignes en cours de modification
e
Dans les FOR EACH ROW triggers, il est possible avant la modification de chaque ligne, de lire l’ancienne ligne et la
nouvelle ligne par l’interm´diaire des deux variables structur´es :old et :new. Par exemple le trigger suivant empˆche
e e e
de diminuer un salaire :
CREATE OR REPLACE TRIGGER pasDeBaisseDeSalaire
BEFORE UPDATE ON EMP
FOR EACH ROW
BEGIN
IF ( : old . sal > : new . sal ) THEN
RAISE_APPLICATION_ERROR ( −20567 ,
’ Pas de b a i s s e de s a l a i r e ! ’ ) ;
END IF ;
END;
Tables en mutation
Il est impossible, dans un trigger de type FOR EACH ROW de faire un SELECT sur la table en cours de modification.
20
22. SQL> CREATE OR REPLACE TRIGGER beforeStatement
2 BEFORE UPDATE ON CLIENT
3 DECLARE
4 NB N M E
U B R;
5 BEGIN
6 SELECT C U T( ∗ ) INTO NB
O N
7 F O CLIENT ;
R M
8 END;
9 /
D´ clencheur cr´ ´ .
e ee
SQL>
SQL> CREATE OR REPLACE TRIGGER afterStatement
2 AFTER UPDATE ON CLIENT
3 DECLARE
4 NB N M E
U B R;
5 BEGIN
6 SELECT C U T( ∗ ) INTO NB
O N
7 F O CLIENT ;
R M
8 END;
9 /
D´ clencheur cr´ ´ .
e ee
SQL>
SQL> UPDATE CLIENT SET nomcli = nomcli ;
21 ligne ( s ) mise ( s ) ` jour .
a
SQL>
SQL> CREATE OR REPLACE TRIGGER beforeForEachRow
2 BEFORE UPDATE ON CLIENT
3 FOR EACH ROW
4 DECLARE
5 NB N M E
U B R;
6 BEGIN
7 SELECT C U T( ∗ ) INTO NB
O N
8 F O CLIENT ;
R M
9 END;
10 /
D´ clencheur cr´ ´ .
e ee
SQL>
SQL> UPDATE CLIENT SET nomcli = nomcli ;
UPDATE CLIENT SET nomcli = nomcli
∗
ERREUR ` la ligne 1 :
a
ORA −04091: la table SCOTT . CLIENT est en mutation ; le d´ clencheur ou la
e
fonction ne peut la voir
ORA −06512: ` ”SCOTT.BEFOREFOREACHROW” , ligne 4
a
ORA −04088: erreur lors d ex´ cution du d´ clencheur ’SCOTT.BEFOREFOREACHROW’
e e
SQL> DROP TRIGGER beforeForEachRow ;
D´ clencheur supprim´ .
e e
21
23. SQL>
SQL>
SQL> CREATE OR REPLACE TRIGGER afterForEachRow
2 AFTER UPDATE ON CLIENT
3 FOR EACH ROW
4 DECLARE
5 NB N M E
U B R;
6 BEGIN
7 SELECT C U T( ∗ ) INTO NB
O N
8 F O CLIENT ;
R M
9 END;
10 /
D´ clencheur cr´ ´ .
e ee
SQL>
SQL> UPDATE CLIENT SET nomcli = nomcli ;
UPDATE CLIENT SET nomcli = nomcli
∗
ERREUR ` la ligne 1 :
a
ORA −04091: la table SCOTT . CLIENT est en mutation ; le d´ clencheur ou la
e
fonction ne peut la voir
ORA −06512: ` ”SCOTT.AFTERFOREACHROW” , ligne 4
a
ORA −04088: erreur lors d ex´ cution du d´ clencheur ’SCOTT.AFTERFOREACHROW’
e e
1.8.5 Contourner le probl`me des tables en mutation
e
Il existe plusieurs fa¸ons de contourner ce probl`me :
c e
– Utiliser un STATEMENT trigger. Comme on ne sait pas quelles lignes ont ´t´ modifi´es, on est oblig´ de toutes
ee e e
les traiter. Cette approche pr´sente donc un inconv´nient majeur : elle nous am`ne ` effectuer de nombreux
e e e a
traitements inutiles.
– En ayant des donn´es redondantes. Il suffit que les donn´es servant ` la v´rification se trouvent dans une autre
e e a e
table que celle en mutation. Cette m´thode a pour inconv´nient la m´moire occup´e et la quantit´ de code ` ´crire
e e e e e ae
pour maintenir la coh´rence des donn´es. Dans la plupart des cas, cette solution est malgr´ tout la meilleure.
e e e
Colonnes suppl´mentaires
e
Par exemple, si l’on souhaite empˆcher un client d’avoir plus de 10 comptes en banque, une solution est de placer
e
dans la table client une colonne contenant le nombre de comptes.
ALTER TABLE CLIENT A D nbComptes number ;
D
UPDATE CLIENT SET nbComptes = 0 ;
Une fois cette table cr´e, il convient de s’assurer que les donn´es de la colonne nbComptes contient toujours les
e e
bonnes valeurs. On le fait avec plusieurs sous-programmes :
CREATE OR REPLACE TRIGGER metAJourNbComptes
AFTER INSERT OR UPDATE OR DELETE ON COMPTECLIENT
BEGIN
UPDATE CLIENT SET nbComptes =
(
SELECT C U T( ∗ )
O N
F O COMPTECLIENT CC
R M
W E E CC . numCli = numCli
HR
);
END;
/
CREATE OR REPLACE TRIGGER verifieNbComptes
22
24. BEFORE INSERT ON COMPTECLIENT
FOR EACH ROW
DECLARE
nbComptes N M E
U B R;
BEGIN
SELECT nbComptes INTO nbComptes
F O CLIENT
R M
W E E numCli = : new . numcli ;
HR
IF ( nbComptes >= 1 0 ) THEN
RAISE_APPLICATION_ERROR ( −20556 ,
’ Ce c l i e n t a d e j a t r o p de comptes ’ ) ;
END IF ;
END;
/
On peut affiner en rempla¸ant metAJourNbComptes par plusieurs sous-programmes :
c
CREATE OR REPLACE TRIGGER initialiseNbComptes
BEFORE INSERT ON CLIENT
FOR EACH ROW
BEGIN
: new . nbComptes := 0 ;
END;
/
CREATE OR REPLACE TRIGGER metAJourNbComptes
AFTER INSERT OR UPDATE OR DELETE ON COMPTECLIENT
FOR EACH ROW
BEGIN
IF DELETING OR UPDATING THEN
UPDATE CLIENT SET nbComptes = nbComptes − 1
W E E numcli = : old . numcli ;
HR
END IF ;
IF INSERTING OR UPDATING THEN
UPDATE CLIENT SET nbComptes = nbComptes + 1
W E E numcli = : new . numcli ;
HR
END IF ;
END;
/
Tables suppl´mentaires
e
Si l’on souhaite par exemple empˆcher les circuits dans la table PERSONNE, il est n´cessaire de faire un parcours
e e
de graphe. Ce qui n´cessite des SELECT dans la table en cours de mutation. La seule solution est dans ce cas d’avoir
e
une table miroir qui contient les colonnes cl´s primaire et ´trang`res de cette table, et de s’en servir pour d´tecter les
e e e e
circuits.
CREATE TABLE MIRRORPERSONNE
(
numpers N M E PRIMARY KEY,
U BR
pere N M E ,
U BR
mere N M E
U BR
);
Nous allons ensuite proc´der de mˆme, en r´percutant chaque op´ration de PERSONNE sur MIRRORPERSONNE.
e e e e
CREATE OR REPLACE TRIGGER miseAJourMirrorPersonne
BEFORE UPDATE OR INSERT OR DELETE ON PERSONNE
FOR EACH ROW
BEGIN
IF DELETING OR UPDATING THEN
23
25. DELETE F O MIRRORPERSONNE
R M
W E E numpers = : old . numpers ;
HR
END IF ;
IF INSERTING OR UPDATING THEN
INSERT INTO MIRRORPERSONNE VALUES
( : new . numpers , : new . pere , : new . mere ) ;
END IF ;
END;
/
Une fois cela fait, il suffit de rechercher si une personne ins´r´e est une descendante d’elle mˆme dans MIRRORPERSONNE.
ee e
CREATE OR REPLACE FUNCTION trouveCircuit ( current N M E , toFind N M E
U BR U B R)
RETURN BOOLEAN IS
numPere N M E
U B R;
numMere N M E
U B R;
BEGIN
IF ( current IS NULL) THEN
RETURN FALSE;
END IF ;
SELECT pere , mere INTO numPere , numMere
F O MIRRORPERSONNE
R M
W E E numPers = current ;
HR
RETURN ( numPere = toFind OR numMere = toFind OR
trouveCircuit ( numPere , toFind ) OR
trouveCircuit ( numMere , toFind ) ) ;
END;
/
CREATE OR REPLACE TRIGGER verifieCircuit
AFTER UPDATE OR INSERT ON PERSONNE
FOR EACH ROW
BEGIN
IF ( trouveCircuit ( : new . numPers , : new . numPers ) ) THEN
RAISE_APPLICATION_ERROR ( −20557 ,
’ C i r c u i t dans l ’ ’ a r b r e g ´ n ´ a l o g i q u e . ’ ) ;
e e
END IF ;
END;
/
24
26. 1.9 Packages
1.9.1 Principe
Un package est un ensemble de sous-programmes et de variables form´ par e
– Une sp´cification : d´claration de variables et de sous-programmes
e e
– Un corps : impl´mentation des sous-programmes
e
Tout ce qui se trouve dans la sp´cification doit se trouver dans le corps, mais la r´ciproque est fausse. Un package
e e
satisfait les points suivants :
– encapsulation : certains traitements sont masqu´s, seule la sp´cification du package est visible. Cela a pour
e e
avantage de simplifier la tˆche de celui qui va utiliser le package.
a
– modularit´ : il est possible de d´velopper s´par´ment les diverses parties de l’application. le d´veloppement
e e e e e
devient ainsi un assemblage de package.
Ces deux aspects fournissent une souplesse certaine au niveau du d´veloppement : il est possible de modifier le
e
corps d’un package sans changer sa sp´cification, donc sans modifier le fonctionnement de l’application.
e
1.9.2 Sp´cification
e
La syntaxe permettant de cr´er l’entˆte est la suivante :
e e
CREATE OR REPLACE PACKAGE nompackage IS
/∗
declarations
∗/
END nomPackage ;
/
Par exemple,
CREATE OR REPLACE PACKAGE compteur IS
procedure reset ;
function nextValue return number ;
END compteur ;
/
1.9.3 Corps
La syntaxe permettant de cr´er le corps est la suivante :
e
CREATE OR REPLACE PACKAGE BODY nompackage IS
/∗
implementation
∗/
END nomPackage ;
/
Par exemple,
CREATE OR REPLACE PACKAGE BODY compteur IS
cpt N M E
U B R := 0 ;
PROCEDURE reset IS
BEGIN
cpt := 0 ;
END;
FUNCTION nextValue RETURN N M E IS
U BR
BEGIN
cpt := cpt + 1 ;
RETURN cpt − 1 ;
END;
END compteur ;
25
27. /
On peut utiliser un package depuis n’importe quel script PL/SQL :
DECLARE
nb N M E
U B R;
BEGIN
FOR nb IN 4 . . 2 0 LOOP
DBMS_OUTPUT . PUT_LINE ( COMPTEUR . nextValue ( ) ) ;
END LOOP ;
COMPTEUR . RESET ( ) ;
FOR nb IN REVERSE 0 . . 1 0 LOOP
DBMS_OUTPUT . PUT_LINE ( COMPTEUR . nextValue ( ) ) ;
END LOOP ;
END;
/
26
28. Chapitre 2
Exercices
2.1 Introduction au PL/SQL
Exercice 1
Ecrivez un programme affectant les valeurs 1 et 2 ` deux variables a et b, puis permutant les valeurs de ces deux
a
variables.
Exercice 2
Ecrivez un programme pla¸ant la valeur 10 dans une variable a, puis affichant la factorielle de a.
c
Exercice 3
Ecrivez un programme pla¸ant les valeurs 48 et 84 dans deux variables a et b puis affichant le pgcd de a et b.
c
27
29. 2.2 Tableaux et Structures
Exercice 1
1. Cr´ez un type tableau pouvant contenir jusqu’` 50 entiers.
e a
2. Cr´ez une variable de ce type , faites une allocation dynamique et dimensionnez ce tableau ` 20 emplacements.
e a
3. Placez dans ce tableau la liste des 20 premiers carr´s parfaits : 1, 4, 9, 16, 25, . . .
e
4. Inversez l’ordre des ´l´ments du tableau
ee
5. Affichez le tableau.
Exercice 2
Triez le tableau pr´c´dent avec la m´thode du tri ` bulle.
e e e a
Exercice 3
Recherchez, par dichotomie, si l’´l´ment 225 se trouve dans le tableau.
ee
Exercice 4
On impl´mente des listes chaˆ ees avec des tableaux de la sorte,
e ın´
SET SERVEROUTPUT ON
DECLARE
-- Maillon d’une liste cha^n´e
ı e
TYPE CELL IS RECORD
(
-- Donn´e de chaque maillon
e
data INTEGER,
-- Indice du maillon pr´c´dent de la liste,
e e
-- -1 s’il n’y en a pas
previous INTEGER,
-- Indice du maillon suivant de la liste,
-- -1 s’il n’y en a pas
next INTEGER
);
-- Type tableau contenant les maillons de la liste
TYPE TREE IS VARRAY (19) OF CELL;
-- Tableau contenant les maillons de la liste
t TREE;
-- indice du premier ´l´ment de la liste
e e
first integer;
-- indice du dernier ´l´ment de la liste
e e
last integer;
BEGIN
t := TREE();
t.extend(19);
-- Initialisation
FOR i IN 1..19 LOOP
t(i).data := power(i, 5) mod 19 ;
t(i).previous := i-1;
t(i).next := i+1;
END LOOP;
first := 1;
last := 19;
t(first).previous := -1;
t(last).next := -1;
28
30. -- Affichage
DECLARE
p integer := first;
BEGIN
WHILE p <> -1 LOOP
DBMS_OUTPUT.PUT_LINE(’(’ || p || ’, ’ ||
t(p).data || ’, ’ ||
t(p).previous || ’, ’ || t(p).next || ’)’);
p := t(p).next;
END LOOP;
END;
/* Ecrivez la suite vous-m^me... */
e
END;
/
Inversez l’ordre des ´l´ments de la liste, sans changer les indices des maillons (seulement en modifiant le chaˆ
ee ınage).
Exercice 5
Utilisez le tri ` bulle pour remettre les ´l´ments dans l’ordre. Les indications sont les mˆmes : ne d´placez pas les
a ee e e
maillons, vous n’avez le droit de toucher qu’au chaˆınage. Bon courage, l’aspirine n’est pas fournie.
29
31. 2.3 Utilisation PL/SQL
Nous travaillerons sur les donn´es A.6 et A.5.
e
Vous n’oublierez pas de placer des commit en des lieux bien choisis.
Exercice 1
Vous remarquerez que les valeurs des numpers de la table PERSONNE forment une s´quence de nombres de 1 ` 21.
e a
Utilisez une boucle dans laquelle vous placerez une requˆte pour recopier les couples nom/pr´nom de la table personne
e e
dans la table CLIENT.
Exercice 2
Ecrivez un script r´cup´rant le client de cl´ primaire la plus ´lev´e, et injectant ce client dans la table PERSONNEL.
e e e e e
Exercice 3
Ouvrez un compte courant pour chaque personne, effectuez un d´pˆt en esp`ce ´gal ` numpers ∗ 100 euros.
e o e e a
Exercice 4
Ouvrez un livret pour chaque personne ayant un numpers pair, faites un virement de leur compte courant vers ce
livret de sorte qu’il ne reste plus que 500 sur leur compte.
30
32. 2.4 Exceptions
Nous utiliserons les donn´es de A.7 et A.5
e
Vous ˆtes invit´s ` modifier le code de la s´ance pr´c´dente. Chaque fois qu’un SELECT ... INTO ... sera effectu´,
e e a e e e e
vous rattraperez les exceptions NO DATA FOUND et TOO MANY ROWS. A chaque insertion, vous ratrapperez l’exception
DUP VAL ON INDEX.
Exercice 1
Faites de sorte que les scripts important les donn´es des tables CLIENT ne puissent ˆtre ex´cut´s qu’une seule fois.
e e e e
Exercice 2
Les scripts remplissant la table Operation ne fonctionneront pas aujourd’hui... Mˆme s’il fonctionnaient la derni`re
e e
fois. Trouvez les codes d’erreurs des exceptions lev´es par ces scripts, rattrapez-les de la fa¸on la plus appropri´e qui
e c e
soit.
31
33. 2.5 Sous-programmes
Exercice 1
Ecrire une fonction r´cursive retournant bn , avec n entier positif ou nul.
e
Exercice 2
Am´liorer la fonction pr´c´dente en utilisant le fait que
e e e
n
bn = (b2 ) 2
si n est pair.
Pour les questions suivantes, utilisez les donn´es de A.5.
e
Exercice 3
Ecrire une fonction demi-freres prenant deux num´ros de personnes en param`tre et retournant vrai si et seulement
e e
si ces deux personnes ont un parent en commun.
Exercice 4
Ecrire une fonction cousins germains prenant deux num´ros de personnes en param`tre et retournant vrai si et
e e
seulement si ces deux deux individus sont cousins germains.
Exercice 5
Ecrire une proc´dure r´cursive affichant le nom de la personne dont le num´ro est pass´ en param`tre et se rappellant
e e e e e
r´cursivement sur le p`re de cette personne. Faites de sorte ` ne pas utiliser d’exceptions.
e e a
Exercice 6
Ecrire une proc´dure r´cursive affichant les noms des ascendants de sexe masculin de la personne dont le num´ro
e e e
est pass´ en param`tre.
e e
Exercice 7
Ecrire une fonction r´cursive prenant deux num´ros de personne A et B et retournant vrai si A est un ascendant
e e
de B.
Exercice 8
Ecrire une fonction prenant en param`tre deux num´ros de personne A et B et retournant, si l’un est un ascendant
e e
de l’autre, le nombre de g´n´rations les s´parant, −1 si l’un n’est pas un ascendant de l’autre.
e e e
Exercice 9
Pr´parez un verre d’aspirine et ´crivez une requˆte retournant le(s) couples(s) personnes s´par´es par le plus de
e e e e e
g´n´rations.
e e
Exercice 10
Reprendre le code du tp pr´c´dent, le d´couper en sous-programmes de la fa¸on la moins inintelligente possible.
e e e c
Bon courage.
32
34. 2.6 Curseurs
Exercice 1
Refaites les exercices de 2.3 en utilisant les curseurs.
Exercice 2
En utlisant les donnees A.5, ecrivez une fonction affichant toute la descendance d’une personne.
33
35. 2.7 Curseurs parametr´s
e
L’int´rˆt de ces exercices ´tant de vous familiariser avec les curseurs param´tr´s, vous ferez en sorte de ne pas
ee e e e
contourner leur usage. Nous utiliserons les donn´es de A.6
e
Exercice 1
Ecrire une proc´dure qui affiche tous les clients, et pour chaque client, la liste des comptes.
e
Exercice 2
Ecrire une proc´dure qui affiche tous les clients, et pour chaque client, la liste des comptes, et pour chacun de ces
e
comptes, l’historique des op´rations.
e
34
36. 2.8 Triggers
Impl´mentez les contraintes suivantes dans les donn´es de les donn´es de A.8. Vous ferez des sous-programmes
e e e
tenant sur une page, et ne contenant pas plus de trois niveaux d’imbrication. Vous r´pertorierez les num´ros d’erreurs
e e
que vous affecterez a chaque lev´e d’exception.
` e
1. Il ne doit pas ˆtre possible de modifier la note min dans la table prerequis.
e
2. Dans un module, il ne doit pas y avoir plus de effecMax ´l`ves inscrits.
ee
3. On ne peut cr´er un examen pour un module que s’il y a des ´l`ves inscrits dans ce module.
e ee
4. Un ´l`ve ne peut passer un examen que si sa date d’inscription est ant´rieure ` la date de l’examen.
ee e a
5. Il ne doit pas y avoir de circuit dans la table prerequis (il existe une fa¸on de la v´rifier en PL/SQL, mais
c e
comme vous ne la connaissez pas, faites un parcours en profondeur du graphe des pr´-requis)
e
6. Un ´l`ve s’inscrivant ` un module doit avoir eu au moins la note min ` tous les modules pr´-requis.
ee a a e
7. Ajouter dans ´tudiant un champ moyenne, celui-ci contiendra la moyenne de chaque ´tudiant s’il a pass´ les
e e e
examens de tous les modules dans lesquels il est inscrit.
8. Revenez sur la premi`re contrainte : il ne doit ˆtre possible de modifier une note min dans la table prerequis que
e e
s’il n’existe pas d’´l`ve dont une inscription serait invalid´e.
ee e
9. Il ne doit ˆtre possible de modifier effecMax que si des ´tudiants ne se retrouvent pas avec une inscription
e e
invalid´e.
e
Libre ` vous par la suite de trouver d’autres contraintes et de les impl´menter.
a e
35
37. 2.9 Packages
Exercice 1
Lancez deux sessions simultan´ment sur le mˆme serveur et invoquez les sous-programmes du package compteur
e e
depuis chacune des sessions. Que remarquez-vous ?
Exercice 2
Impl´menter le corps du package suivant (utilisez les donn´es de A.5).
e e
CREATE OR R E P L A C E P A C K A G E g e s t i o n _ a r b r e I S
c i r c u i t exception ;
c u r s o r f e u i l l e s r e t u r n p e r s o n n e%r o w t y p e ;
p r o c e d u r e a j o u t e P e r s o n n e ( n o m p e r s o n n e . n o m%t y p e ,
p r e n o m p e r s o n n e . p r e n o m%t y p e , p e r e p e r s o n n e . p e r e%t y p e ,
m e r e p e r s o n n e . m e r e%t y p e ) ;
p r o c e d u r e m o d i f i e P a r e n t s ( p e r s p e r s o n n e . n u m p e r s%t y p e ,
n u m P e r e p e r s o n n e . p e r e%t y p e , n u m M e r e p e r s o n n e . m e r e%t y p e ) ;
END g e s t i o n _ a r b r e ;
/
36
38. 2.10 R´visions
e
Impl´mentez les contraintes suivantes dans les donn´es de A.9.
e e
1. Les parents d’une mˆme personne sont des personnes diff´rentes.
e e
2. L’arbre g´n´alogique ne contient pas de circuit.
e e
3. Les dates de divorce sont ult´rieures aux dates de mariage.
e
4. Une mˆme personne ne peut pas ˆtre mari´e ` plusieurs personnes simultan´ment.
e e e a e
5. Personne ne peut ˆtre p`re d’une personne et m`re d’une autre.
e e e
6. Un mari ne peut pas ˆtre m`re et une femme ne peut pas ˆtre p`re.
e e e e
7. Deux personnes ayant du sang en commun ne peuvent se marier.
37
39. Chapitre 3
Corrig´s
e
3.1 Introduction au PL/SQL
−− E x e r c i c e 1
DECLARE
a N ME
U B R;
b N ME
U B R;
t N ME
U B R;
BEGIN
a := 1 ;
b := 2 ;
DBMS_OUTPUT . PUT_LINE ( ’a = ’ | | a ) ;
DBMS_OUTPUT . PUT_LINE ( ’b = ’ | | b ) ;
DBMS_OUTPUT . P U T _ L I N E ( ’ Let ’ ’ s swap a and b . . . The r e s u l t is : ’ );
t := a ;
a := b ;
b := t ;
DBMS_OUTPUT . PUT_LINE ( ’a = ’ | | a ) ;
DBMS_OUTPUT . PUT_LINE ( ’b = ’ | | b ) ;
END;
/
−− E x e r c i c e 2
DECLARE
a N ME
U B R;
res N M E
U B R;
counter N M E
U B R;
BEGIN
a := 1 0 ;
r e s := 1 ;
c o u n t e r := a ;
WHILE counter > 0 LOOP
r e s := r e s ∗ c o u n t e r ;
c o u n t e r := c o u n t e r − 1 ;
END L O O P ;
D B M S _ O U T P U T . P U T _ L I N E ( a | | ’ != ’ | | r e s ) ;
END;
/
−− E x e r c i c e 3
DECLARE
a N M E := 4 8 ;
U BR
b N M E := 8 4 ;
U BR
amodb N M E
U B R;
BEGIN
D B M S _ O U T P U T . P U T ( ’PGCD( ’ | | a | | ’ , ’ | | b | | ’ ) = ’ ) ;
WHILE b > 0 LOOP
a m o d b := a ;
W H I L E a m o d b >= b L O O P
a m o d b := a m o d b − b ;
END L O O P ;
a := b ;
b := a m o d b ;
END L O O P ;
DBMS_OUTPUT . PUT_LINE ( a ) ;
END;
/
38
40. 3.2 Tableaux et Structures
SET S E R V E R O U T P U T ON
−− Tableaux
DECLARE
T Y P E m o n t a b I S V A R R A Y ( 5 0 ) O F INTEGER ;
t montab ;
BEGIN
t := m o n t a b ( ) ;
t . extend ( 2 0 ) ;
−− I n i t i a l i s a t i o n
F O R i IN 1 . . 2 0 L O O P
t ( i ) := i ∗ i ;
END L O O P ;
−− I n v e r s i o n de l ’ o r d r e d e s ´ l´ m e n t s
e e
DECLARE
temp i n t e g e r ;
BEGIN
F O R i IN 1 . . 1 0 L O O P
t e m p := t ( i ) ;
t ( i ) := t (20− i + 1 ) ;
t (20− i +1) := t e m p ;
END L O O P ;
END;
−− A f f i c h a g e
F O R i IN 1 . . 2 0 L O O P
DBMS_OUTPUT . PUT_LINE ( ’ t ( ’ | |
i || ’) = ’ || t(i ));
END L O O P ;
−− Tri ` b u l l e
a
DECLARE
temp i n t e g e r ;
BEGIN
F O R i IN R E V E R S E 2 . . 2 0 L O O P
F O R j IN 2 . . i L O O P
IF t ( j − 1) > t ( j ) T E H N
t e m p := t ( j ) ;
t ( j ) := t ( j − 1 ) ;
t ( j −1) := t e m p ;
END I F ;
END L O O P ;
END L O O P ;
END;
−− A f f i c h a g e
F O R i IN 1 . . 2 0 L O O P
DBMS_OUTPUT . PUT_LINE ( ’ t ( ’ | |
i || ’) = ’ || t(i ));
END L O O P ;
−− Recherche par d i c h o t o m i e de l ’ ´ l´ m e n t 225
e e
DECLARE
i n f INTEGER := 1 ;
s u p INTEGER := 2 0 ;
m INTEGER ;
X INTEGER := 4 0 0 ;
BEGIN
LOOP
DBMS_OUTPUT . PUT_LINE ( ’ i n f = ’ | | inf ||
’ ; sup = ’ | | s u p ) ;
m := ( i n f + s u p ) / 2 ;
EXIT W E HN
t ( m ) = X OR i n f = s u p ;
IF t ( m ) > X T E H N
s u p := m −1;
ELSE
i n f := m +1;
END I F ;
END L O O P ;
IF t ( m ) = X T E H N
DBMS_OUTPUT . PUT_LINE ( X | |
’ e s t dans l e t a b l e a u ’ ) ;
ELSE
DBMS_OUTPUT . PUT_LINE ( X | |
’ n” e s t pas dans l e t a b l e a u ’ ) ;
END I F ;
END;
END;
/
39
41. −− S t r u c t u r e s
DECLARE
−− M a i l l o n d ’ une l i s t e c h aˆn´ e
ı e
TYPE CELL IS RECORD
(
−− Donn´e de chaque m a i l l o n
e
d a t a INTEGER,
−− I n d i c e du m a i l l o n p r´ c´ d e n t de l a l i s t e ,
e e
−− −1 s ’ i l n ’ y en a pas
p r e v i o u s INTEGER,
−− I n d i c e du m a i l l o n s u i v a n t de l a l i s t e ,
−− −1 s ’ i l n ’ y en a pas
next INTEGER
);
−− Type t a b l e a u c o n t e n a n t l e s m a i l l o n s de l a l i s t e
TYPE TREE IS VARRAY ( 1 9 ) OF CELL ;
−− Tableau c o n t e n a n t l e s m a i l l o n s de l a l i s t e
t TREE ;
−− i n d i c e du premier ´ l´ m e n t de l a l i s t e
e e
first integer ;
−− i n d i c e du d e r n i e r ´ l´ m e n t de l a l i s t e
e e
last integer ;
BEGIN
t := T R E E ( ) ;
t . extend ( 1 9 ) ;
−− I n i t i a l i s a t i o n
F O R i IN 1 . . 1 9 L O O P
t ( i ) . d a t a := p o w e r ( i , 5 ) m o d 19 ;
t ( i ) . p r e v i o u s := i −1;
t ( i ) . next := i +1;
END L O O P ;
f i r s t := 1 ;
l a s t := 1 9 ;
t ( f i r s t ) . p r e v i o u s := −1;
t ( l a s t ) . next := −1;
−− A f f i c h a g e
DECLARE
p i n t e g e r := f i r s t ;
BEGIN
W H I L E p <> −1 L O O P
DBMS_OUTPUT . PUT_LINE ( ’ ( ’ | | p | | ’, ’ ||
t ( p ) . data | | ’ , ’ | |
t ( p ) . previous | | ’ , ’ | |
t ( p ) . next | | ’ ) ’ ) ;
p := t ( p ) . next ;
END L O O P ;
END;
−− I n v e r s i o n de l ’ o r d r e d e s ´ l´ m e n t s
e e
DECLARE
t e m p INTEGER ;
BEGIN
F O R i IN 1 . . 1 9 L O O P
t e m p := t ( i ) . p r e v i o u s ;
t ( i ) . p r e v i o u s := t ( i ) . next ;
t ( i ) . next := t e m p ;
END L O O P ;
f i r s t := 1 9 ;
l a s t := 1 ;
END;
−− A f f i c h a g e
DECLARE
p i n t e g e r := f i r s t ;
BEGIN
W H I L E p <> −1 L O O P
DBMS_OUTPUT . PUT_LINE ( ’ ( ’ | |
p || ’ , ’ ||
t ( p ) . data | | ’ , ’ | |
t ( p ) . previous | | ’ , ’ ||
t ( p ) . next | | ’ ) ’ ) ;
p := t ( p ) . next ;
END L O O P ;
END;
−− Tri ` b u l l e
a
DECLARE
i i n t e g e r := l a s t ;
j integer ;
BEGIN
W H I L E t ( t ( i ) . p r e v i o u s ) . p r e v i o u s <> −1 L O O P
j := f i r s t ;
W H I L E i< >j L O O P
I F ( t ( j ) . d a t a > t ( t ( j ) . next ) . d a t a ) T E
H N
40