Вы находитесь на странице: 1из 47

INSIA SIGL 3 ORACLE PL-SQL

Procdures Fonctions - Triggers


Bertrand LIAUDET

SOMMAIRE
SOMMAIRE PL-SQL ORACLE 1. Programme et script 2. Variables et types scalaires 3. Procdures 4. Fonctions 5. Caractristiques des paramtres de procdure et de fonction 6. Gestion des procdures et des fonctions 7. Les structures de contrle 8. Types composs 9. SQL et PL-SQL 10. Les curseurs 11. Les exceptions 12. Les triggers (dclencheurs) 13. Gestion des triggers
Premire dition : juin 2008 Deuxime dition : mai 2010

1 2 2 5 10 12 14 16 17 20 25 29 36 42 47

INSIA Cours ORACLE - PL-SQL - page 1/47 - Bertrand LIAUDET

PL-SQL ORACLE

1.

Programme et script

Structure dun programme : le bloc


[ DECLARE ] BEGIN [ EXCEPTION ] END ;

Au minimum : BEGIN et END ; Programme en ligne


SQL> 2 3 4 begin dbms_output.put_line('Bonjour'); end; /

Procdure PL/SQL termine avec succs.

Le / demande lexcution du bloc en cours. Lexcution est correcte, mais rien ne saffiche ! Il faut passer le paramtre serveroutput ON.
SQL> show serveroutput serveroutput OFF SQL> set serveroutput ON

On peut aussi afficher la dure dexcution des instructions :


SQL> show timing timing OFF SQL> set timing ON

On peut relancer lexcution avec /


SQL> / Bonjour Procdure PL/SQL termine avec succs. SQL>

On peut aussi rafficher le code en relanant lexcution avec RUN :


SQL> run 1 begin 2 dbms_output.put_line('Bonjour');
INSIA Cours ORACLE - PL-SQL - page 2/47 - Bertrand LIAUDET

3 end; 4 / Bonjour Procdure PL/SQL termine avec succs. SQL>

A noter : / et RUN sappliquent de faon gnrale la dernire instruction SQL. On peut vider le buffer :
SQL> clear buffer buffer effac SQL> / SP2-0103: Rien excuter dans la mmoire tampon SQL.

Script Les programmes peuvent tre saisies directement sous SQLPLUS ou enregistr dans des scripts.
-- test.sql -- script qui affiche bonjour begin dbms_output.put_line('Bonjour'); end; /

Excution du script
SQL> @test Bonjour Procdure PL/SQL termine avec succs. SQL>

Commentaires
/* */ -- commentaires derrire deux tirets et un espace script de dfinition dune procdure procdure bonjour : affiche bonjour, usage des commentaires en style C

Affichage : dbms_output.put_line Un seul argument. Type au choix : chane, number ou une date.
SQL> begin 2 dbms_output.put_line('Bonjour '||user||'. Nous sommes le '||to_char(sysdate, 'dd month yyyy')); 3 end; 4 / Bonjour SM4R3M1. Nous sommes le 05 june 2008 Procdure PL/SQL termine avec succs.

Version sans espace aprs june :


2 dbms_output.put_line('Bonjour '||user||'. Nous sommes le '||rtrim(to_char(sysdate, 'dd month'))||' '||to_char(sysdate,'yyyy'));

Bonjour SM4R3M1. Nous sommes le 05 june 2008

INSIA Cours ORACLE - PL-SQL - page 3/47 - Bertrand LIAUDET

Instructions SQL Pas de SELECT classique en PL-SQL Pas de DDL en PL-SQL : CREATE TABLE, DROP TABLE, ALTER TABLE
SQL> begin 2 select * from employes; 3 end; 4 / select * from employes; * ERREUR la ligne 2 : ORA-06550: Ligne 2, colonne 1 : PLS-00428: une clause INTO est attendue dans cette instruction SELECT

Seules les instructions du DML sont autorises : INSERT, UPDATE, DELETE


SQL> 2 3 3 4 begin update employes set comm = comm + 0.1comm where job=MANAGER; end; /

Procdure PL/SQL termine avec succs. SQL>

Blocs imbriqus On peut dclarer des blocs dinstructions tout moment dans un bloc. Quand on dclare un bloc dinstruction, on peut y associer de nouvelles dclarations de variables.

INSIA Cours ORACLE - PL-SQL - page 4/47 - Bertrand LIAUDET

2.

Variables et types scalaires

Variables de substitution (variable globale) Cration dune variable


DEF[INE] maVar = texte

La variable est accessible pendant toute la session. Cest un CHAR (chane de caractres). Toutefois, il pourra sexploiter comme un NUM. Suppression dune variable
UNDEF[INE] maVar

Saisie dune valeur


ACCEPT maVar NUM prompt entrez un nombre : ACCEPT maVar CHAR prompt entrez du texte :

ACCEPT dfinit la variable si a na pas dj t fait. Afficher la valeur dune variable


DEF[INE] maVar

Afficher toutes les variables


DEF[INE]

Les variables dans les select


Select * from &tab where num > &num and nom = &nom;

Le & fait rfrence 3 variables : tab, num et nom qui ont du tre dclares prcdemment. Avec && : la variable est cre si elle ne ltait pas dj. Mais la valeur quelle aura prise la premire excution sera la valeur quelle aura pour la suivante. requtes SQL : &nomVar &nomVar dans une requte est substitu par la valeur de nomVar si cette variable existe. Sinon, le systme demande une valeur pour nomVar. Aprs excution de la requte, la variable nomVar est dtruite. Format classique
SQL> Select * from emp where ename = &v_nomEmp ; Entrez une valeur pour v_nomEmp : DUPONT

Utilisation du like
SQL> Select * from emp where ename like &v_nomEmp ; Entrez une valeur pour v_nomEmp : D%

INSIA Cours ORACLE - PL-SQL - page 5/47 - Bertrand LIAUDET

Ou encore
Entrez une valeur pour var_subst : % and JOB like MANAGER

On aura ainsi contourner la requte initiale ! requtes SQL : &&nomVar &&nomVar dans une requte est substitu par la valeur de nomVar si cette variable existe. Sinon, le systme demande une valeur pour nomVar. Aprs excution de la requte, la variable nomVar est conserve. Variables de liaison (variable globale) Cration dune variable en SQL-PLUS
VAR [IABLE] [nom [type] ]

Exemple :
VAR[IABLE] maVar VARCHAR2(10)

Utilisation en PL-SQL
begin :maVar := bonjour; dbms_output.put_line(:maVar); end; /

Afficher la valeur dune variable en SQL-PLUS


Print :maVar

Afficher toutes les variables


VAR[IABLE]

Zone des variables et des type des programmes : DECLARE


DECLARE [TYPE nomType IS definitionDuType;] [...] NomVar [CONSTANT] nomType [NOT NULL] [ {DEFAULT | :=} VALEUR ]; [...] BEGIN [ EXCEPTION ] END ;

Les CONSTANT sont associs un DEFAULT. Une variable dclare NOT NULL doit avoir une valeur dinitialisation : elle est donc associe un := . Zone des variables des procdures et des fonctions :
{IS | AS} [TYPE nomTypeLocal IS definitionDuType;] [...] [nomVarLocale TYPE;] [...] BEGIN

Nommer les variables


INSIA Cours ORACLE - PL-SQL - page 6/47 - Bertrand LIAUDET

Il faut faire attention ne pas confondre le nom des variables PL-SQL avec celui des attributs des tables. Pour viter a : on prfixe toutes les variables PL_SQL dun v_ . En cas dambigut, PL-SQL utilise toujours lattribut de table. SUBTYPE : cration dun type avec restriction du domaine de valeur Prsentation On peut crer des types qui restreignent le domaine des valeurs autorises par le type original. On peut aussi crer des types qui renomment des types existants. Exemple
-- definition du type chiffres: valeurs entre 9 et 9 DECLARE SUBTYPE CHIFFRE is NUMBER(1,0) ; subtype FLOAT2 is FLOAT ; n CHIFFRE; f FLOAT ; BEGIN n:=&entre; dbms_output.put_line('Chiffre saisi : '|| n); f:=&entre; dbms_output.put_line('Flottant saisi : '|| n); END; /

Syntaxe
SUBTYPE nomSousType IS nomType [(contraintes)][NOT NULL];

Les types scalaires Les caractres


CHAR( n ) NCHAR( n ) VARCHAR2( n ) NVARCHAR2( n ) CLOB NCLOB LONG Chane fixe de n caractres. Chane fixe de n caractres. Format Unicode Chane variable de n caractres. Chane variable de n caractres. Format Unicode Flot de caractres Flot de caractres. Format Unicode Flot de caractres n octets. 2000 caractres max n octets. 2000 caractres max Taille variable. 4000 caractres max Taille variable. 4000 caractres max Jusqu 4 Giga Jusqu 4 Giga Jusqu 2 Giga. Obsolte

Unicode est une mthode de codage universelle utilise par XML, Java, Javascript, LDAP et WML. Sous-types
NOM CHARACTER Type dorigine CHAR Caractristiques Identique CHAR

Les donnes numriques


NUMBER ( N, D) N chiffres en tout, dont D pour la partie dcimale. Avec N+D <39. 21 octets max. De +/- 1*10 130 +/- 9.99*10 +125

