Introduction à ORACLE PL-SQL
Introduction à ORACLE PL-SQL
Bertrand LIAUDET
SOMMAIRE
SOMMAIRE 1
PL-SQL – ORACLE 5
1. Programme et script 5
Structure d’un programme : le bloc 5
Programme en ligne 5
Script 6
Commentaires 7
Affichage : dbms_output.put_line 8
Instructions SQL 8
Blocs imbriqués 9
2. Variables et types scalaires 10
Variable globales - DEF - Variables de substitution 10
Variable globale – bind variable - VAR 12
Variable locale : DECLARE : zone des variables et des types 13
Nommer les variables 13
%TYPE : Utiliser un type provenant d’un attribut d’une table ou d’une variable 14
SUBTYPE : création d’un type avec restriction du domaine de valeur 14
TOUS LES TYPES 15
3. Procédures 17
Bloc procedure 17
Création d’une procedure 17
Appel d’une procédure dans SQL*PLUS : call ou execute ou exec 17
Appel d’une procédure dans une procédure ou une fonction 18
Variable locale : IS ou AS : Zone des variables des procédures et des fonctions 18
Paramètres en entrée d’une procédure 18
Structure d’une procédure 19
RETURN 19
4. Fonctions 20
Bloc function 20
Création d’une fonction 20
1. Programme et script
[ DECLARE
…]
BEGIN
…
[ EXCEPTION
…]
END ;
Programme en ligne
code
begin
dbms_output.put_line('Bonjour');
end;
/
Commande « / »
Le « / » enregistre le bloc et demande l’exécution du bloc en cours.
Si on rappelle le /, ça exécute le bloc en cours.
Le « / » rappelle toujours la dernière commande passée.
Serveroutput à ON
L’exécution est correcte, mais rien ne s’affiche !
Il faut passer le paramètre « serveroutput » à ON.
SQL Developper
Run
Le RUN affiche le bloc en plus de l’exécuter.
SQL> run
1 begin
2 dbms_output.put_line('Bonjour');
3 end;
4 /
Bonjour
clear buffer
On peut vider le buffer :
SQL> clear buffer
buffer effacé
SQL> /
SP2-0103: Rien à exécuter dans la mémoire tampon SQL.
Script
Les programmes peuvent être saisis directement sous SQLPLUS ou enregistrés dans des
scripts.
-- test.sql
-- script qui affiche bonjour
SQL>
Commentaires
/* script de définition d’une procédure
procédure « bonjour » : affiche bonjour,
usage des commentaires en style C
*/
Version 1
begin
dbms_output.put_line('Bonjour '||user||'. Nous sommes le
'||to_char(sysdate, 'dd month yyyy'));
end;
/
Instructions SQL
Pas de SELECT classique en PL-SQL
Pas de DDL en PL-SQL : CREATE TABLE, DROP TABLE, ALTER TABLE
begin
select * from emp;
end;
/
begin
update emp set comm = comm + 0.1*comm
where job=’SALESMAN’;
end;
/
Blocs imbriqués
On peut déclarer des blocs d’instructions à tout moment dans un bloc.
Quand on déclare un bloc d’instruction, on peut y associer de nouvelles déclarations de
variables.
Principes : &&
Dans SQL*PLUS, Il existe des variables de niveau session.
On peut les créer avec la commande DEF.
On peut les utiliser dans un SELECT avec un &&.
La variable de substitution n’est visible qu’au niveau de SQL*PLUS : elle ne peut pas être
utilisée par du code serveur (code serveur PHP ou JAVA par exemple). Autrement dit, elle
n’est pas enregistrée dans la BD.
La variable EMPNO est définie après la requête : c’est une variable globale.
Une variable définie avec un && garde sa valeur en dehors de la requête de définition.
SQL> DEF EMPNO
Autres exemples :
Ø Chaine de caractères
SELECT * FROM emp WHERE ename = &ename;
Ø remarques
• La variable est accessible pendant toute la session.
• C’est un CHAR (chaîne de caractères). Toutefois, il pourra s’exploiter comme un NUM.
Ø exemples
DEF[INE] maVar = 30
Ou
DEF[INE] maVar = DALLAS
A noter qu’on peut écrire indifféremment sans guillemets, avec guillemets ou avec apostrophes.
Principes
On peut définir des variables qui pourront circuler dans les blocs indépendamment de
SQL*PLUS.
La variable de liaison (link) est visible au niveau de la BD : elle peut être utilisée par du code
serveur (code serveur PHP ou JAVA par exemple).
Toutefois, on peut aussi utiliser ces variables et les déclarer dans SQL*PLUS.
On peut les créer avec la commande VAR dans SQL*PLUS.
Exemple 1
Exemple 2
Exemple
DECLARE
f FLOAT;
v_deptno EMP.DEPTNO%TYPE;
BEGIN
dbms_output.put_line('Ma constante : '|| maConstante);
dbms_output.put_line('Number saisi : '|| x);
n:=&chiffre;
dbms_output.put_line('Chiffre saisi : '|| n);
f:=&float;
dbms_output.put_line('Flottant saisi : '|| f);
END;
/
Présentation
Partout où on utilise un nom de type (nomType), on peut utiliser un type provenant d’un
attribut d’une table de la BD.
Exemple
Traité dans le premier exemple.
Syntaxe
NomVar [CONSTANT] {nomTable.nomAttribut | nomVariable}%TYPE [NOT
NULL] [ {DEFAULT | :=} VALEUR ];
Présentation
On peut créer des types qui restreignent le domaine des valeurs autorisées par le type original.
On peut aussi créer des types qui renomment des types existants.
Exemple
Traité dans le premier exemple.
Syntaxe
SUBTYPE nomSousType IS nomType [(contraintes)][NOT NULL];
Les caractères
CHAR( n ) Chaîne fixe de n caractères. n octets. 2000 caractères max
NCHAR( n ) Chaîne fixe de n caractères. Format Unicode n octets. 2000 caractères max
VARCHAR2( n ) Chaîne variable de n caractères. Taille variable. 4000 caractères max
NVARCHAR2( n ) Chaîne variable de n caractères. Format Unicode Taille variable. 4000 caractères max
CLOB Flot de caractères Jusqu’à 4 Giga
NCLOB Flot de caractères. Format Unicode Jusqu’à 4 Giga
LONG Flot de caractères Jusqu’à 2 Giga. Obsolète
Unicode est une méthode de codage universelle utilisée par XML, Java, Javascript, LDAP et
WML.
Ø Sous-types
NOM Type d’origine Caractéristiques
CHARACTER CHAR Identique à CHAR
Ø Sous-types
NOM Type d’origine Caractéristiques
INTEGER NUMBER (38, 0) Taille variable. 4000 caractères max
PLS_INTEGER NUMBER Entiers signées. Moins coûteux qu’un NUMBER. Plus
performant pour les opérations mathématiques due
BINARY_INTEGER. Les valeurs réelles sont
arrondies à l’entier le plus proche.
BINARY_INTEGER NUMBER Entiers signées. Moins coûteux qu’un NUMBER.
NATURAL, POSITIVE BINARY_INTEGER Entiers positifs.
NATURALN, POSITIVEN BINARY_INTEGER Entiers positifs et non nul.
SIGNTYPE BINARY_INTEGER -1, 0 ou 1
DEC, DECIMAL, NUMERIC NUMBER Décimaux, precision de 38 chiffres
DOUBLE PRECISON, NUMBER Flottants
FLOAT, REAL
INTEGER, INT, SMALLINT NUMBER Entiers sur 38 chiffres.
Declare a Boolean:=&verite;
Begin
If a then
dbms_output.put_line('vrai');
elsif not(a) then
dbms_output.put_line('faux');
else
dbms_output.put_line('null');
end if;
end;
/
Bloc procedure
On peut créer une procédure dans la zone DECLARE de la création d’un bloc.
La procédure peut être appelée dans la zone BEGIN – END par une simple référence à son
nom, avec des parenthèses.
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;
/
Procédure créée.
SQL>
Appel terminé.
call affichage1() ;
BEGIN
SELECT count(*) INTO v_nbemp
FROM emp WHERE deptno=v_deptno;
dbms_output.put_line('Le département '||v_deptno||
' contient '||v_nbemp||' employe(s)') ;
dbms_output.put_line('.');
FOR tuple IN (
SELECT empno, ename, sal, deptno FROM emp
where deptno = v_deptno
)
LOOP
dbms_output.put_line(
'. Employé n°' || tuple.EMPNO||
' : ' || tuple.ENAME||', salaire = '||tuple.SAL);
END LOOP;
END;
/
`
call affempdept(10);
A noter le SELECT … INTO qui permet de récupérer un retour de SELECT dans une
variable.
Et le FOR tuple IN (SELECT ..) LOOP … END LOOP, qui permet de boucler sur chaque
tuple d’un SELECT.
RETURN
L’instruction RETURN entre le BEGIN et le END permet de quitter la procédure.
Bloc function
On peut créer une procédure dans la zone DECLARE de la création d’un bloc.
La fonction peut être appelée dans la zone BEGIN – END par une simple référence à son nom,
possiblement sans parenthèses s’il n’y a pas de paramètres.
BEGIN
dbms_output.put_line('Moyenne des salaires : '||moySal);
END;
/
Fonction créée.
Dans un select
SELECT empno, ename, sal, moysal FROM emp
WHERE sal > moysal order by sal;
Dans un bloc
begin
dbms_output.put_line('Moyenne des salaires : '||moySal);
end;
/
FOR tuple IN (
select empno, ename, sal, deptno from emp
where deptno = v_deptno and sal > v_moysal
)
LOOP
dbms_output.put_line('. - Employé n°' || tuple.EMPNO||
' : ' || tuple.ENAME||', salaire = ' || tuple.SAL);
END LOOP;
END;
/
Procédure créée.
CALL affempdept2(10);
Appel terminé.
SQL>
Syntaxe
CREATE [OR REPLACE] {PROCEDURE | FUNCTION} nomProcOuFonc [(
nomVar [ {IN | OUT [NOCOPY] | IN OUT [NOCOPY] } ]
type [ { := | DEFAULT } valeurParDefaut ]
[, ...]
)]
[ RETURN type]
{IS | AS}
IN : paramètre en entrée
Un argument IN est un argument dont la valeur d’entrée est utilisée par le bloc et n’est pas
modifiée par le bloc (procédure ou fonction).
Il se comporte comme une constante dans le bloc.
NOCOPY
De façon paradoxale, par défaut, les arguments IN sont passés par référence, les arguments
OUT et IN OUT sont passés par valeur. Le but est de pouvoir annuler les modifications : en
passant les paramètres OUT et INOUT par valeurs, on fera la modification réelle à la fin de la
procédure et on peut ainsi annuler les modifications faites dans la procédure (ROLLBACK).
L’argument NOCOPY s’applique donc uniquement aux arguments OUT ou IN OUT pour
qu’ils soient passés par référence.
L’avantage est que les traitements sont plus rapide.
Le défaut est que quand la modification est faite, on ne peut plus revenir en arrière.
var x number;
var nbsol number;
-- equa1 : 2x-4=0
call equa1(2, -4, :x, :nbsol);
print :x;
print :nbsol;
La vue USER_SOURCE permet de lister les procédures et les fonctions en distinguant entre les
deux :
Select distinct name, type from user_source;
Débogage
SQL> Show errors ;
« Show errors » utilise la vue USER_ERRORS.
Tests
Syntaxe
IF expression THEN
instructions;
[ ELSIF expression THEN
instructions; ...]
[ ELSE
instructions;]
END IF;
Syntaxe
CASE expression
WHEN valeur THEN
instructions
[, … ]
Syntaxe
CASE expression
WHEN condition THEN
instructions
[, … ]
[ ELSE
instructions ]
END CASE;
On sort du CASE dès qu’une condition est vérifiée.
Syntaxe
[<< nomBoucle >>]
LOOP
instructions ;
END LOOP [ nomBoucle ] ;
On sort de la boucle sans fin avec un EXIT :
EXIT [ nomBoucle ] [ WHEN condition ] ;
L’EXIT seul permet de sortir sans condition. En général un EXIT seul est dans un test.
L’EXIT WHEN inclut la condition de sortie. En général, il n’est pas dans un test.
Syntaxe
[<< nomBoucle >>]
WHILE condition LOOP
instructions ;
END LOOP [ nomBoucle ] ;
Syntaxe
[<< nomBoucle >>]
FOR variable IN [REVERSE] expression1 .. expression2 LOOP
instructions ;
END LOOP [ nomBoucle ] ;
Syntaxe
[<< nomBoucle >>]
FORALL variable IN expression1 .. expression2 LOOP
[instructions ;]
instruction LMD ;
Présentation
Exit permet de quitter la boucle.
Syntaxe
EXIT [nomBoucle] [WHEN condition] ;
Le type RECORD
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=&empno;
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 ;
/
Présentation
Partout où on utilise un nom de type (nomType), on peut utiliser un type RECORD provenant
d’une table ou d’une variable.
Exemple
On reprend l’exemple précédent, mais avec un ROWTYPE.
undef empno; -- empno à saisir : 7839 par exemple
DECLARE
v_emp EMP%ROWTYPE;
BEGIN
SELECT * INTO v_emp
FROM emp
WHERE empno=&empno;
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 ;
/
Remarque
Le ROWTYPE oblige à faire un SELECT * INTO : le * est nécessaire pour qu’on ait le même
nombre d’attributs.
Subtilité syntaxique
Il ne faut pas créer des types synonymes ou des sous-types à partir de type ROWTYPE.
FOR i IN 1 .. 5 LOOP
dbms_output.put_line('v_tab['||I||']='||v_tab(i));
END LOOP;
END;
/
BEGIN
SELECT empno BULK COLLECT INTO v_tabEmpnos
FROM emp
WHERE ROWNUM<5;
v_tabEmpnos(-5):=-5;
v_tabEmpnos(10000):=1000;
dbms_output.put_line(v_tabEmpnos(1));
dbms_output.put_line(v_tabEmpnos(2));
dbms_output.put_line(v_tabEmpnos(3));
dbms_output.put_line(v_tabEmpnos(4));
dbms_output.put_line(v_tabEmpnos(-5));
dbms_output.put_line(v_tabEmpnos(10000));
END;
/
Ø A noter
• v_tabEmpnos(-5):=10; Les indices d’un tableau de taille variable ne sont pas
nécessairement contigus. C’est le principe du tableau associatif en PHP.
• Select BULK COLLECT INTO : équivalent à Select INTO pour une variable de type
simple.
Le BULK COLLECT INTO ne fonctionne qu’avec 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
• v_tabMoy('CLERK') : on retrouve le principe du tableau associatif du PHP.
• INDEX BY varchar2(10) : 10 fixe la limite du nom donné à l’indice du tableau.
• v_tabMoy('CLERK'). L’indice du tableau est une chaîne.
Présentation
EXISTS(n) : Revoie vrai si l’élément d’indice n existe.
COUNT : Renvoie le nombre d’éléments du tableau.
FIRST / LAST Renvoie le premier ou le dernier élément du tableau.
PRIOR(n) / NEXT(n) Retourne l’élément précédent ou suivant l’élément d’indice n.
DELETE(n) Supprime l’élément d’indice n.
DELETE Supprime l’élément de l’indice en cours.
DELETE(a,b) Supprime les éléments d’indice a et b
Principe d’utilisation
Ce sont des méthodes au sens de la programmation objet.
Exemple
DECLARE
if (v_tabEntier.exists(5)) then
dbms_output.put_line('t(5) existe');
else
dbms_output.put_line('t(5) n’’existe pas');
end if;
if (v_tabEntier.exists(11)) then
dbms_output.put_line('t(11) existe');
else
dbms_output.put_line('t(11) n’’existe 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
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
Procédure PL/SQL terminée avec succès.
INSERT INTO
C’est un INSERT INTO classique.
On peut faire : « VALUES(v_emp) » avec v_emp de type %ROWTYPE.
UPDATE
C’est un UPDATE classique.
On peut faire « SET ROW = v_emp » avec v_emp de type %ROWTYPE.
DELETE
C’est un DELETE classique.
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 d’’employé augmenté');
end if;
END;
/
SQL%NOTFOUND
Vrai (true) si aucun tuple n’a été créé ou modifié ou supprimé.
C’est le contraire de SQL%FOUND.
SQL%ROWCOUNT
Renvoie le nombre de tuples qui ont été créés ou modifies ou supprimés.
Présentation
RETURNING permet de renvoyer le ou les tuples qui ont été modifiés 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('L’’employé n° '||v_empno||' a été
augmenté');
else
dbms_output.put_line('Pas d’’employé augmenté');
end if;
END;
/
Syntaxe
RETURNING expression1 [,...] [BULK COLLECT] INTO variable1 [,...];
Présentation
Le PL-SQL ne permet pas d’utiliser directement les commandes du DDL (CREATE TABLE,
DROP TABLE, etc.) ni les commandes du DCL (GRANT, REVOKE, etc.).
La clause EXECUTE IMMEDIATE permet d’utiliser toutes ces commandes.
La clause EXECUTE IMMEDIATE remplace avantageusement le package DBMS_SQL (cf.
chapitre sur les packages standards).
Exemple
ACCEPT nomAtt CHAR prompt 'Entrez l’’attribut que vous voulez
catégoriser : ';
DECLARE
v_sqlDynamique varchar2(200);
v_nomAtt varchar2(20);
v_nomTable varchar2(20);
BEGIN
v_nomAtt := '&nomAtt';
v_nomTable :='cat_'||v_nomAtt ;
dbms_output.put_line('nomAtt : '||v_nomAtt||' - nomTable :
'||v_nomTable);
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 récupération d’un ou de plusieurs tuples
résulats d’un SELECT.
• RETURNING permet de renvoyer le ou les tuples qui ont été modifiés dans une variable.
• USING permet de mettre des arguments dans l’instruction de l’EXECUTE IMMEDIATE.
Dans la chaîne « varInstruction », les arguments sont précédés par « : ». Attention, ces
arguments ne peuvent être passés que dans un SELECT ou une instruction du DML. On ne
peut pas les utiliser pour une instruction du DDL ou du DCL.
Présentation
Un curseur est une variable qui permet de parcourir les lignes d’un SELECT une par une.
Sa syntaxe est un peu compliquée.
Il existe un usage de base à syntaxe simplifiée pour parcourir un SELECT : le FOR IN, sans
fetch et sans curseur.
L’usage de base du parcours d’un SELECT : FOR tuple IN SELECT (…) LOOP
Le FOR IN sans fetch et sans curseur permet de parcourir les lignes d’une table relationnelle
de façon simple.
NON
Exemple
On déclare une variable de type CURSOR qui est un SELECT
Et une variable correspondant au ROWTYPE 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;
BEGIN
...
END;
Syntaxe
DECLARE
CURSOR c_nomCurseur IS requiteSQL;
v_nomVar c_nomCurseur%ROWTYPE;
BEGIN
...
END;
Open curseur
Pour commencer à travailler sur un curseur, on doit l’ouvrir :
OPEN c_emp;
Close curseur
Quand on a fini d’utiliser un curseur, il vaut mieux le fermer : ça libère l’accès aux données
pour d’autres utilisateurs.
Par défaut, PL-SQL ferme tous les curseurs ouverts à la fin de la procédure.
CLOSE c_emp;
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
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 traitées : '||c_emp%rowcount);
CLOSE c_emp;
END;
/
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 traitées : '||c_emp%rowcount);
close c_emp;
END;
/
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 traitées : '||c_emp%rowcount);
close c_emp;
END;
/
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 ;
END;
/
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;
Commit et Rollback
Pour libérer les attributs verrouillés il faut utiliser un COMMIT ou un ROLLBACK.
Après un COMMIT ou un ROLLBACK, on ne peut plus faire de fetch.
Ø Explications
• On déclare d’abord un TYPE REF CURSOR en précisant le type de tuple retourné dans le
RETURN.
• Ensuite on déclare une variable du type REF CURSOR prédéfini.
• Enfin, à l’ouverture, OPEN, on ajoute un FOR suivi d’une requête.
• Ainsi, on pourra utiliser le même curseur pour des requêtes différentes, à condition
seulement que les attributs projetés soient les mêmes. Il faudra fermer le curseur (CLOSE),
pour ensuite pouvoir le rouvrir : OPEN … FOR.
Ø Explications
• On déclare d’abord un TYPE REF CURSOR sans préciser le type de tuple retourné.
• Ensuite on déclare une variable du type REF CURSOR prédéfini.
• Enfin, à l’ouverture, OPEN, on ajoute un FOR suivi d’une requête.
• Ainsi, on pourra utiliser le même curseur pour des requêtes différentes quels que soient les
attributs projetés. Il faudra fermer le curseur (CLOSE), pour ensuite pouvoir le rouvrir :
OPEN … FOR.
Ø Explications
• On déclare d’abord un TYPE REF CURSOR sans préciser le type de tuple retourné.
• Ensuite on déclare une variable du type REF CURSOR prédéfini.
• On déclare une chaîne de caractères qui contient un SELECT avec un paramètre
« :nomParam ».
• On ouvre le curseur, OPEN, avec un FOR de la chaîne de caractères et un USING
proposant une valeur pour le paramètre de la chaîne.
Conclusion
L’intérêt des variables curseur c’est qu’elles pourront être passées en paramètre à des
procédures ou des fonctions.
Présentation
Quand une erreur système ou applicative se produit, une exception est générée.
Les traitements en cours dans la section d’exécution (section du BEGIN) sont stoppés.
Les traitements de la section d’exception (section du EXCEPTION) commence.
Syntaxe
DECLARE
déclaration de variables
BEGIN
instructions
EXCEPTION
WHEN nom d’exceptions THEN
instructions
END;
SQLCODE et SQLERRM
SQLCODE : pour afficher le numéro de l’erreur.
SQLERRM : pour afficher le numéro et le message d’erreur.
dbms_output.put_line('SQLCODE = '||SQLCODE);
dbms_output.put_line('SQLERRM = '||SQLERRM);
Principe
En utilisant les exceptions prédéfinies par le système, on va pouvoir préciser les messages
d’erreur.
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 NO_DATA_FOUND THEN
dbms_output.put_line('L’’employé n° '||v_empno||' n’’existe
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 donnée trouvée
Principe
En déclarant une zone d’exception avec le cas « OTHERS », on prend en compte toutes les
erreurs système.
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 donnée trouvée
Ø Précisions
La fonction SQLCODE renvoie le code de l’erreur.
La fonction SQLERRM renvoie le message de l’erreur.
Principe
L’utilisateur peut gérer ses propres erreurs, c’est-à-dire des contraintes d’intégrité spécifiques
non prises en compte par le SQL.
En déclarant une exception et en utilisant la fonction RAISE nomException, l’utilisateur
renvoie le programme vers la zone nomException en cas d’erreur. L’exception est nommée.
Exemple
On considère qu’un nouveau salaire doit toujours être supérieur à l’ancienne 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;
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 inférieur à l’’ancien : '||v_emp.sal) ;
RAISE UPDATE_SAL_EMP ;
end if;
EXCEPTION
WHEN UPDATE_SAL_EMP THEN
dbms_output.put_line('Problème de mise à jour des
données');
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('L’’employé n° '||v_empno||' n’’existe
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 inférieur à l'ancien :
5000
Problème de mise à jour des données
Principe
L’utilisateur peut gérer ses propres erreurs, c’est-à-dire des contraintes d’intégrité spécifiques
non prises en compte par le SQL. En utilisant la fonction RAISE_APLICATION_ERREUR,
l’utilisateur renvoie le programme vers la zone OTHERS en cas d’erreur. L’exception n’est pas
nommée.
Exemple
On considère qu’un nouveau salaire doit toujours être supérieur à l’ancienne 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);
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 inférieur à l’’ancien : '||v_emp.sal) ;
RAISE_APPLICATION_ERROR(-20000,
'Problème d’’intégrité des données') ;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('L’’employé n° '||v_empno||' n’’existe
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 inférieur à l'ancien :
5000
SQLCODE = -20000
SQLERRM = ORA-20000: Problème d'intégrité des données
Ø Précisions
La fonction RAISE_APPLICATION_ERROR a deux paramètres :
• Un numéro d’erreur, qu’on choisit entre –20 000 et –20 999 pour éviter les conflits avec les
erreurs ORACLE.
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
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
Si on supprime les WHEN MON_EXCEPTION, c’est le WHEN OTHERS qui sera pris en
compte.
/
Others - bloc 2
bloc 1
Présentation
Principe
Un trigger est un bloc qui est déclenché automatiquement à chaque occurrence d’un événement
déclenchant. Ils n’ont jamais d’arguments. Ils ne peuvent pas être déclenchés
« manuellement ».
Usages
Les triggers servent à :
• Maintenir des contraintes d’intégrité non gérées par le DDL.
• Consigner les changements apportés à une table et mettre à jour des statistiques.
• Mettre à jour des données calculées.
3 types de triggers
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 caractéristiques principales
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 déclenché sur un INSERT, un UPDATE, un DELETE ou n’importe quelle
combinaison des trois.
Il concerne nécessairement une table et éventuellement un ou plusieurs attributs de cette table.
Ce sont des booléens. Ils permettent de spécifier une partie du corps du trigger en fonction de la
nature de l’instruction déclenchante : INSERT, UPDATE ou DELETE.
TRIGGER BEFORE
Remarque: ce principe vaut pour tous les blocs ORACLE (Procédures, fonctions, etc.).
La vue USER_SOURCE permet de lister les triggers, les procédures et les fonctions en
distinguant entre les trois :
Select distinct name, type from user_source;
Débogage
Outils système
Show errors ;
« Show errors » utilise la vue USER_ERRORS.
Outils classique
On peut afficher des commentaires dans un trigger via un dbms_output.put_line.
Particulièrement, pour tracer l’entrée dans un trigger.
Principe
Un package permet de regrouper des fonctions et des procédures qui vont ensemble.
On peut y ajouter des variables, des types et des curseurs.
Syntaxe
Exemples de packages :
• DBMS_OUTPUT : pour afficher des messages
• DBMS_LOB : pour travailler avec des données binaires
• DBMS_JOB : pour soumettre des travaux à une fréquence déterminée (scheduler).
• UTL_FILE : pour manipuler des fichiers
• UTL_MAIL : pour envoyer des mails
• DBMS_SQL : pour faire du DDL avec du PLSQL, est avantageusement remplacé par
EXECUTE IMMEDIATE
• DBMS_RANDOM : pour générer des nombres aléatoires
• Etc.
Présentation
Le scheduler ORACLE permet d’exécuter des tâches SQL ou OS à intervalles réguliers.
Il permet de :
• définir la tâche à exécuter : CREATE_PROGRAM
• définir la durée et la fréquence de l’exécution : CREATE_SCHEDULE
• lancer la tâche à exécuter : CREATE_JOB
Codage
Programmation PLSQL via SQLPLUS
Programmation graphique assistée via OEM (Oracle Entreprise Manager).
Création en 3 étapes
Ø CREATE_PROGRAM
BEGIN
DBMS_SCHEDULER.CREATE_PROGRAM (
program_name => ‘nomDuProgramme’
program_action => ‘codeDuProgramme’
program_type => ‘typeDuPrograme’
comments => ‘commentaires’
);
END;
/
• Program_name : le nom du programme sera utilisé dans la création du JOB
• Program_action : du code PL_SQL (commence par un BEGIN et finit par un END ;), ou
bien un appel à une procédure stockée (le nom de la procédure suffit), un appel à un exé ou
un batch OS (le nom du batch avec le chemin et l’extension suffit).
• Program_type : ‘PLSQL_BLOCK’ pour un bloc PLSQL, ‘stored_procedure’ pour une
procédure stockée, ‘exécutable’ pour un exécutable OS.
• Comments : des commentaires.
Ø CREATE_SCHEDULE
BEGIN
DBMS_SCHEDULER.CREATE_SCHEDULE (
schedule_name => ‘nomDuScheduler’
start_date => dateDeDébut
end_date => dateDeFin
repeat_intervel => ‘fréquenceDExecution’
comments => ‘commentaires’
Ø CREATE_JOB
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => ‘nomDuJob’
program_name => ‘nomDuProgramme’
schedule_name => ‘nomDuScheduler’
);
END;
/
• Job_name : le nom du job
• Program_name : le nom du programme créé avec CREATE_PROGRAM
• schedule_name : le nom du scheduler créé avec CREATE_SCHEDULER
Création en 1 étape
Le CREATE_JOB peut reprendre tous les attributs utiles des CREATE_PROGRAM et
CREATE_SCHEDULER
Ø CREATE_JOB
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => ‘nomDuJob’
job_type => ‘typeDuPrograme’
job_action => ‘codeDuProgramme’
start_date => dateDeDébut
end_date => dateDeFin
repeat_intervel => ‘fréquenceDExecution’
);
END;
/
• Job_name : le nom du job
• job_type : équivalent de « program_type »
• job_action : équivalent de « program_action »
Ø ENABLE
ENABLE permet de rendre actif un JOB.
BEGIN
DBMS_SCHEDULER.ENABLE (‘nomDuJob’);
END;
/
Consultation: DBA_SCHEDULER_RUNNING_JOB
La vue DBA_SCHEDULER_RUNNING_JOB permet de savoir si un JOB est en cours
d’exécution.
Ø Attributs :
• JOB_NAME
• ENABLED : actif ou pas
• STATE : en cours ou pas (entre le start_date et le end_date)
• RUN_COUNT : nombre d’exécutions ou date de début
• MAX_RUNS
• NEXT_RUN_DATE
TP
Soit une table des employés qui contient l’attribut Salaire et l’attribut « numéro de
département ». Créer un JOB qui, chaque minute, augmente de 5 tous les employés du
département 10.
Vérifier le bon fonctionnement de ce JOB en consultant la table des employés et la vue des
JOBS.
Créer ensuite un job qui exécutera toutes les minutes une commande UNIX. Par exemple un
« df » pour connaître la taille du disque. Le résultat sera enregistré dans un fichier de log.
Type structuré
Création du type
CREATE OR REPLACE TYPE type_adresse
IS OBJECT (
RUE VARCHAR(100),
CP VARCHAR(10),
VILLE VARCHAR(20)
)
/
Consultation du type
DESC type_adresse ;
Création de la table
CREATE TABLE ELEVES (
NE NUMBER,
NOM VARCHAR(50),
ADRESSE TYPE_ADRESSE
) ;
Type objet
Les types structurés sont en fait des objets.
L’accès aux champs n’est possible qu’à travers des méthodes
CREATE OR REPLACE TYPE type_adresse
AS OBJECT (
RUE VARCHAR2(100),
CP VARCHAR2(10),