CONCEPTS DE BASE
LANGAGE PL/SQL
2022-2023 Enis BELHASSEN ([Link]@[Link])
Langage PL/SQL vs SQL
2
SQL : langage ensembliste non procédural
Ensemble de requêtes distinctes
Langage assertionnel de 4ème génération : on décrit le résultat sans dire
comment il faut accéder aux données
Possibilité d’encapsulation dans un langage hôte de 3ème génération
PL/SQL
‘Procedural Language’ : surcouche procédurale à SQL (traitements
itératifs, contrôles, affectations, exceptions, ….)
Un langage procédural pour lier plusieurs requêtes SQL avec des
variables et dans les structures de programmation habituelles
L’intérêt du PL/SQL est de pouvoir mélanger la puissance des instructions
SQL avec la souplesse d’un langage procédural
Avantages du PL/SQL
3
Traitements procéduraux : la gestion des variables
et les structures de contrôle (conditionnelles et
itératives)
Fonctionnalités supplémentaires : la gestion des
curseurs et le traitement des erreurs (exceptions)
Amélioration des performances : plusieurs
instructions sont regroupées dans une unité (bloc) qui
ne génèrera qu’un "accès" à la base (à la place
d’un accès par instruction)
Avantages du PL/SQL
4
Modularité : un bloc peut être nommé pour devenir
une procédure ou une fonction cataloguée et
réutilisable. Une procédure, ou fonction, cataloguée
peut être incluse dans un paquetage (package)
Portabilité : un programme PL/SQL est indépendant
du système d’exploitation qui héberge le serveur
Oracle. En changeant de système, les applicatifs
n’ont pas à être modifiés
Structure d’un bloc PL/SQL
5
Section DECLARE (section optionnelle) pour la déclaration des :
Variables locales simples
[DECLARE
Variables tableaux
Curseurs / Exceptions -- Déclarations]
Section BEGIN (section obligatoire) BEGIN
Section des ordres exécutables -- Instructions
Ordres SQL [EXCEPTION
Ordres PL -- Erreurs]
Section EXCEPTION (section optionnelle) END;
Traitement des erreurs interceptées
/
Exceptions SQL ou utilisateur
Il est possible d’ajouter des commentaires à un bloc :
-- commentaire sur une seule ligne (mono-lignes)
/*... ... */ commentaire sur plusieurs lignes (multi-lignes)
Structure d’un bloc PL/SQL
6
Un bloc peut être imbriqué dans le code d’un autre bloc :
sous-bloc.
Un sous-bloc peut aussi se trouver dans la partie des
exceptions.
Un sous-bloc commence par BEGIN et se termine par END.
PL/SQL n’est pas
sensible à la casse
(not case sensitive)
Types de blocs PL/SQL
7
Bloc Anonyme
Stockéen-dehors de la base de données
Compilé et exécuté à la volée
Procédure Stockée :
Compilée séparément
Stockée de façon permanente dans la BD
Déclencheur (Trigger)
Procédure stockée associée à une table
Exécution automatique sur événement
Commandes de dialogue – SQL*Plus
8
PROMPT texte : afficher le texte
ACCEPT variable [PROMPT texte] : forcer SQL*Plus à
attendre une valeur pour définir une variable permanente
Variables – SQL*Plus
9
Variables lues par un ACCEPT …. PROMPT
SQL*Plus ACCEPT var PROMPT 'Entrer la valeur : '
DECLARE
-- déclarations
BEGIN
PL/SQL -- &var si numérique
-- '&var' si chaine de caractères
END;
/
SQL*Plus -- Ordre SQL .....
Package DBMS_OUTPUT – SQL*Plus
10
Messages enregistrés dans une mémoire tampon
côté serveur
La mémoire tampon est affichée sur le poste client à
la fin Serveur ORACLE
Client SQL*PLUS BEGIN
DBMS_OUTPUT.PUT_LINE('Message1');
DBMS_OUTPUT.PUT_LINE('Message2');
Message1 DBMS_OUTPUT.PUT_LINE('Message3');
Message2 END;
Message3
Message1
Message2
Message3
SQL>SET SERVEROUT ON
Mémoire tampon
Types de Variables PL/SQL
11
Variables locales:
De type simple: type de base ou booléen
Faisant référence à la métabase (dictionnaire de
données de Oracle)
De type composé : Tableau, Record
Variables Extérieures:
Variablesd’un langage hôte (ex: C) (préfixés par : )
Paramètres (ex: SQL interactif préfixés par &)
Types de Variables
12
Opérateurs SQL
13
Opérateur d’affectation :=
Opérateurs arithmétiques + - / * **
Opérateur de concaténation ||
Opérateurs de comparaison
= < > <= >= <> != IS NULL LIKE BETWEEN IN
Opérateurs logiques AND, OR, NOT
Variables simples
14
Déclaration :
variable_name [CONSTANT] datatype
[NOT NULL] [:= | DEFAULT initial_value];
Le nom d’une variable (identificateur) commence
par une lettre suivie (optionnel) de symboles (lettres,
chiffres, $, _ , #).
Un identificateur peut contenir jusqu’à 30 caractères
Les autres symboles sont interdits (& , - / espace …)
Variables simples
15
Variables de type SQL
Date_nais DATE;
nom VARCHAR2(30) DEFAULT 'Bonjour';
minimum CONSTANT INTEGER := 5;
salaire NUMBER(8,2);
debut NUMBER NOT NULL := 10;
Variables de type booléen (TRUE, FALSE, NULL)
fin BOOLEAN;
reponse BOOLEAN DEFAULT TRUE;
ok BOOLEAN := TRUE;
Variables référençant la métabase
16
Référence à une colonne (table, vue)
vsalaire [Link]%TYPE;
vnom [Link]%TYPE;
Référence à une ligne (table, vue)
vemploye emp%ROWTYPE;
Variable de type ‘struct’
Contenu d’une variable : [Link]
[Link]
Référence une variable précédemment définie
commi number(7,2);
Salaire commi%TYPE;
Variables référençant la métabase
17
Exemple : déclaration et utilisation d'une variable ROWTYPE
var_emp qui contient le même nombre de champs que la
table EMP
declare
var_emp emp%rowtype;
begin
var_emp.empno := 1234;
var_emp.ename := 'Salah';
dbms_output.put_line ('Num emp : '||var_emp.empno);
dbms_output.put_line ('Nom emp : '||var_emp.ename);
dbms_output.put_line ('Job emp : '||var_emp.job);
end;
/
Conversion de type de données
18
Fonctions de conversion :
TO_CHAR, TO_DATE, TO_NUMBER
v_date1 := to_date('01/01/1999', 'dd/mm/yyyy');
v_date2 := to_date('01-jan-1999', 'dd-mon-yyyy');
v_date_systeme := to_char(sysdate, 'dd/mm/yyyy');
v_heure_systeme := to_char(sysdate, 'hh24:mi:ss');
v_date_systeme_complete :=
to_char(sysdate, 'dd/mm/yyyy hh24:mi:ss');
v_num_str := to_char(130);
v_num := to_number('140');
Traitements conditionnels
19
Structure alternative ou conditionnelle
IF …. THEN ….. ELSIF …… ELSE ……END IF;
IF condition1 THEN
instructions1;
[ELSIF condition2 THEN
instructions2;]
[… ELSIF condition3 THEN
instructions3;]
[ELSE
instructions;]
END IF;
Une instruction IF peut contenir plusieurs clauses ELSIF,
mais une seule clause ELSE (optionnelle).
Structure alternative : CASE
20
Choix selon la valeur d’une variable/une expression
CASE variable
WHEN valeur1 THEN action1;
WHEN valeur2 THEN action2;
………
ELSE action;
END CASE;
CASE
WHEN condition1 THEN action1;
WHEN condition2 THEN action2;
………
ELSE action;
END CASE;
Structure alternative : CASE
21
DECLARE
note varchar2(1) := 'D';
BEGIN
CASE note
when 'A' then dbms_output.put_line('Excellent');
when 'B' then dbms_output.put_line('Très Bien');
when 'C' then dbms_output.put_line('Bien');
when 'D' then dbms_output.put_line('Passable');
when 'E' then dbms_output.put_line('Mauvais');
else dbms_output.put_line('Valeur incorrecte');
END CASE;
END;
/
Structure alternative : CASE
22
Traitements itératifs
23
LOOP
LOOP
instructions;
EXIT WHEN (condition);
END LOOP;
FOR
FOR (indice IN [REVERSE] borne1..borne2) LOOP
instructions;
END LOOP;
WHILE
WHILE (condition) LOOP
instructions;
END LOOP;
Structures itératives
24
------ Affichage des nombres divisibles par 4 compris entre 1 et 20
------ Boucle FOR
BEGIN
dbms_output.put_line ('Les nombres divisibles par 4 sont : ');
FOR i IN 4..20 LOOP
IF (mod(i,4) = 0) THEN
dbms_output.put_line (to_char(i) || '-');
END IF;
END LOOP;
END;
/
Structures itératives
25
Structures itératives
26
------ Affichage des nombres divisibles par 4 compris entre 1 et 20
------ Boucle WHILE
DECLARE
i number (2);
BEGIN
dbms_output.put_line ('Les nombres divisibles par 4 sont : ');
i := 4;
WHILE (i <= 20) LOOP
IF (mod(i,4) = 0) THEN -- mod(i,4) : reste de la division de i par 4
dbms_output.put_line (to_char(i) || ' – ');
END IF;
i := i + 1;
END LOOP;
END;
/
Structures itératives
27
------ Affichage des nombres divisibles par 4 compris entre 1 et 20
------ Boucle LOOP
DECLARE
max_nombre CONSTANT number(3) := 20;
i number (2);
BEGIN
dbms_output.put_line ('Les nombres divisibles par 4 sont : ');
i := 4;
LOOP
IF (mod(i,4) = 0) THEN -- mod(i,4) : reste de la division de i par 4
dbms_output.put_line (to_char(i) || ' – ');
END IF;
i := i + 1;
EXIT WHEN (i > 20);
END LOOP;
END;
/
Exemple – Années Bissextiles
28
Affichage des années bissextiles comprise entre ANNEE1 et
ANNEE2
ACCEPT ANNEE1 PROMPT 'Entrer la valeur de annee 1 '
ACCEPT ANNEE2 PROMPT 'Entrer la valeur de annee 2 '
BEGIN
dbms_output.put_line ('Les annees bissextiles sont : ');
FOR i IN &ANNEE1 .. &ANNEE2 LOOP
IF ( (MOD (i,4) = 0 AND MOD(i, 100) != 0) OR MOD(i, 400) = 0 )
THEN dbms_output.put_line(i);
END IF;
END LOOP;
END;
/
SELECT …. INTO … (mono – ligne)
29
Toute valeur de colonne est rangée dans une
variable avec INTO
DECLARE
nom [Link]%type;
fonction [Link]%type;
salaire [Link]%type;
BEGIN
SELECT ename, job, sal
INTO nom, fonction, salaire
FROM emp
WHERE empno = 7369;
dbms_output.put_line ('Nom de emp 7369 : '||nom);
dbms_output.put_line ('Fonction de emp 7369 : '||fonction);
dbms_output.put_line ('Salaire de emp 7369 : '||salaire);
END;
/
SELECT …. INTO … (mono – ligne)
30
Variable ROWTYPE
DECLARE
vdept dept%rowtype;
BEGIN
SELECT *
INTO vdept
FROM dept
WHERE deptno = 40;
DBMS_OUTPUT.PUT_LINE('Nom dept 40 : '|| [Link]);
DBMS_OUTPUT.PUT_LINE('Local dept 40 : '|| [Link]);
END;
/
Curseurs (Sélection multi – ligne)
31
Curseur : Structure de données permettant de stocker
le résultat d’une requête qui retourne plusieurs lignes
Curseur implicite : généré et géré par le noyau de
Oracle pour chaque ordre SQL
SELECT t.* FROM table t WHERE ……
t est un curseur utilisé par SQL
Curseur explicite : généré par l’utilisateur pour traiter
un ordre SELECT qui ramène plusieurs lignes
Déclaration
Ouverture du curseur
Traitement des lignes
Fermeture du curseur
Démarche générale des curseurs
32
Déclaration du curseur : DECLARE
Ordre SQL sans exécution
Ouverture du curseur : OPEN
SQL ‘monte‘ les lignes sélectionnées en mémoire Verrouillage
préventif possible
Sélection d’une ligne : FETCH
Chaque FETCH ramène une ligne dans le programme client
Tant qu’il existe une ligne en mémoire
Fermeture du curseur : CLOSE
Récupération de l’espace mémoire
Traitement d’un curseur
33
Programme PL/SQL
Mémoire
FETCH
variables
DECLARE
CURSOR c1 IS SELECT ……;
BEGIN
OPEN c1;
FETCH c1 INTO ………;
WHILE (c1%FOUND) LOOP
………
……… OPEN
FETCH c1 INTO ………;
END LOOP;
CLOSE c1;
END;
BD
Attributs d’un Curseur
34
Curseur%FOUND
Variable
booléenne : est égal à TRUE si le dernier
FETCH a retourné un résultat
Curseur%NOTFOUND
Variable booléenne opposée au précédent : est égal à
TRUE si le dernier FETCH n’a pas retourné un résultat
Curseur%ROWCOUNT
Variable numérique : retourne le nombre de lignes lues
Curseur%ISOPEN
Variable booléenne : est égal à TRUE si le curseur est
ouvert
Gestion ‘classique’ d’un curseur
35
DECLARE
CURSOR emp_cursor IS -- déclarer le curseur
SELECT ename, dname FROM emp, dept
WHERE [Link] = [Link];
emp_record emp_cursor%ROWTYPE;
BEGIN
OPEN emp_cursor ; -- Ouvrir le curseur
LOOP
FETCH emp_cursor INTO emp_record; -- avancer au tuple suivant
EXIT WHEN emp_cursor%NOTFOUND; -- sortir si la fin du curseur
-- est détectée
DBMS_OUTPUT.PUT_LINE('L''employe '|| emp_record.ename ||
' travaille dans le departement ' || emp_record.dname);
END LOOP;
CLOSE emp_cursor; -- fermer le curseur
END;
/
Gestion ‘classique’ d’un curseur
36
Gestion ‘classique’ d’un curseur
37
DECLARE
CURSOR emp_cursor IS -- déclarer le curseur
SELECT ename, dname FROM emp, dept
WHERE [Link] = [Link];
emp_record emp_cursor%ROWTYPE;
BEGIN
OPEN emp_cursor ; -- Ouvrir le curseur
LOOP
FETCH emp_cursor INTO emp_record; -- avancer au tuple suivant
EXIT WHEN emp_cursor%NOTFOUND; -- sortir si la fin du curseur
-- est détectée
DBMS_OUTPUT.PUT_LINE(emp_cursor%ROWCOUNT||' - L''employe '||
emp_record.ename ||
' travaille dans le departement '
|| emp_record.dname);
END LOOP;
CLOSE emp_cursor; -- fermer le curseur
END;
/
Gestion ‘classique’ d’un curseur
38 DECLARE
CURSOR emp_cursor IS -- déclarer le curseur
SELECT ename, dname FROM emp, dept
WHERE [Link] = [Link];
nom_emp [Link]%TYPE;
nom_dept [Link]%TYPE;
BEGIN
OPEN emp_cursor ; -- Ouvrir le curseur
LOOP
FETCH emp_cursor INTO nom_emp, nom_dept;
-- avancer au tuple suivant
EXIT WHEN emp_cursor%NOTFOUND; -- sortir si la fin du curseur
-- est détectée
DBMS_OUTPUT.PUT_LINE(emp_cursor%ROWCOUNT||' - L''employe '||
nom_emp ||
' travaille dans le departement '||
nom_dept);
END LOOP;
CLOSE emp_cursor; -- fermer le curseur
END;
/
Gestion ‘classique’ d’un curseur
39
DECLARE
CURSOR emp_cursor IS -- déclarer le curseur
SELECT ename, dname FROM emp, dept
WHERE [Link] = [Link];
emp_record emp_cursor%ROWTYPE;
BEGIN
OPEN emp_cursor; -- Ouvrir le curseur
FETCH emp_cursor INTO emp_record; -- avancer au premier tuple
WHILE (emp_cursor%FOUND) LOOP -- sortir si aucun tuple
-- n’a été ramené par
-- le dernier fetch
DBMS_OUTPUT.PUT_LINE(emp_cursor%ROWCOUNT||' - L''employe '||
emp_record.ename ||
' travaille dans le departement '
|| emp_record.dname);
FETCH emp_cursor INTO emp_record; -- avancer au tuple suivant
END LOOP;
CLOSE emp_cursor; -- fermer le curseur
END;
/
Gestion ‘automatique’ d’un curseur
40
DECLARE
CURSOR emp_cursor IS -- déclarer le curseur
SELECT ename, dname FROM emp, dept
WHERE [Link] = [Link];
BEGIN
-- Ouverture, parcours et fermeture automatique du curseur
FOR emp_record IN emp_cursor LOOP
DBMS_OUTPUT.PUT_LINE(emp_cursor%rowcount||' - L''employe '||
emp_record.ename ||
' travaille dans le departement ' ||
emp_record.dname);
END LOOP;
END;
/
Gestion ‘automatique’ d’un curseur
41
BEGIN
-- Déclaration, ouverture, parcours et fermeture
-- automatique du curseur
FOR emp_record IN ( SELECT ename, dname FROM emp, dept
WHERE [Link] = [Link])
LOOP
DBMS_OUTPUT.PUT_LINE('L''employe '||
emp_record.ename ||
' travaille dans le departement '
|| emp_record.dname);
END LOOP;
END;
/
Curseurs paramétrés
42
ACCEPT num_dep PROMPT 'Entrer le numero du departement : '
DECLARE
CURSOR emp_cursor (p_dep [Link]%TYPE) IS
SELECT ename, job, sal
FROM emp
WHERE deptno = p_dep;
v_employe emp_cursor%rowtype;
BEGIN
dbms_output.put_line ('Employes du dept. '||&num_dep);
open emp_cursor(&num_dep);
LOOP
fetch emp_cursor into v_employe;
exit when emp_cursor%notfound;
dbms_output.put_line (v_employe.ename|| ' - '|| v_employe.job ||
' : '|| v_employe.sal);
END LOOP;
close emp_cursor;
END;
/
Curseur - Clause CURRENT-OF
43
la clause CURRENT-OF permet d’accéder
directement, en modification ou en suppression, à la
ligne que vient de renvoyer l’ordre FETCH.
Au préalable, dans la déclaration du curseur, il faut
réserver les lignes qui seront modifiées par la pose
d’un verrou d’intention (...FOR UPDATE OF
colonne_à_modifier...).
Curseur - Clause CURRENT-OF
44
MODIFIER LA COLONNE COMM QUI DOIT ETRE EGALE À 5% SAL
SI LA COMMISSION EST NULL OU ÉGALE À 0
DECLARE
CURSOR emp_cursor IS
SELECT comm
FROM emp
FOR UPDATE OF comm;
BEGIN
FOR v_employe IN emp_cursor LOOP
IF v_employe.comm IS NULL or v_employe.comm = 0
THEN
UPDATE emp SET comm = sal * 0.05
WHERE CURRENT OF emp_cursor;
END IF;
END LOOP;
END;
/
Gestion des Exceptions
45
Toute erreur (SQL ou applicative) entraîne automatiquement
un débranchement vers le paragraphe EXCEPTION :
Débranchement involontaire (erreur SQL)
BEGIN ou volontaire (erreur applicative)
instruction1;
instruction2;
……
instructionn;
EXCEPTION
WHEN exception1 THEN
………
WHEN exception2 THEN
………
WHEN OTHERS THEN
………
END;
Gestion des Exceptions
46
Exceptions SQL déclenchée implicitement par une erreur Oracle
Déjà définies (pas de déclaration) :
◼ DUP_VAL_ON_INDEX
◼ NO_DATA_FOUND / TOO_MANY_ROWS
◼ OTHERS
Exceptions applicatives déclenchée explicitement par le programme
Déclaration sans n°erreur (section DECLARE)
nomerreur EXCEPTION;
Lever l'exception (section BEGIN)
RAISE nomerreur;
Gestion des Exceptions
47
Fonctions PL/SQL pour la gestion d’erreurs
SQLCODE : renvoyer la valeur numérique associée à la dernière
exception détectée
SQLERRM : renvoyer le message associé au code de l’erreur
Exceptions prédéfinies
Nom Code erreur Sqlcode Description
--------------------------------------------------------------------------------------------------
NO_DATA_FOUND ORA-01403 -1403 SELECT mono-ligne retournant 0 ligne
TOO_MANY_ROWS ORA-01422 -1422 SELECT mono-ligne retournant plus d’1 ligne
DUP_VAL_ON_INDEX ORA-00001 -1 Insertion d’une ligne en doublon
VALUE_ERROR ORA-06502 -6502 Erreur arithmétique, conversion ou limite de taille
ZERO_DIVIDE ORA-01476 -1476 Division par zéro
CURSOR_ALREADY_OPEN ORA-06511 -6511 Ouverture d’un curseur déjà ouvert
INVALID_NUMBER ORA-01722 -1722 Echec sur une conversion d’un chaîne de caractères
vers un nombre
…
Exemple de gestion d’exception
48
DECLARE
erreur EXCEPTION; ---- Déclaration exception
BEGIN
SELECT … INTO ……
IF ……… THEN RAISE erreur; ---- Levée exception
………
EXCEPTION
WHEN NO_DATA_FOUND THEN ---- Exception prédéfinie
………
WHEN erreur THEN ---- Traitement exception
………
WHEN OTHERS THEN ---- Exception prédéfinie
………
END;
Exemple de gestion d’exception
49
DECLARE
nbre_emp INTEGER;
aucun_emp EXCEPTION; ---- Déclaration exception
BEGIN
SELECT COUNT(*) INTO nbre_emp FROM emp;
IF nbre_emp =0 THEN
RAISE aucun_emp; ---- Levée exception
ELSE
DBMS_OUTPUT.PUT_LINE('La table EMP contient '||nbre_emp||' employes');
END IF;
EXCEPTION
WHEN aucun_emp THEN ---- Traitement exception
DBMS_OUTPUT.PUT_LINE('La table EMP est vide');
WHEN OTHERS THEN ---- Exception prédéfinie
DBMS_OUTPUT.PUT_LINE('Erreur inconnue ' || SQLERRM);
END;
/
Exemple de gestion d’exception
50
DECLARE
c INTEGER;
aucun_emp EXCEPTION; ---- Déclaration exception
BEGIN
SELECT COUNT(*) INTO c FROM emp;
IF c=0 THEN
RAISE aucun_emp; ---- Levée exception
………
EXCEPTION
WHEN aucun_emp THEN ---- Traitement exception
RAISE_APPLICATION_ERROR(-20501, 'La table EMP est vide');
………
END;
/
La procédure RAISE_APPLICATION_ERROR permet de définir des messages d'erreur
personnalisés en indiquant :
• numero_erreur : représente un entier négatif compris entre -20000 et -20999
• message : représente le texte du message d'une longueur maximum de 2048 octets
Exercice
51
Écrire un bloc anonyme PL/SQL permettant de calculer et d’afficher pour
chaque employé le montant de l’impôt et le net à payer :
Définir et parcourir un curseur permettant de récupérer les noms des
employés (ENAME), leurs salaires (SAL) et leurs commissions (COMM)
Pour chaque employé, afficher un message à l’écran indiquant son nom,
son salaire ainsi que le montant de l’impôt et le net à payer qui doivent
être calculés comme suit :
Montant de l’impôt :
◼ Si le salaire est inférieur à 1000 alors l’impôt = 25% du salaire
◼ Si le salaire est compris entre 1000 et 1500 alors l’impôt = 30 % du salaire
◼ Si le salaire supérieur à 1500 alors l’impôt = 35 % du salaire
Net à payer = salaire (SAL) + commission (COMM) – Montant de l’impôt
Programmer l’exception prédéfinie OTHERS en affichant le message
suivant : « Erreur inconnue !!! »
Exercice
52
DECLARE
impot [Link]%type;
net_payer [Link]%type;
BEGIN
FOR emp_record IN (SELECT ename, sal, comm FROM emp)
LOOP
IF (emp_record.sal < 1000)
THEN impot := emp_record.sal * 0.25;
ELSIF emp_record.sal <= 1500
THEN impot := emp_record.sal * 0.3;
ELSE impot := emp_record.sal * 0.35;
END IF;
net_payer := emp_record.sal + nvl(emp_record.comm, 0) - impot;
DBMS_OUTPUT.PUT_LINE('L''employe '||emp_record.ename ||
' - Salaire = ' ||emp_record.sal|| ' - Impot = ' ||impot||' - Net a payer = ' ||net_payer);
END LOOP;
EXCEPTION
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Erreur inconnue !!!');
END;
/
PROCÉDURES –
FONCTIONS – PACKAGES
2022-2023 Enis BELHASSEN ([Link]@[Link])
Procédures stockées
54
Une procédure PL/SQL est un bloc nommé qui
exécute une ou plusieurs actions.
Une procédure peut être stockée dans la base de
données, comme tout autre objet.
BD – Oracle
Procédure/Fonction
Stockée
Procédures stockées
55
CREATE [OR REPLACE] PROCEDURE nom-procédure
[(argument [mode] type, ...)] IS
bloc-procédure;
argument : nom d’un paramètre formel
mode : définit si le paramètre formel est en entrée (IN), en
sortie (OUT) ou en entrée-sortie (IN OUT). Par défaut : IN
type : le type du paramètre formel
bloc-procédure : le corps PL/SQL de la procédure
Procédures stockées
56
Exemple :
CREATE OR REPLACE PROCEDURE modifierTitre
(num IN [Link]%TYPE, nouvTitre IN [Link]%TYPE) IS
BEGIN
UPDATE film SET titre = UPPER(nouvTitre)
WHERE numFilm = num;
END modifierTitre;
Utiliser ‘SHOW ERRORS’ pour voir les erreurs de la
compilation.
Pour exécuter la procédure :
execute nom_proc (valeurs des paramètres)
Procédures stockées
57
Ecrire une procédure pour afficher le nom et le salaire d’un
employé dont le numéro est spécifié comme paramètre
CREATE OR REPLACE PROCEDURE affiche_salaire
(num_employe IN [Link]%type) IS
v_ename [Link]%type;
v_sal [Link]%type;
BEGIN
SELECT ename, sal
INTO v_ename, v_sal
FROM EMP
WHERE empno = num_employe;
DBMS_OUTPUT.PUT_LINE(num_employe||' '||v_ename||'
'||v_sal);
END;
/
Procédures stockées
58
Pour tester une procédure sous SQL*Plus :
EXECUTE affiche_salaire (7369)
Ou créer un bloc PL/SQL dans lequel on fait appel à
la procédure :
BEGIN
affiche_salaire (7369);
END;
/
Procédures stockées
59
Ecrire une procédure qui permet de retourner dans des
paramètres OUT le nom et le salaire d’un employé dont le
numéro est spécifié comme paramètre IN
CREATE OR REPLACE PROCEDURE affiche_salaire
(num_employe IN [Link]%type,
v_ename OUT [Link]%type,
v_sal OUT [Link]%type) IS
BEGIN
SELECT ename, sal
INTO v_ename, v_sal
FROM EMP
WHERE empno = num_employe;
END;
/
Procédures stockées
60
Pour tester une procédure avec des paramètres OUT sous
SQL*Plus : créer un bloc PL/SQL dans lequel on fait appel à
la procédure :
DECLARE
nom_emp [Link]%type;
sal_emp [Link]%type;
BEGIN
affiche_salaire (7369, nom_emp, sal_emp);
DBMS_OUTPUT.PUT_LINE(nom_emp||' '|| sal_emp);
END;
/
Procédures stockées
61
Suppression d’une procédure :
DROP PROCEDURE nom_proc;
Dictionnaire de données :
USER_SOURCE, USER_OBJECTS, USER_ERRORS
Fonctions stockées
62
Les fonctions stockées sont des blocs PL/SQL
nommés qui retournent une valeur.
Une fonction peut être stockée dans la base de
données comme les autres objets de la base de
données.
Fonctions stockées
63
CREATE [OR REPLACE] FUNCTION nom-fonction
[(argument [IN] type, ...)]
RETURN type-retour IS
bloc-fonction;
Les paramètres sont forcément en entrée (IN)
Dans le bloc-fonction :
RETURN nom-variable;
Fonctions stockées
64
Exemple :
CREATE OR REPLACE FUNCTION moyenneDuree (dd IN DATE )
RETURN NUMBER IS
moy NUMBER(8) := 0;
BEGIN
SELECT AVG(duree)
INTO moy
FROM film WHERE dateFilm <= dd;
RETURN moy;
END moyenneDuree;
Fonctions stockées
65
Ecrire une fonction qui retourne la somme du salaire et des
commissions d’un employé dont le numéro est spécifié comme
paramètre
CREATE OR REPLACE FUNCTION SALAIRE_COMMISSION
(v_num IN [Link]%type)
RETURN number IS
v_resultat [Link]%type;
BEGIN
SELECT sal + nvl(comm, 0) INTO v_resultat
FROM EMP
WHERE empno = v_num;
Return (v_resultat);
END;
/
Fonctions stockées
66
Pour tester une fonction avec une requête select :
select SALAIRE_COMMISSION(7369) from dual;
Ou créer un bloc PL/SQL dans lequel on fait appel à la
fonction :
DECLARE
v_sal number;
BEGIN
v_sal := SALAIRE_COMMISSION(7369);
DBMS_OUTPUT.PUT_LINE('Salaire : '|| v_sal);
END;
/
Suppression d’une fonction :
DROP FUNCTION nom_fonction;
Packages
67
Un package est un ensemble de procédures, de
fonctions, de variables, de constantes, de curseurs et
d’exceptions stockés dans la base de données
Oracle.
Un package ne peut être appelé ni paramétré.
Pour concevoir un package, on doit créer une
spécification et un corps.
Packages
68
Deux parties distinctes dans un package (chaque
partie doit être créée et compilée séparément) :
La partie déclaration ou spécification qui contient la
déclaration des procédures, fonctions et variables, …
La partie corps, ou body, qui contient la définition des
procédures ou fonctions de type public déclarées dans
la partie spécification ainsi que les déclarations de
procédures ou fonctions de type privé
Packages - Spécification
69
CREATE [OR REPLACE] PACKAGE
nom-package IS
[déclaration-de-variable;]
[déclaration-de-curseur;]
[déclaration-de-procédure;]
[déclaration-de-fonction;]
[déclaration-d’exception;]
END nom-package;
Packages - Définition
70
CREATE [OR REPLACE] PACKAGE BODY
nom-package IS
[définition-de-variable;]
[définition-de-curseur;]
[définition-de-procédure;]
[définition-de-fonction;]
[définition-d’exception;]
END nom-package;
Package - Exercice
71
Créer un package (pack1) contenant :
une variable global initialisée à 10
une fonction (fonct1) avec 1 paramètre en entrée (no
employé) et retourne le salaire de l’employé
un curseur (cur_max) qui ramène le salaire maximal de
tous les employés
une fonction (fonct2) qui retourne le salaire maximal de
tous les employés en utilisant le curseur.
une procédure (pro1) avec 1 paramètre en entrée (le
no employé) et 2 paramètres en sortie le nom et le
salaire de l’employé.
Packages - Spécification
72
CREATE OR REPLACE PACKAGE pack1 IS
p_global number := 10;
FUNCTION fonct1(p_num IN number) RETURN number;
CURSOR cur_max IS SELECT max(sal) FROM EMP;
FUNCTION fonct2 RETURN number;
PROCEDURE proc1(p_num IN number,
p_nom OUT varchar2, p_sal OUT number);
END;
Packages - BODY
73
CREATE OR REPLACE PACKAGE BODY pack1 IS
FUNCTION fonct1(p_num IN number) RETURN number IS
v_sal [Link]%type;
begin
select sal into v_sal from emp where empno = p_num;
return v_sal;
end;
FUNCTION fonct2 RETURN number IS
v_max number;
begin
OPEN cur_max;
FETCH cur_max INTO v_max;
CLOSE cur_max;
p_global := v_max;
Return v_max;
end;
Packages - Spécification
74 FUNCTION SALAIRE_COMMISSION (v_num IN [Link]%type)
RETURN number IS
v_resultat [Link]%type := 0;
BEGIN
SELECT sal + nvl(comm, 0) INTO v_resultat
FROM EMP
WHERE empno = v_num;
Return (v_resultat);
END;
PROCEDURE proc1(p_num IN number,
p_nom OUT varchar2, p_sal OUT number) IS
begin
select ename into p_nom
from emp where empno = p_num;
p_sal := SALAIRE_COMMISSION(p_num);
exception
when others then p_nom := null; p_sal := null;
end;
END;
/
Packages - Spécification
75
Exécution d’une fonction d’un package :
select pack1.fonct1(7902) from dual;
Exécution d’une procédure d’un package :
declare
v_nom [Link]%type;
v_salaire number;
begin
pack1.proc1 (7902, v_nom, v_salaire);
dbms_output.put_line(v_nom||'-'||v_salaire);
end;
Affichage d’une variable globale d’un package
begin
dbms_output.put_line(pack1.p_global);
end;
Packages - Spécification
76
Suppression du package body :
DROP PACKAGE BODY nom_package;
Suppression spécification du package :
DROP PACKAGE nom_package;
TRIGGERS
2022-2023 Enis BELHASSEN ([Link]@[Link])
Triggers en PL/SQL
78
Un trigger est un programme PL/SQL exécuté quand un
événement arrive : INSERT, UPDATE ou DELETE
La forme générale d'un trigger est :
CREATE [OR REPLACE] TRIGGER nom_trigger
{BEFORE | AFTER } INSERT ou UPDATE ou DELETE
ON table_mis_à_jour
[ ... options supplémentaires...]
[DECLARE
définition_variables_constantes]
BEGIN
instructions
[EXCEPTION
programmation_exceptions]
END;
Triggers en PL/SQL
79
Un trigger est activé dès sa création
Commande pour désactiver un trigger :
ALTER TRIGGER nom_trigger DISABLE;
Commande pour activer un trigger :
ALTER TRIGGER nom_trigger ENABLE;
Commande pour supprimer un trigger :
DROP TRIGGER nom_trigger;
Triggers en PL/SQL - after insert
80
Afficher un message après l'insertion d'un employé
create or replace trigger monTrigger1
after insert on emp
begin
dbms_output.put_line('OK');
end;
Test avec une requête insert :
SQL> insert into emp (empno) values (123);
OK <-- message affiché par le trigger
1 ligne crÚÚe. <-- message affiché par Oracle (SQLPlus)
Triggers en PL/SQL - after update
81
Afficher un message après la mise à jour d'un employé
create or replace trigger monTrigger2
after update on emp
begin
dbms_output.put_line('MAJ OK');
end;
Test avec une requête update :
SQL> update emp set sal=sal*1.2 where deptno=10;
MAJ OK
3 lignes mises Ó jour.
Triggers en PL/SQL - after update
82
Afficher un message après la mise à jour d’un attribut spécifié
create or replace trigger monTrigger2
after update of sal on emp
begin
dbms_output.put_line('Update Salaire');
end;
Test avec des requêtes update :
SQL> update emp set sal = sal*1.2 where deptno = 10;
Update Salaire <-- Mise à jour du salaire : trigger exécuté
3 lignes mises Ó jour.
SQL> update emp set comm = 0 where deptno = 20;
5 lignes mises Ó jour.<-- Mise à jour comm : trigger non exécuté
Triggers en PL/SQL - FOR EACH ROW
83
Une fois lancé, un trigger peut exécuter son code pour chaque
tuple touché par la mise à jour : utiliser la clause FOR EACH ROW
create or replace trigger monTrigger2
after update of sal on emp for each row
begin
dbms_output.put_line('Update Salaire');
end;
/
Test avec une requête update :
SQL> update emp set sal = sal*1.2 where deptno = 10;
Update Salaire <-- Message affiché pour chaque ligne màj
Update Salaire
Update Salaire
3 lignes mises Ó jour.
Triggers en PL/SQL - FOR EACH ROW
84
On peut accéder aux valeurs des champs avant et après la mise
à jour : valeurs préfixées par OLD et NEW
create or replace trigger monTrigger2
after update of sal on emp for each row
begin
dbms_output.put_line(:[Link]||' - Nouveau Sal: '||
:[Link]||' - Ancien Sal : ' || :[Link]);
end;
/
Test avec une requête update :
SQL> update emp set sal = sal*1.2 where deptno = 10;
KING - Nouveau Sal: 6000 - Ancien Sal : 5000
CLARK - Nouveau Sal: 2940 - Ancien Sal : 2450
MILLER - Nouveau Sal: 1560 - Ancien Sal : 1300
3 lignes mises Ó jour.
Triggers en PL/SQL - FOR EACH ROW
85
On peut filtrer sur quels tuples sera exécuté le corps du trigger :
clause WHEN
create or replace trigger monTrigger2
after update of sal on emp for each row
when ([Link] < 3000)
begin
dbms_output.put_line(:[Link]||' - Nouveau Sal: '||
:[Link]||' - Ancien Sal : ' || :[Link]);
end;
/
Test avec une requête update :
SQL> update emp set sal = sal*1.2 where deptno = 10;
CLARK - Nouveau Sal: 3528 - Ancien Sal : 2940
MILLER - Nouveau Sal: 1872 - Ancien Sal : 1560
3 lignes mises Ó jour.