INSIA Cours ORACLE - PL-SQL - page 7/47 - Bertrand LIAUDET

Sous-types
NOM INTEGER PLS_INTEGER Type dorigine NUMBER (38, 0) NUMBER Caractristiques Taille variable. 4000 caractres max Entiers signes. Moins coteux quun NUMBER. Plus performant pour les oprations mathmatiques due BINARY_INTEGER. Les valeurs relles sont

arrondies lentier le plus proche.


BINARY_INTEGER NATURAL, POSITIVE NATURALN, POSITIVEN SIGNTYPE NUMBER Entiers signes. Moins coteux quun NUMBER. BINARY_INTEGER Entiers positifs. BINARY_INTEGER Entiers positifs et non nul. BINARY_INTEGER -1, 0 ou 1 Dcimaux, precision de 38 chiffres Flottants Entiers sur 38 chiffres.

DEC, DECIMAL, NUMERIC NUMBER DOUBLE PRECISON, NUMBER FLOAT, REAL INTEGER, INT, SMALLINT NUMBER

Les dates
DATE TIMESTAMP INTERVAL YEAR TO MONTH INTERVAL DAY TO SECOND TIMESTAMP WITH TIME ZONE TIMESTAMP TIME ZONE WITH Date et heure jusquaux secondes Date et heure jusquaux fractions de secondes Priode en annes et mois Priode en jours, heures, seconde Timestamp avec dcalage horaire par rapport au serveur. 7 octets 7 11 octets. La prcision des fractions de seconde va de 0 9, 6 par dfaut. 5 octets 11 octets 13 octets

LOCAL Timestamp avec dcalage horaire 7 11 octets par rapport au client.

Les donnes binaires


BLOB BFILE RAW ( size ) LONG RAW Donnes binaires Donnes binaries stocks dans un fichier externe. Donnes binaires Donnes binaires Jusqu 4 Gigas Jusqu 4 Gigas Jusqu 2000 octets. Obsolte Jusqu 2 Gigas. Obsolte

Les boolens (type logique)


BOOLEAN SIGNTYPE BINARY_INTEGER TRUE, FALSE, NULL -1, 0 ou 1

Le PL-SQL dfinit un type BOOLEAN. Un BOOLEAN peut prendre 3 valeurs : TRUE, FALSE ou NULL. Le type SIGNTYPE peut aussi jouer le rle de Boolen : TRUE = 1, FALSE = 0, NULL = -1. Attention, le SQL ORACLE ne dfinit pas de type BOOLEAN , ni le type SIGNINT. Un attribut de table ne peut pas ni tre de type BOOLEAN, ni tre de type SIGNTYPE.
Declare a Boolean:=&verite; Begin If a then dbms_output.put_line('vrai');

INSIA Cours ORACLE - PL-SQL - page 8/47 - Bertrand LIAUDET

elsif not(a) then dbms_output.put_line('faux'); else dbms_output.put_line('null'); end if; end; /

%TYPE : Utiliser un type provenant dun attribut dune table ou dune variable Prsentation Partout o on utilise un nom de type (nomType), on peut utiliser un type provenant dun attribut table ou dune variable. Exemple
v_deptno EMP.DEPTNO%TYPE;

Syntaxe
NomVar [CONSTANT] {nomTable.nomAttribut | nomVariable}%TYPE [NOT NULL] [ {DEFAULT | :=} VALEUR ];

INSIA Cours ORACLE - PL-SQL - page 9/47 - Bertrand LIAUDET

3.

Procdures

Bloc procdure
SQL> declare procedure affichage is Begin dbms_output.put_line('Bonjour '||user||'. Nous sommes le '||rtrim(to_char(sysdate, 'dd month'))||' '||to_char(sysdate,'yyyy')); end ; begin affichage; end ; / Bonjour SM4R3M1. Nous sommes le 05 june 2008 Procdure PL/SQL termine avec succs. SQL>

Cration dune procdure


SQL> create or replace procedure affichage is Begin dbms_output.put_line('Bonjour '||user||'. Nous sommes le '||rtrim(to_char(sysdate, 'dd month'))||' '||to_char(sysdate,'yyyy')); end ; / Procdure cre. SQL>

Appel dune procdure


SQL> call affichage(); Bonjour BERTRAND. Nous sommes le 05 juin 2008 Appel termin. SQL>

Paramtres en entre dune procdure


SQL> create or replace procedure AffEmpDept(v_deptno EMP.DEPTNO%TYPE) is v_nbemp number; Begin select count(*) into v_nbemp from emp where deptno=v_deptno; dbms_output.put_line('Le dpartement '||v_deptno|| ' contient '||v_nbemp||' employe(s)') ; dbms_output.put_line('.'); for i_emp in ( select empno, ename, sal, deptno from emp where deptno = v_deptno ) loop dbms_output.put_line('. Employ n'||i_emp.EMPNO|| ' : '||i_emp.ENAME||', salaire = '||i_emp.SAL); end loop; end; / Procdure cre. SQL>

Appel dune procdure


INSIA Cours ORACLE - PL-SQL - page 10/47 - Bertrand LIAUDET

SQL> call affempdept(10); Le dpartement 10 contient 4 employe(s) . . Employ n7839 : KING, salaire = 5000 . Employ n7782 : CLARK, salaire = 2695 . Employ n7934 : MILLER, salaire = 1300 . Employ n7935 : DUPONT, salaire = 800 Appel termin. SQL>

Structure dune procdure


CREATE [OR REPLACE] PROCEDURE nomProcedure [( nomVar [{IN | OUT | IN OUT }] TYPE [, ...] )] {IS | AS} [nomVarLocale TYPE;] [...] BEGIN instructions; [EXCEPTION instructions dexception] END [nomProcedure];

IS ou AS sont quivalent On reviendra sur les paramtres en sortie (OUT) et en entre-sortie (IN OUT) On reviendra sur les EXCEPTIONS Rappel de la smantique du mtalangage Majuscule : les mots-cls Entre crochets : ce qui est facultatif Entre accolades : proposition de plusieurs choix possibles. Les choix sont spars par des barres verticales. 3 petits points avant un crochet fermant : ce qui prcde sur la mme ligne, ou la ligne prcdente, ou le bloc prcdent, peut tre rpt. Le texte qui prcde les 3 petits points et est aprs le crochet ouvrant est rpt aussi. Cet aspect du mtalangage nest pas parfaitement formalis et demande une interprtation intuitive. En italique (ou pas) et en minuscule : le nom dune valeur, dune variable, dune expression, dune instruction ou dune srie dinstructions. 3 petits points tout seul sur une ligne : tout ce qui est possible entre la ligne qui prcde les 3 petits points et la ligne qui les suit.

RETURN Linstruction RETURN entre le BEGIN et le END permet de quitter la procdure.

INSIA Cours ORACLE - PL-SQL - page 11/47 - Bertrand LIAUDET

4.

Fonctions

Bloc fonction
SQL> declare function moySal return number is v_moy EMP.SAL%TYPE; begin select avg(sal) into v_moy from emp; return v_moy; end; begin dbms_output.put_line('Moyenne des salaire : '||moySal); end ; / Moyenne des salaire : 2043,5 Procdure PL/SQL termine avec succs. SQL>

Cration dune fonction


SQL> create or replace function moySal return number is v_moy EMP.SAL%TYPE; begin select avg(sal) into v_moy from emp; return v_moy; end moySal; / Fonction cre. SQL>

Appel dune fonction


SQL> begin 2 dbms_output.put_line('Moyenne des salaire : '||moySal); 3 end; 4 / Moyenne des salaire : 2043,5 Procdure PL/SQL termine avec succs. SQL> SQL> select empno, ename, sal from emp 2 where sal > moysal order by sal; EMPNO ENAME SAL ---------- ---------- ---------7782 CLARK 2695 7788 SCOTT 3000 7902 FORD 3000 7698 BLAKE 3135 7566 JONES 3272,5 7839 KING 5000 6 ligne(s) slectionne(s).

Paramtres en entre dune fonction


SQL> create or replace function moySalDept(v_deptno EMP.DEPTNO%TYPE) return number is v_moy EMP.SAL%TYPE; begin select avg(sal) into v_moy from emp where deptno=v_deptno; return v_moy; end moySalDept; / Fonction cre. SQL>

INSIA Cours ORACLE - PL-SQL - page 12/47 - Bertrand LIAUDET

Appel dune fonction


SQL> create or replace procedure AffEmpDept2(v_deptno EMP.DEPTNO%TYPE) is v_nbemp number; v_nbemptot number; v_moysal EMP.SAL%TYPE; Begin v_moysal := moySalDept(v_deptno); select count(*) into v_nbemp from emp where deptno=v_deptno and sal > v_moysal; select count(*) into v_nbemptot from emp where deptno=v_deptno; dbms_output.put_line('Le dpartement '||v_deptno|| ' contient '||v_nbemp||' employe(s) sur '||v_nbemptot|| ' salaire > ' ||v_moysal) ; dbms_output.put_line('.'); for i_emp in ( select empno, ename, sal, deptno from emp where deptno = v_deptno and sal > v_moysal ) loop dbms_output.put_line('. Employ n'||i_emp.EMPNO|| ' : '||i_emp.ENAME||', salaire = '||i_emp.SAL); end loop; end; / Procdure cre. SQL> SQL> call affempdept2(10); Le dpartement 10 contient 2 employe(s) sur 4 salaire > 2448,75 . . Employ n7839 : KING, salaire = 5000 . Employ n7782 : CLARK, salaire = 2695 Appel termin. SQL>

Structure dune fonction


CREATE [OR REPLACE] FUNCTION nomFonction [( nomVar [{IN | OUT | IN OUT }] type [, ...] )] RETURN type {IS | AS} [nomVarLocale TYPE;] [...] BEGIN instructions; [EXCEPTION instructions dexception] END [nomFonction];

IS ou AS sont quivalent On reviendra sur les paramtres en sortie (OUT) et en entre-sortie (IN OUT) On reviendra sur les EXCEPTIONS.

INSIA Cours ORACLE - PL-SQL - page 13/47 - Bertrand LIAUDET

5.

Caractristiques des paramtres de procdure et de fonction

Syntaxe
CREATE [OR REPLACE] {PROCEDURE | FUNCTION} nomBloc [( nomVar [ {IN | OUT [NOCOPY] | IN OUT [NOCOPY] } ] type [ { := | DEFAULT } valeurParDefaut ] [, ...] )] [ RETURN type] {IS | AS}

IN : paramtre en entre Un argument IN est un argument dont la valeur dentre est utilise par le bloc et nest pas modifie par le bloc (procdure ou fonction). Il se comporte comme une constante dans le bloc. OUT : paramtre en sortie Un argument OUT est un argument dont la valeur dentre nest pas utilise par le bloc. Largument est initialise et modifie par le bloc (procdure ou fonction) pour produire une valeur en sortie du bloc. IN OUT : paramtre en entre-sortie Ce mode est un combinaison du IN et du OUT. Un argument IN OUT est un argument dont la valeur dentre est utilise par le bloc (IN) et sera aussi modifie par le bloc (OUT). NOCOPY 2 manires de passer un argument : par rfrence (adresse) ou par valeur. Le passage par rfrence est gnralement plus rapide, particulirement dans le cas dargument volumineux. Par dfaut, les arguments IN sont passs par rfrence, les arguments OUT et IN OUT sont passs par valeur. Pour passer les arguments OUT ou IN OUT par rfrence, on prcise NOCOPY dans la dclaration : OUT NOCOPY ou IN OUT NOCOPY. Valeur par dfaut On peut fixer une valeur par dfaut aux arguments. Cette valeur sera utilise si aucune autre nest fournie lappel du bloc. Passage des paramtres par position et par nom Quand on utilise un bloc (procdure ou fonction), on peut passer les paramtres par position ou par nom.

INSIA Cours ORACLE - PL-SQL - page 14/47 - Bertrand LIAUDET

Passage par position Le premier argument dappel prend la place du premier argument formel, etc. Passage par nom Les arguments dappel sont relis au nom de largument formel auxquels ils correspondent.. Ils peuvent alors est passs dans nimporte quel ordre.
BEGIN [instructions ] nomBloc( nomArgumentFormel => argumentDAppel [, ...] ); [instructions ] END;

INSIA Cours ORACLE - PL-SQL - page 15/47 - Bertrand LIAUDET

6.

Gestion des procdures et des fonctions

Lister les procdures et les fonctions La vue USER_PROCEDURES permet de lister les procdures et les fonctions sans distinction entre les deux.
Select object_name from user_procedures ; // affiche les procedures et les functions.

La vue USER_SOURCE permet de lister les procdures et les fonctions en distinguant entre les deux :
Select distinct name, type from user_source;

Voir le code des procdures et les fonctions La vue USER_SOURCE contient chaque ligne du code des procdures et des fonctions.
Set linesize 100 Col text format A80 Select line, text from user_source where name = NOMPROCouFONC;

Dbogage
SQL> Show errors ;

Show errors utilise la vue USER_ERRORS.


SQL> select object_name, object_type, status from user_object where object_name=NOMPROCouFONC;

Lattribut STATUS de la vue USER_OBJECTS prcise ltat des procdures et des fonctions. Suppression dune fonction
DROP FUNCTION nomFonction ;

Cette suppression met jour les vues USER_SOURCE et USER_PROCEDURES. Suppression dune procdure
DROP PROCEDURE nomProcdure ;

Cette suppression met jour les vues USER_SOURCE et USER_PROCEDURES.

INSIA Cours ORACLE - PL-SQL - page 16/47 - Bertrand LIAUDET

7. Tests

Les structures de contrle

Syntaxe
IF expression THEN instructions; [ ELSIF expression THEN instructions; ...] [ ELSE instructions;] END IF;

Rappel de la smantique du mtalangage Majuscule : les mots-cls Entre crochets : ce qui est facultatif Entre accolades : proposition de plusieurs choix possibles. Les choix sont spars par des barres verticales. 3 petits points avant une accolade fermante : ce qui est entre accolades peut tre rpt. En italitque : le nom dune valeur, dune variable, dune expression, dune instruction ou dune srie dinstructions. Table de vrit du AND, OR et NOT en logique trivalente : AND TRUE FALSE NULL OR TRUE FALSE NULL NOT TRUE TRUE FALSE NULL TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE NULL FALSE TRUE NULL NULL FALSE NULL NULL TRUE NULL NULL NULL NULL

Case simple: case sur valeur Syntaxe


CASE expression WHEN valeur THEN instructions [, ] [ ELSE instructions ]
INSIA Cours ORACLE - PL-SQL - page 17/47 - Bertrand LIAUDET

END CASE;

Case avec recherche : case sur condition Syntaxe


CASE expression WHEN condition THEN instructions [, ] [ ELSE instructions ] END CASE;

On sort du CASE ds quune condition est vrifie. Boucles sans fin : LOOP Syntaxe
[<< nomBoucle >>] LOOP instructions ; END LOOP [ nomBoucle ] ;

On sort de la boucle sans fin avec un EXIT :


EXIT [ nomBoucle ] [ WHEN condition ] ;

LEXIT seul permet de sortir sans condition. En gnral un EXIT seul est dans un test. LEXIT WHEN inclut la condition de sortie. En gnral, il nest pas dans un test. Boucles WHILE : tant que Syntaxe
[<< nomBoucle >>] WHILE condition LOOP instructions ; END LOOP [ nomBoucle ] ;

Boucle FOR : compteur Syntaxe


[<< nomBoucle >>] FOR variable IN [REVERSE] expression1 .. expression2 LOOP instructions ; END LOOP [ nomBoucle ] ;

Boucle FORALL : compteur avec regroupement des instructions LMD Syntaxe


[<< nomBoucle >>] FORALL variable IN expression1 .. expression2 LOOP [instructions ;] instruction LMD ; [instructions ;] END LOOP [ nomBoucle ] ;

INSIA Cours ORACLE - PL-SQL - page 18/47 - Bertrand LIAUDET

La boucle FORALL remplace le FOR et permet de regrouper les instructions LMD de la boucle en une seule srie. Ca optimise le traitement. Exit : sortie de boucle Prsentation Exit permet de quitter la boucle. Syntaxe
EXIT [nomBoucle] [WHEN condition] ;

INSIA Cours ORACLE - PL-SQL - page 19/47 - Bertrand LIAUDET

8.

Types composs

Classification des types 3 types de variable Scalaire : atomique. Cest un des types de colonne des tables. Compos : comprend plus dun lment. Rfrence : pointeur. Pas trait. 2 types de variable compose Les enregistrements : RECORD et %ROWTYPE Les tableaux : TYPE, ARRAY et TABLE Le type RECORD Dclaration dun type RECORD
TYPE nomType IS RECORD ( NomChamp1 TYPE [NOT NULL] [:= expression] [,...] );

Dclaration dune variable de type RECORD


TYPE nomType IS RECORD ( NomChamp1 TYPE [NOT NULL] [:= expression] [,...] );

Usage dune variable de type RECORD Les variables de type RECORD peuvent correspondre un tuple.
DECLARE TYPE type_employe IS RECORD ( empno EMP.EMPNO%TYPE, ename EMP.ENAME%TYPE, sal EMP.SAL%TYPE ); v_emp type_employe; BEGIN select empno, ename, sal into v_emp from emp where empno=&numEmp; dbms_output.put_line(v_emp.ename||', employe n' || v_emp.empno); dbms_output.put_line('salaire mensuel :'||v_emp.sal) ; dbms_output.put_line('salaire annuel :'||12*v_emp.sal) ; END ; /

%ROWTYPE : dclarer une variable dont le type correspond la dfinition dune table Prsentation

INSIA Cours ORACLE - PL-SQL - page 20/47 - Bertrand LIAUDET

Partout o on utilise un nom de type (nomType), on peut utiliser un type RECORD provenant dune table ou dune variable. Exemple
DECLARE employe EMPO%ROWTYPE; BEGIN employe.empno := 9000; etc.

Remarque Il ne faut pas crer des types synonyme ou des sous-types partir de type ROWTYPE. Le tableau de taille fixe : ARRAY Dclaration dun type tableau de taille variable : ARRAY
TYPE nomType IS VARRAY (taille) OF nomTypeElt [NOT NULL];

Dclaration dune variable de type tableau de taille fixe : ARRAY


TYPE nomType IS VARRAY (taille) OF nomTypeElt [NOT NULL]; v_tab nomType ;

Usage dune variable de type TABLE


DECLARE TYPE type_tabNumber IS ARRAY(5) OF NUMBER; v_tab type_tabNumber; i number; n number; BEGIN v_tab:=type_tabNumber(1, 2, 3, 4, 5); FOR i IN 1 .. 5 LOOP dbms_output.put_line('v_tab['||I||']='||v_tab(i)); END LOOP; v_tab(1):=11; v_tab(2):=12; v_tab(3):=13; v_tab(4):=14; v_tab(5):=15; FOR i IN 1 .. 5 LOOP dbms_output.put_line('v_tab['||I||']='||v_tab(i)); END LOOP; END; /

Le tableau de taille variable : TABLE Dclaration dun type tableau de taille variable : TABLE
TYPE nomType IS TABLE OF nomTypeElt [NOT NULL] INDEX BY {PLS_INTEGER | BINARY_INTEGER | VARCHAR2(taille)};

PLS_INTEGER et BINARY_INTEGER sont quivalents. Ce sont des types entiers. Les rels sont arrondis pour donner un entier. Lentier correspond lindice daccs aux lments du tableau. VARCHAR2(taille) permet de donner une chane de caractre comme indice daccs un lment du tableau.

INSIA Cours ORACLE - PL-SQL - page 21/47 - Bertrand LIAUDET

Dclaration dune variable de type tableau de taille variable : TABLE


TYPE type_tabEmpnos IS TABLE of emp.empno%type; V_tabEmpno type_tabEmpnos

Usage dune variable de type TABLE


DECLARE TYPE type_tabEmpnos IS TABLE of emp.empno%type INDEX BY PLS_INTEGER; v_tabEmpnos type_tabEmpnos; BEGIN select empno BULK COLLECT INTO v_tabEmpnos from emp where ROWNUM<3; v_tabEmpnos(-5):=10; v_tabEmpnos(10000):=320; dbms_output.put_line(v_tabEmpnos(1)); dbms_output.put_line(v_tabEmpnos(2)); dbms_output.put_line(v_tabEmpnos(-5)); dbms_output.put_line(v_tabEmpnos(10000)); END; /

A noter v_tabEmpnos(-5):=10; Les indices dun tableau de taille variable ne sont pas ncessairement contigus. Select BULK COLLECT INTO : equivalent Select INTO pour une variable scalaire. Le BULK COLLECT INTO ne fonctionne quavec des index de type PLS_INTEGER ou BINARY_INTEGER ;
DECLARE TYPE type_tabSalMoy IS TABLE of FLOAT INDEX BY varchar2(10); v_tabMoy type_tabSalMoy; BEGIN select avg(sal) INTO v_tabMoy('CLERK') from emp where job = 'CLERK'; dbms_output.put_line('Moyenne des salaries des CLERK : ' ||v_tabMoy('CLERK') ) ; END; /

A noter INDEX BY varchar2(10); v_tabMoy('CLERK'). Lindice du tableau est une chane.


DECLARE TYPE type_employe IS RECORD ( empno EMP.EMPNO%TYPE, ename EMP.ENAME%TYPE, sal EMP.SAL%TYPE ); TYPE type_tabEmployes IS TABLE of type_employe INDEX BY PLS_INTEGER; v_tabEmp type_tabEmployes; i number; n number; BEGIN select empno, ename, sal BULK COLLECT INTO v_tabEmp from emp; select count(*) into n from emp; FOR i IN 1 .. n LOOP dbms_output.put_line(v_tabEmp(i).ename || ', employe n' ||v_tabEmp(i).empno || ' : salaire mensuel = '||v_tabEmp(i).sal) ;
INSIA Cours ORACLE - PL-SQL - page 22/47 - Bertrand LIAUDET

END LOOP; END; /

A noter Les variables de type TABLE peuvent correspondre une table SQL. Mthodes associes aux tableaux de taille variable Prsentation EXISTS(n) : COUNT : FIRST / LAST PRIOR(n) / NEXT(n) DELETE(n) DELETE DELETE(a,b) Principe dutilisation Ce sont des mthodes au sens de la programmation objet. Exemple
DECLARE TYPE type_tabEntier IS TABLE of NUMBER(5,0) INDEX BY BINARY_INTEGER; v_tabEntier type_tabEntier; BEGIN for i in 1..10 loop v_tabEntier(i):=i*100; end loop; if (v_tabEntier.exists(5)) then dbms_output.put_line('t(5) existe'); else dbms_output.put_line('t(5) nexiste pas'); end if; if (v_tabEntier.exists(11)) then dbms_output.put_line('t(11) existe'); else dbms_output.put_line('t(11) nexiste pas'); end if; dbms_output.put_line('first : '||v_tabEntier.first); dbms_output.put_line('last : '||v_tabEntier.last); dbms_output.put_line('count : '||v_tabEntier.count); v_tabEntier(20):=2000; dbms_output.put_line('count : '||v_tabEntier.count); dbms_output.put_line('last 20: '||v_tabEntier.last); dbms_output.put_line('prior(5) : '||v_tabEntier.prior(5)); dbms_output.put_line('prior(20) : '||v_tabEntier.prior(20)); dbms_output.put_line('next(5) : '||v_tabEntier.next(5)); dbms_output.put_line('next(10) : '||v_tabEntier.next(10)); dbms_output.put_line('next(20) : '||v_tabEntier.next(20)); v_tabEntier.delete(3); dbms_output.put_line('count : '||v_tabEntier.count); dbms_output.put_line('next(2) : '||v_tabEntier.next(2)); v_tabEntier.delete; dbms_output.put_line('count : '||v_tabEntier.count); END; / t(5) existe t(11) n'existe pas

Revoie vrai si llment dindice n existe. Renvoie le nombre dlments du tableau. Renvoie le premier ou le dernier lment du tableau. Retourne llment prcdent ou suivant llment dindice n. Supprime llment dindice n. Supprime llment de lindice en cours. Supprime les lments dindice a et b

INSIA Cours ORACLE - PL-SQL - page 23/47 - Bertrand LIAUDET

first : 1 last : 10 count : 10 count : 11 last 20: 20 prior(5) : 4 prior(20) : 10 next(5) : 6 next(10) : 20 next(20) : count : 10 next(2) : 4 count : 0 Procdure PL/SQL termine avec succs.

INSIA Cours ORACLE - PL-SQL - page 24/47 - Bertrand LIAUDET

9.

SQL et PL-SQL

SELECT INTO : rcupration dune valeur ou dun tuple de la BD


DECLARE v_nbEmp integer; v_emp emp%rowtype; BEGIN SELECT count(*) into v_nbEmp from emp; dbms_output.put_line('nb_emp : '||v_nbEmp); SELECT * into v_emp from emp where empno=&numEmp; dbms_output.put_line('Employ n '||v_emp.empno); dbms_output.put_line('Nom : '||v_emp.ename||' - salaire : '||v_emp.sal); END; /

Remarque: une requte qui ne renvoie aucun tuple gnre une erreur. SELECT BULK COLLECT INTO : rcupration dune liste de tuples de dans la BD
DECLARE TYPE type_tabEmp IS TABLE of emp%rowtype INDEX BY PLS_INTEGER; v_tabEmp type_tabEmp; BEGIN select * BULK COLLECT INTO v_tabEmp from emp; for i in 1..v_tabEmp.count loop dbms_output.put_line('Employ n '||v_tabEmp(i).empno|| ', Nom : '||v_tabEmp(i).ename|| ' - salaire : '||v_tabEmp(i).sal); end loop; END; /

INSERT INTO Cest un INSERT INTO classique. On peut faire : VALUES(v_emp) avec v_emp de type %ROWTYPE. UPDATE Cest un UPDATE classique. On peut faire SET ROW = v_emp avec v_emp de type %ROWTYPE. DELETE Cest un DELETE classique.

INSIA Cours ORACLE - PL-SQL - page 25/47 - Bertrand LIAUDET

RETOUR du DML SQL%FOUND Vrai (true) si un tuple a t cr ou modifi ou supprim.


BEGIN Update emp Set sal=sal*1.1 Where empno = &numEmp; If sql%found then dbms_output.put_line('Un employ a t augment'); else dbms_output.put_line('Pas demploy augment'); end if; END; /

SQL%NOTFOUND Vrai (true) si aucun tuple na t cr ou modifi ou supprim. Cest le contraire de SQL%FOUND. SQL%ROWCOUNT Renvoie le nombre de tuples qui ont t crs ou modifies ou supprims. UPDATE... RETURNING et RETURNING BULK COLLECT Prsentation RETURNING permet de renvoyer le ou les tuples qui ont t modifis dans une variable. Exemple de RETURNING
DECLARE V_empno emp.empno%TYPE; BEGIN Update emp Set sal=sal*1.1 Where empno = &numEmp Returning empno into v_empno; If sql%found then dbms_output.put_line('Lemploy n '||v_empno||' a t augment'); else dbms_output.put_line('Pas demploy augment'); end if; END; /

La clause RETURNING se place la fin de linstruction DML. On peut aussi retourner le ROWID. Syntaxe
RETURNING expression1 [,...] [BULK COLLECT] INTO variable1 [,...];

Exemple de RETURNING BULK COLLECT


DECLARE
INSIA Cours ORACLE - PL-SQL - page 26/47 - Bertrand LIAUDET

TYPE type_tabEmp IS TABLE of emp%rowtype INDEX BY BINARY_INTEGER; v_tabEmp type_tabEmp; BEGIN Update emp Set sal=sal*1.1 Where job = '&nomJob' -- Returning * into v_tabEmp; NE MARCHE PAS !! Dommage !! Returning empno, ename, job, mgr, hiredate, sal, comm, deptno BULK COLLECT INTO v_tabEmp; If sql%found then dbms_output.put_line('Les employs suivants ont t augments : '); for i in 1..v_tabEmp.count loop dbms_output.put_line('Employ n '||v_tabEmp(i).empno|| ', Nom : '||v_tabEmp(i).ename|| ' - salaire : '||v_tabEmp(i).sal); end loop; else dbms_output.put_line('Pas demploy augment'); end if; END; /

INSIA Cours ORACLE - PL-SQL - page 27/47 - Bertrand LIAUDET

EXECUTE IMMEDIATE : SQL dynamique Prsentation Le PL-SQL ne permet pas dutiliser directement les commandes du DDL (CREATE TABLE, DROP TABLE, etc.) ni les commandes du DCL (GRANT, REVOKE, etc.). La clause EXECUTE IMMEDIATE permet dutiliser toutes ces commandes. Exemple
DECLARE v_sqlDynamique varchar2(200); v_nomAtt varchar2(20); v_nomTable varchar2(20); BEGIN dbms_output.put_line('Entrez lattribut que vous voulez catgoriser : ') ; v_nomAtt :='&nomAtt'; v_nomTable :='cat_'||v_nomAtt ; dbms_output.put_line('nomAtt : '||v_nomAtt||' - nomTable : '||v_nomTable); v_sqlDynamique:='CREATE TABLE '||v_nomTable||' as select distinct '||v_nomAtt||' from emp'; dbms_output.put_line('Instruction : '||v_sqlDynamique); EXECUTE IMMEDIATE v_sqlDynamique ; END; /

ATTENTION : la chane-instruction de la variable v_sqlDynamique ne doit pas se terminer par un ; . Syntaxe


EXECUTE IMMEDIATE varInstruction [ { INTO {nomVar1 [, ...]} | BULK COLLECT INTO varTable ] [ RETURNING expression1 [,...] [BULK COLLECT] INTO nomVar1[,...] ] [ USING [IN|OUT|IN OUT] nomArgument [, ...] ] ;

Explications INTO et BULK COLLECT INTO permettent la rcuperation dun ou de plusieurs tuples rsulats dun SELECT. RETURNING permet de renvoyer le ou les tuples qui ont t modifis dans une variable. USING permet de mettre des arguments dans linstruction de lEXECUTE IMMEDIATE. Dans la chane varInstruction , les arguments sont prcds par : . Attention, ces arguments ne peuvent tre passs que dans un SELECT ou une instruction du DML. On ne peut pas les utiliser pour une instruction du DDL ou du DCL.

INSIA Cours ORACLE - PL-SQL - page 28/47 - Bertrand LIAUDET

10. Les curseurs Prsentation Un curseur est une variable qui permet de parcourir les lignes dune table relationnelle une par une. Organigramme dun curseur (diagramme dactivits) DECLARE OPEN FETCH
NON

vide ?
OUI

CLOSE

Lalgorithmique des curseurs ressemble celle des fichiers : 1. Dclaration de la variable de type CURSOR 2. Ouverture du curseur : OPEN 3. Boucle pour lire une ligne et passer la suivante : FETCH 4. Condition de sortie de la boucle : on est arriv la fin du tableau. 5. Fermeture du curseur. Dclaration dun curseur Exemple
DECLARE cursor c_emp is select empno, ename, job, sal+nvl(comm, 0) as saltot from emp order by saltot desc; v_emp c_emp%rowtype; BEGIN ... END;

Syntaxe
DECLARE CURSOR c_nomCurseur IS requiteSQL; v_nomVar c_nomCurseur%ROWTYPE; BEGIN ... END;

Deux types de FETCH FETCH INTO : une seule ligne Le FETCH INTO permet de lire une ligne dans une variable de type RECORD. On peut utiliser un ROWTYPE correspondant au type du curseur.
DECLARE cursor c_emp is select empno, ename, job, sal+nvl(comm, 0) as saltot from emp order by saltot desc; v_emp c_emp%rowtype;

INSIA Cours ORACLE - PL-SQL - page 29/47 - Bertrand LIAUDET

BEGIN open c_emp; if c_emp%isopen then dbms_output.put_line('Le curseur c_emp est ouvert'); end if ; loop fetch c_emp into v_emp; exit when c_emp%notfound ; dbms_output.put_line(v_emp.empno||'-'||v_emp.ename ||'-'||v_emp.job||'-'||v_emp.saltot); end loop; dbms_output.put_line('Nb lignes traites : '||c_emp%rowcount); close c_emp; END; /

FETCH BULK COLLECT INTO : toute la table Le FETCH BULK COLLECT INTO permet de lire toute la table dans une variable de type tableau : TABLE.
DECLARE cursor c_emp is select empno, ename, job, sal+nvl(comm, 0) as saltot from emp order by saltot desc; TYPE type_tabEmp IS TABLE of c_emp%rowtype; v_tabEmp type_tabEmp ; BEGIN open c_emp; fetch c_emp bulk collect into v_tabEmp; for I in 1..v_tabemp.count loop dbms_output.put_line(v_tabemp(i).empno|| '-' || v_tabemp(i).ename||'-'||v_tabemp(i).job|| '-'||v_tabemp(i).saltot); end loop; dbms_output.put_line('Nb lignes traites : '||c_emp%rowcount); close c_emp; END; /

Etats dun curseur %FOUND %NOTFOUND %ISOPEN %ROWOUNT Close curseur Quand on a fini dutiliser un curseur, il vaut mieux le fermer : a libre laccs aux donnes pour dautres utilisateurs. Par dfaut, PL-SQL ferme tous les curseurs ouverts la fin de la procdure. Curseur paramtr Curseur avec des paramtres dappel
DECLARE cursor c_emp (v_deptno emp.deptno%type) is select empno, ename, job, sal+nvl(comm, 0) as saltot from emp where deptno=v_deptno order by saltot desc; v_emp c_emp%rowtype;

VRAI si le fetch renvoie une ligne ou un tableau. VRAI si le fetch na rien renvoy. VRAI si le curseur est ouvert. Nombre de lignes renvoyes par le fetch depuis louverture du curseur.

INSIA Cours ORACLE - PL-SQL - page 30/47 - Bertrand LIAUDET

BEGIN open c_emp(&v_deptno); if c_emp%isopen then dbms_output.put_line('Le curseur c_emp est ouvert'); end if ; loop fetch c_emp into v_emp; exit when c_emp%notfound ; dbms_output.put_line(v_emp.empno||'-'||v_emp.ename ||'-'||v_emp.job||'-'||v_emp.saltot); end loop; dbms_output.put_line('Nb lignes traites : '||c_emp%rowcount); close c_emp; END; /

Curseur avec des paramtres dans le select


DECLARE v_deptno emp.deptno%type; -- utilise dans le curseur. -- dclar avant le curseur.

cursor c_emp is select empno, ename, job, sal+nvl(comm, 0) as saltot from emp where deptno=v_deptno order by saltot desc; v_emp c_emp%rowtype; BEGIN -- v_deptno := 20; v_deptno := &v_numDept; open c_emp; if c_emp%isopen then dbms_output.put_line('Le curseur c_emp est ouvert'); end if ; loop fetch c_emp into v_emp; exit when c_emp%notfound ; dbms_output.put_line(v_emp.empno||'-'||v_emp.ename ||'-'||v_emp.job||'-'||v_emp.saltot); end loop; dbms_output.put_line('Nb lignes traites : '||c_emp%rowcount); close c_emp; END; /

Curseur implicite : SQL Quand on fait un UPDATE ou un DELETE, a donne lieu un curseur implicite. Le nom du curseur implicite est : SQL. On peut accder ltat du curseur implicite SQL.
DECLARE BEGIN if sql%isopen = FALSE then dbms_output.put_line('Avant LMD : isopen vaut FALSE'); end if; if sql%found is NULL then dbms_output.put_line('Avant LMD : found vaut NULL'); end if ; if sql%rowcount is NULL then dbms_output.put_line('Avant LMD : rowcount vaut NULL'); end if ; update emp set sal=1.1*sal where deptno=30; if sql%found = TRUE then
INSIA Cours ORACLE - PL-SQL - page 31/47 - Bertrand LIAUDET

dbms_output.put_line('Aprs LMD : found vaut TRUE ' ||sql%rowcount||' enregistrements modifis'); elsif sql%found = FALSE then dbms_output.put_line('Aprs LMD : found vaut FALSE ' ||sql%rowcount||' enregistrements modifis'); elsif sql%found is NULL then dbms_output.put_line('Aprs LMD : found vaut NULL ' ||sql%rowcount||' enregistrements modifis'); end if; if sql%isopen = FALSE then dbms_output.put_line('Aprs LMD : isopen vaut FALSE'); end if ; END; /

A noter ltat du curseur implicite SQL: ISOPEN est toujours faux. FOUND et REOWCOUNT vallent NULL avant le traitement. FOUND vaut TRUE si un tuple au moins a t trait, FALSE sinon. ROWCOUNT renvoie le nombre de tuples traits, 0 si aucun. FOR IN : parcours dune table sans FETCH avec ou sans curseur Sans fetch et avec curseur
DECLARE cursor c_emp is select empno, ename, job, sal+nvl(comm, 0) as saltot from emp order by saltot desc; BEGIN for v_emp in c_emp loop dbms_output.put_line(v_emp.empno||'-'||v_emp.ename ||'-'||v_emp.job||'-'||v_emp.saltot); end loop; END; /

Sans fetch et sans curseur


DECLARE i number; BEGIN i:=0; for v_emp in (select empno, ename, job, sal+nvl(comm, 0) as saltot from emp order by saltot desc) loop dbms_output.put_line(v_emp.empno||'-'||v_emp.ename ||'-'||v_emp.job||'-'||v_emp.saltot); i:=i+1; end loop; dbms_output.put_line('Nb lignes traites : '|| i); END; /

INSIA Cours ORACLE - PL-SQL - page 32/47 - Bertrand LIAUDET

CURSEUR avec verrouillage : FOR UPDATE et WHERE CURRENT OF Syntaxe du FOR UPDATE
CURSOR nomCurseur [(nom_arg TYPE [:= valDef] [, ...] ] IS requete FOR UPDATE [OF nomAttribut [, ...] ] [{NOWAIT | WAIT nbSecondes}] ;

La clause FOR UPDATE peut porter sur tous les attributs du curseur (situation par dfaut) ou seulement sur ceux qui sont prciss : OF. La clause NOWAIT nattend pas pour verrouiller les attributs. Sils sont dj verrouills, la dclaration de curseur renverra une erreur. La clause WAIT nbSecondes accorde nbSeconde pour ressayer de verrouiller les attributs avant de dclencher une erreur. Commit et Rollback Pour librer les attributs verrouills il faut utiliser un COMMIT ou un ROLLBACK. Aprs un COMMIT ou un ROLLBACK, on ne peut plus faire de fetch. Where current of Avec un curseur for update, on peut utiliser un where current of dans les update et les delete pour travailler sur le tuple courant du curseur.
DECLARE CURSOR c_nomCurseur ... v_numCurseur c_nomCurseur; BEGIN ... for v_nomCurseur in c_nomCurseur loop ... update ... set where CURRENT OF c_nomCurseur; ... end loop; ... END;

INSIA Cours ORACLE - PL-SQL - page 33/47 - Bertrand LIAUDET

Variable curseur : REF CURSOR et OPEN FOR REF CURSOR avec RETURN et OPENFOR : dclaration et ouverture
DECLARE TYPE t_curseur_emp IS REF CURSOR RETURN emp%ROWTYPE; c_emp t_curseur_emp; BEGIN OPEN c_emp FOR SELECT * FROM emp WHERE deptno=30; ... END;

Explications On dclare dabord un TYPE REF CURSOR en prcisant le type de tuple retourn dans le RETURN. Ensuite on dclare une variable du type REF CURSOR prdfini. Enfin, louverture, OPEN, on ajoute un FOR suivi dune requte. Ainsi, on pourra utiliser le mme curseur pour des requtes diffrentes, condition seulement que les attributs projets soient les mmes. Il faudra fermer le curseur (CLOSE), pour ensuite pouvoir le rouvrir : OPEN FOR.

REF CURSOR sans RETURN


DECLARE TYPE t_curseur_emp IS REF CURSOR; c_emp t_curseur_emp; BEGIN OPEN c_emp FOR SELECT emmno, ename, deptno FROM emp WHERE deptno=30; ... END;

Explications On dclare dabord un TYPE REF CURSOR sans prciser le type de tuple retourn. Ensuite on dclare une variable du type REF CURSOR prdfini. Enfin, louverture, OPEN, on ajoute un FOR suivi dune requte. Ainsi, on pourra utiliser le mme curseur pour des requtes diffrentes quels que soient les attributs projets. Il faudra fermer le curseur (CLOSE), pour ensuite pouvoir le rouvrir : OPEN FOR.

REF CURSOR avec SELECT paramtr


DECLARE TYPE t_curseur_emp IS REF CURSOR; c_emp t_curseur_emp; v_SQL varchar2(250) :='select * from emp where deptno=:vp_deptno'; v_deptno emp.deptno%type; v_emp emp%rowtype; BEGIN v_deptno := 20; OPEN c_emp FOR v_SQL USING v_deptno; loop fetch c_emp into v_emp; exit when c_emp%notfound ; dbms_output.put_line(v_emp.empno||'-'||v_emp.ename ||'-'||v_emp.job||'-'||v_emp.deptno);

INSIA Cours ORACLE - PL-SQL - page 34/47 - Bertrand LIAUDET

end loop; dbms_output.put_line('Nb lignes traites : '||c_emp%rowcount); close c_emp; END; /

Explications On dclare dabord un TYPE REF CURSOR sans prciser le type de tuple retourn. Ensuite on dclare une variable du type REF CURSOR prdfini. On dclare une chane de caractres qui contient un SELECT avec un paramtre :nomParam . On ouvre le curseur, OPEN, avec un FOR de la chane de caractres et un USING proposant une valeur pour le paramtre de la chane.

Conclusion Lintrt des variables curseur cest quelles pourront tre passes en paramtre des procdures ou des fonctions.

INSIA Cours ORACLE - PL-SQL - page 35/47 - Bertrand LIAUDET

11. Les exceptions Prsentation Quand une erreur systme ou applicative se produit, une exception est gnre. Les traitements en cours dans la section dexcution (section du BEGIN) sont stopps. Les traitements de la section dexception (section du EXCEPTION) commence. Les 4 types dexception

Type dexception Systme

Nomme Exception systme nomme Exception utilisateur nomme

Anonyme (numrote) Exception systme anonyme Exception utilisateur anonyme

Utilisateur

Exceptions systme nommes et exceptions systme anonymes (numrotes) Les exceptions prdfinies par ORACLE ne sont pas toutes nommes (il y en a trop). Par contre, elles ont toutes un numro. Les exceptions prdfinies non nommes sont dites anonymes. Exceptions prdfinies nommes DUP_VAL_ON_INDEX : violation de lunicit lors dun insert ou dun update (-1). NO_DATA_FOUND : un select into ne ramne aucune ligne (-1403 ; 100). TOO_MANY_ROWS : un select into ramne plusieurs lignes (-1422). VALUE_ERROR : erreur de onversion de type (-6502). ZERO_DIVIDE : division par 0 (-1476). Etc. Exceptions prdfinies anonymes
BEGIN For i in 2299 .. 2290 loop dbms_output.put_line(sqlerrm(i)); end loop ; END ; / ORA-02299: impossible de valider (.) - cls en double trouves ORA-02298: impossible de valider (.) - cls parents introuvables ORA-02297: impossible de dsactiver la contrainte (.) - des dpendances existent ORA-02296: impossible d'activer la contrainte (.) - valeurs null trouves ORA-02295: plusieurs clauses ENABLE/DISABLE trouves pour la contrainte

INSIA Cours ORACLE - PL-SQL - page 36/47 - Bertrand LIAUDET

ORA-02294: impossible d'activer (.) - contrainte modifie pendant la validation ORA-02293: impossible de valider (.) - violation d'une contrainte de contrle ORA-02292: violation de contrainte (.) d'intgrit - enregistrement fils existant ORA-02291: violation de contrainte d'intgrit (.) - cl parent introuvable ORA-02290: violation de contraintes (.) de vrification Procdure PL/SQL termine avec succs.

WHEN OTHERS : exception systme anonyme Principe En dclarant une zone dexception avec le cas OTHERS , on prend en compte toutes les erreurs systme. Exemple
DECLARE v_emp emp%rowtype; v_empno emp.empno%type; BEGIN v_empno:=&numEmp; SELECT * into v_emp from emp where empno=v_empno; dbms_output.put_line('Employ n '||v_emp.empno); dbms_output.put_line('Nom : '||v_emp.ename||' - salaire : '||v_emp.sal); EXCEPTION WHEN OTHERS THEN dbms_output.put_line('SQLCODE = '||SQLCODE); dbms_output.put_line('SQLERRM = '||SQLERRM); END; / Entrez une valeur pour numemp : 2000 ancien 5 : v_empno:=&numEmp; nouveau 5 : v_empno:=2000; SQLCODE = 100 SQLERRM = ORA-01403: aucune donne trouve Procdure PL/SQL termine avec succs.

Prcisions La fonction SQLCODE renvoie le code de lerreur. La fonction SQLERRM renvoie le message de lerreur. WHEN exceptionPrdfinie : exception systme nomme Principe En utilisant les exceptions prdfinies par le systme, on va pouvoir prciser les messages derreur. Exemple
DECLARE v_emp emp%rowtype; v_empno emp.empno%type; BEGIN v_empno:=&numEmp; SELECT * into v_emp from emp where empno=v_empno;

INSIA Cours ORACLE - PL-SQL - page 37/47 - Bertrand LIAUDET

dbms_output.put_line('Employ n '||v_emp.empno); dbms_output.put_line('Nom : '||v_emp.ename||' - salaire : '||v_emp.sal); EXCEPTION WHEN NO_DATA_FOUND THEN dbms_output.put_line('Lemploy n '||v_empno||' nexiste pas'); dbms_output.put_line('SQLCODE = '||SQLCODE); dbms_output.put_line('SQLERRM = '||SQLERRM); END; / Entrez une valeur pour numemp : 1000 ancien 5 : v_empno:=&numEmp; nouveau 5 : v_empno:=1000; L'employ n 1000 n'existe pas SQLCODE = 100 SQLERRM = ORA-01403: aucune donne trouve Procdure PL/SQL termine avec succs.

EXCEPTION_INIT : exception systme anonyme nomme par lutilisateur On peut associer un nom une exception prdfinie anonyme. Lopration se fait en trois temps : 2 dclarations dans le bloc DECLARE et un WHEN dans le bloc EXCEPTION.
DECLARE NOM_EXCEPTION EXCEPTION; PRAGMA EXCEPTION_INIT(NOM_EXCEPTION, CODE_ERREUR); ... BEGIN ... EXCEPTION WHEN NOM_EXCEPTION THEN Instructions ; ... END;

NOM_EXCEPTION est un nom choisi par le programmeur. CODE_ERREUR est le numro derreur prdfinie par ORACLE. Par exemple : -2291 correspond un problme dintgrit rfrentielle. A noter que les CODE_ERREUR sont le plus souvent ngatifs. RAISE_APPLICATION_ERROR : exception utilisateur anonyme Principe Lutilisateur peut grer ses propres erreurs, cest--dire des contraintes dintgrit spcifiques non prises en compte par le SQL. En utilisant la fonction RAISE_AAPLICATION_ERREUR, lutilisateur renvoie le programme vers la zone OTHERS en cas derreur. Lexception nest pas nomme. Exemple On considre quun nouveau salaire doit toujours tre suprieur lancienne valeur.
DECLARE v_emp emp%rowtype; v_empno emp.empno%type; v_sal emp.sal%type; BEGIN v_empno:=&numEmp; v_sal:=&salaire; SELECT * into v_emp from emp where empno=v_empno; dbms_output.put_line('Employ n '||v_emp.empno);
INSIA Cours ORACLE - PL-SQL - page 38/47 - Bertrand LIAUDET

dbms_output.put_line('Nom : '||v_emp.ename||' - salaire : '||v_emp.sal); if v_sal < v_emp.sal then dbms_output.put_line('Le nouveau salaire : '||v_sal|| ' ne peut pas tre infrieur lancien : '||v_emp.sal) ; RAISE_APPLICATION_ERROR(-20000, 'Problme dintgrit des donnes') ; end if; EXCEPTION WHEN NO_DATA_FOUND THEN dbms_output.put_line('Lemploy n '||v_empno||' nexiste pas'); dbms_output.put_line('SQLCODE = '||SQLCODE); dbms_output.put_line('SQLERRM = '||SQLERRM); WHEN OTHERS THEN dbms_output.put_line('SQLCODE = '||SQLCODE); dbms_output.put_line('SQLERRM = '||SQLERRM); END; / Entrez une valeur pour numemp : 7839 ancien 6 : v_empno:=&numEmp; nouveau 6 : v_empno:=7839; Entrez une valeur pour salaire : 4000 ancien 7 : v_sal:=&salaire; nouveau 7 : v_sal:=4000; Employ n 7839 Nom : KING - salaire : 5000 Le nouveau salaire : 4000 ne peut pas tre infrieur l'ancien : 5000 SQLCODE = -20000 SQLERRM = ORA-20000: Problme d'intgrit des donnes Procdure PL/SQL termine avec succs.

Prcisions La fonction RAISE_APPLICATION_ERROR a deux paramtres : Un numro derreur, quon choisit entre 20 000 et 20 999 pour viter les conflits avec les erreurs ORACLE.

Un message derreur. Ces deux paramtres sont ensuite utiliss par les fonctions SQLCODE et SQLERRM dans la zone dexception WHEN OTHERS. RAISE nomException : exception utilisateur nomme Principe Lutilisateur peut grer ses propres erreurs, cest--dire des contraintes dintgrit spcifiques non prises en compte par le SQL. En dclarant une exception et en utilisant la fonction RAISE nomException, lutilisateur renvoie le programme vers la zone nomException en cas derreur. Lexception est nomme. Exemple On considre quun nouveau salaire doit toujours tre suprieur lancienne valeur.
DECLARE UPDATE_SAL_EMP EXCEPTION; v_emp emp%rowtype; v_empno emp.empno%type; v_sal emp.sal%type; BEGIN v_empno:=&numEmp; v_sal:=&salaire;

INSIA Cours ORACLE - PL-SQL - page 39/47 - Bertrand LIAUDET

SELECT * into v_emp from emp where empno=v_empno; dbms_output.put_line('Employ n '||v_emp.empno); dbms_output.put_line('Nom : '||v_emp.ename||' - salaire : '||v_emp.sal); if v_sal < v_emp.sal then dbms_output.put_line('Le nouveau salaire : '||v_sal|| ' ne peut pas tre infrieur lancien : '||v_emp.sal) ; RAISE UPDATE_SAL_EMP ; end if; EXCEPTION WHEN UPDATE_SAL_EMP THEN dbms_output.put_line('Problme de mise jour des donnes'); WHEN NO_DATA_FOUND THEN dbms_output.put_line('Lemploy n '||v_empno||' nexiste pas'); dbms_output.put_line('SQLCODE = '||SQLCODE); dbms_output.put_line('SQLERRM = '||SQLERRM); END; / Entrez une valeur pour numemp : 7839 ancien 7 : v_empno:=&numEmp; nouveau 7 : v_empno:=7839; Entrez une valeur pour salaire : 4000 ancien 8 : v_sal:=&salaire; nouveau 8 : v_sal:=4000; Employ n 7839 Nom : KING - salaire : 5000 Le nouveau salaire : 4000 ne peut pas tre infrieur l'ancien : 5000 Problme de mise jour des donnes Procdure PL/SQL termine avec succs.

Propagation des exceptions : exception et blocs imbriqus. 1) Dans un bloc, en cas derreur, on passe la zone dexception du bloc. 2) Si lexception est prise en compte (par son nom ou par le OTHERS), les instructions correspondantes sont excutes et on quitte le bloc : on revient donc dans le bloc appelant. 3) Si lexception nest pas prise en compte, on passe la zone dexception du bloc appelant si il existe et on revient ltape 2. Sil ny a pas de bloc appelant, le programme va sarrter (planter !) sur cette erreur.
DECLARE MON_EXCEPTION EXCEPTION; BEGIN BEGIN BEGIN RAISE MON_EXCEPTION; EXCEPTION WHEN NO_DATA_FOUND THEN dbms_output.put_line('No date found bloc 3'); END; dbms_output.put_line('bloc 2'); EXCEPTION WHEN NO_DATA_FOUND THEN dbms_output.put_line('No date found bloc 2'); WHEN MON_EXCEPTION THEN dbms_output.put_line('Mon exception bloc 2'); WHEN OTHERS THEN dbms_output.put_line('Others bloc 2'); END; dbms_output.put_line('bloc 1'); EXCEPTION

INSIA Cours ORACLE - PL-SQL - page 40/47 - Bertrand LIAUDET

WHEN NO_DATA_FOUND THEN dbms_output.put_line('No date found bloc 1'); WHEN MON_EXCEPTION THEN dbms_output.put_line('Mon exception bloc 1'); WHEN OTHERS THEN dbms_output.put_line('Others bloc 1'); END; / Mon exception - bloc 2 bloc 1 Procdure PL/SQL termine avec succs.

Si on supprime les WHEN MON_EXCEPTION, cest le WHEN OTHERS qui sera pris en compte.
/ Others - bloc 2 bloc 1 Procdure PL/SQL termine avec succs.

INSIA Cours ORACLE - PL-SQL - page 41/47 - Bertrand LIAUDET

12. Les triggers (dclencheurs) Prsentation Principe Un trigger est un bloc qui est dclench automatiquement chaque occurrence dun vnement dclenchant. Il nont jamais darguments. Ils ne peuvent pas tre dclenchs manuellement . Usages Les triggers servent : Maintenir des contraintes dintgrit non gres par le DDL. Consigner les changements apports une table et mettre jour des statistiques. Mettre jour des donnes calcules.

3 types de triggers Les triggers DML Ils sont dclenchs avant (BEFORE) ou aprs (AFTER) une instruction du DML : INSERT, UPDATE ou DELETE. Ce sont les plus utiliss. Les triggers INSTEAD OF Lexcution du trigger remplace celle de linstruction dclenchante. Pas abord dans ce poly. Les triggers Systme Ils sont dclenchs louverture ou la fermeture dune BD, ou lors dune opration du DDL (create table, drop table, etc.). Pas abord dans ce poly. Caractristiques des triggers DML syntaxe
CREATE [OR REPLACE] TRIGGER [nomSchema.]nomTrigger {BEFORE | AFTER | INSTEAD OF} instructionDeclencheur [REFERENCING reference] [FOR EACH ROW] [WHEN condition] [DECLARE ...] BEGIN ... [EXCEPTION ...] END [nomTrigger];

3 caractristiques principales Linstruction dclenchante : INSERT et/ou UPDATE et/ou DELETE avec ses prdicats associs : INSERTING, UPDATING, DELETING.

INSIA Cours ORACLE - PL-SQL - page 42/47 - Bertrand LIAUDET

Le moment de lexcution : AFTER ou BEFORE. Le niveau de lexcution : niveau tuple (FOR EACH ROW) ou niveau table. Le niveau tuple permet laccs au tuple en cours de modification avant modification : OLD, et au tuple en cours de modification aprs modification : NEW.

2 caractristiques secondaires lies au niveau de lexcution La condition de dclenchement : WHEN Renommer NEW et OLD : REFERENCING

Instruction dclenchante : INSERT, UPDATE, DELETE syntaxe


{BEFORE | AFTER } {INSERT | UPDATE | DELETE } [OR {INSERT | UPDATE | DELETE }] [OR {INSERT | UPDATE | DELETE }] [OF nomAttribut [,nomAttribut ...] ] ON nomTable [FOR EACH ROW]

Le trigger peut tre dclench sur un INSERT, un UPDATE, un DELETE ou nimporte quelle combinaison des trois. Il concerne ncessairement une table et ventuellement un ou plusieurs attributs de cette table. Prdicats associs : INSERTING, UPDATING, DELETING Dans le corps du trigger, on peut les prdicats INSERTING, UPDATING, DELETING. Ce sont des boolens. Ils permettent de spcifier une partie du corps du trigger en fonction de la nature de linstruction dclenchante : INSERT, UPDATE ou DELETE. Moment dexcution : BEFORE et AFTER Un trigger BEFORE est un trigger qui se dclenche avant laction dclenchante du DML (INSERT, UPDATE ou DELETE) et peut empcher son excution. Il sert vrifier lintgrit de la BD. Si lintgrit des donnes est altre, le trigger renverra un message derreur et laction dclenchante du DML ne sera pas effectue. Un trigger AFTER est un trigger qui se dclenche aprs laction dclenchante. Il ne peut pas empcher son excution. Il sert mettre jour la BD du fait de la modification quon vient dy apporter : attributs calculs, statistiques, audit. Niveau dexcution : niveau tuple FOR EACH ROW et niveau table La notion de niveau dexcution concerne les triggers UPDATE et DELETE. En effet, lUPDATE et le DELETE peuvent concerner plusieurs tuples de la table. Par contre, un INSERT ne concerne quun seul tuple. Lors dun UPDATE ou dun DELETE, un trigger peut tre dclench plusieurs fois, chaque modification de tuple (FOR EACH ROW), ou une seule fois, aprs toutes les modifications de lUPDATE ou du DELETE. On distingue donc entre un NIVEAU TUPLE et un NIVEAU TABLE.

INSIA Cours ORACLE - PL-SQL - page 43/47 - Bertrand LIAUDET

Trigger niveau tuple : :NEW et :OLD Pour les triggers de niveau tuple, le tuple avant modification est nomm :OLD , le tuple aprs modification est nomm :NEW . :OLD nest pas dfini pour un insert. :NEW nest pas dfini pour un delete. On ne peut pas modifier le NEW dans un trigger AFTER. Trigger niveau table NEW et OLD ne peuvent pas tre utiliss avec un trigger de niveau table. WHEN : conditionner le dclenchement Le WHEN permet de conditionner le dclenchement en fonction des valeurs de NEW et de OLD.
WHEN (NEW.att1 > NEW.att2) WHEN (OLD.att in (1, 2, 3))

A noter quil ny a pas de : avant le OLD et le NEW. REFERENCING : renommer NEW et OLD Le REFERENCING permet de renommer les pseudo-tuples NEW et OLD :
{BEFORE | AFTER | INSTEAD OF} instructionDeclencheur REFERENCING NEW as nouveauNew OlD as nouveauOld [FOR EACH ROW] [WHEN condition]

A noter quil ny a pas de : avant le OLD et le NEW. Algorithme ORACLE de lexcution dune instruction DML 1 : Excuter les triggers BEFORE de niveau TABLE ; 2 : Pour chaque tuple modifi par linstruction 2.1 : Excuter les triggers BEFORE de niveau TUPLE ; 2.2 : Excuter linstruction 2.3 : Excuter les triggers AFTER de niveau TUPLE ; Fin pour 3 : Excuter les trigger AFTER de niveau TABLE ; Exemples de TRIGGER TRIGGER BEFORE Vrification de contraintes dintgrit
Create or replace trigger tbiu_emp -- tbiu: trigger before insert or update on emp before insert or update of sal on emp for each row begin

INSIA Cours ORACLE - PL-SQL - page 44/47 - Bertrand LIAUDET

dbms_output.put_line('entre dans tbiu_emp : '||:old.sal||' '||:new.sal); if :new.sal < 0 then raise_application_error(-20000,'Erreur: salaire ngatif'); elsif :new.sal < :old.sal then raise_application_error(-20000,'Erreur: salaire rduit'); end if ; end ; /

Mise jour dattribut calcul dans la table


alter table emp add saltot number(7,2); update emp set saltot=sal+nvl(comm,0); Create or replace trigger tbiu_emp before insert or update on emp for each row begin :new.saltot:=:new.sal+nvl(:new.comm,0); end ; /

Remarques On ne peut pas modifier un NEW dans un trigger after. On ne peut pas utiliser NEW et OLD dans un trigger de niveau table. TRIGGER AFTER Mise jour dattribut calcul dans la table
Alter table livres add dispo number(1,0) check (dispo in (0,1)); Update livres set dispo=1; Update livres set dispo=0 where NL in (select NL from emprunter where dateret is null); Create or replace trigger tau_emprunter after update on emprunter for each row begin dbms_output.put_line('entre dans tai_emprunter'); if :new.dateret is not null then update livres set dispo=1 where nl = :new.nl; dbms_output.put_line('Le livre n'|| :new.nl||' est de nouveau disponible') ; end if; end; / update emprunter set dateret=current_date where dateret is null and nl=29;

Dans la BD biblio, on ajoute le champ dispo dans la table LIVRES : cest un champ calcul qui dit si le livre est disponible ou pas. On le met jour. Ensuite on cre un trigger pour que lattribut calcul soit mis jour automatiquement. A noter quon pourrait remplacer lafter par un before : on obtiendrait le mme rsultat. Les transactions autonomes Les instructions dun trigger peuvent toujours tre annules par un ROLLBACK. Si on veut les valider, il suffit de faire un COMMIT dans le trigger. Mais ce COMMIT sappliquera toutes les instructions actuellement non valides.

INSIA Cours ORACLE - PL-SQL - page 45/47 - Bertrand LIAUDET

Pour limiter leffet dun COMMIT dans un trigger aux instructions du trigger, on ajoute dans la dclaration des variables un PRAGMA AUTONOMOUS_TRANSACTION.
CREATE [OR REPLACE] TRIGGER ... DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN ... COMMIT; [EXCEPTION ...] END [nomTrigger];

Remarque: ce principe vaut pour tous les blocs ORACLE (Procdures, fonctions, etc.).

INSIA Cours ORACLE - PL-SQL - page 46/47 - Bertrand LIAUDET

13. Gestion des triggers Lister les triggers La vue USER_TRIGGERS permet de lister les triggers
Select trigger_name from user_triggers ;

La vue USER_SOURCE permet de lister les triggers, les procdures et les fonctions en distinguant entre les trois :
Select distinct name, type from user_source;

Voir le code des triggers La vue USER_SOURCE contient chaque ligne du code des triggers.
Set linesize 100 Col text format A80 Select line, text from user_source where name = TBIU_EMP;

Suppression dun trigger


DROP TRIGGER nomTrigger ;

Cette suppression met jour les vues USER_SOURCE et USER_TRIGGERS. Dbogage Outils systme
Show errors ;

Show errors utilise la vue USER_ERRORS.


Set linesize 100 Col object_name format A20 Col object_type format A20 select object_name, object_type, status from user_objects where object_name=TBIU_EMP;

Lattribut STATUS de la vue USER_OBJECTS prcise ltat du trigger. Outils classique On peut afficher des commentaires dans un trigger via un dbms_output.put_line. Particulirement, pour tracer lentre dans un trigger. Nommer des triggers On ne peut avoir plusieurs triggers BEFORE par action de DML pour une table donne. Toutefois, comme lordre dexcution des ces triggers nest pas garanti, il faut mieux navoir quun trigger BEFORE par action du DML. Idem pour les triggers AFTER.

INSIA Cours ORACLE - PL-SQL - page 47/47 - Bertrand LIAUDET