PL/SQL — procédures, curseurs, exceptions
Du code stocké et appelable, des curseurs pour parcourir des résultats, et un gestionnaire d'erreurs robuste. Le PL/SQL qui rentre vraiment dans le moteur.
1. Procédures et fonctions stockées
- Déporter les traitements de manipulation vers le SGBD.
- Ensemble d'instructions procédurales + requêtes SQL.
- On les dit procédures/fonctions stockées.
- Traitement identique et commun à tous les applicatifs.
- Charge supplémentaire pour le SGBD (à arbitrer).
Une fonction renvoie une valeur ; une procédure exécute un effet.
Déclaration
CREATE [OR REPLACE] PROCEDURE nom_procédure (paramètres)
IS
<bloc de variables>
BEGIN
<bloc de traitements>
END;
CREATE [OR REPLACE] FUNCTION nom_fonction (paramètres)
RETURN type
IS
<bloc de variables>
BEGIN
<bloc de traitements>
RETURN <result>;
END;
Paramètres — trois modes
| Mode | Sens | Usage |
|---|---|---|
IN | Entrée (lecture seule) | Valeur reçue par la procédure |
OUT | Sortie | Valeur renvoyée par paramètre |
IN OUT | Entrée + sortie | Modifié sur place |
CREATE OR REPLACE PROCEDURE nom_procédure (
p_1 IN <type1> DEFAULT <valeur>,
p_2 OUT <type2>,
p_3 IN OUT <type3>)
IS
<bloc de variables>
BEGIN
<bloc de traitements>
END;
NUMBER, VARCHAR2, et non pas NUMBER(6,2) ou VARCHAR2(50).
-- BON
CREATE OR REPLACE PROCEDURE ma_proc (
p_1 IN NUMBER,
p_2 IN VARCHAR2)
Variables, typage interne
CREATE [OR REPLACE] PROCEDURE nom_procédure (<paramètres>)
IS
v_1 NUMBER;
v_2 <table>.<colonne>%TYPE;
v_3 <table>%ROWTYPE;
TYPE t_rec IS RECORD (a t1, b t2);
v_rec t_rec;
BEGIN
<bloc de traitements>
END;
Appel
-- Dans un bloc PL/SQL
BEGIN
v_res := f_nom(args);
proc_nom(args);
END;
-- Depuis SQL*Plus
SQL> SELECT f_nom(args) FROM DUAL;
SQL> EXEC proc_nom(args);
Fonctions natives utiles
| Catégorie | Exemples |
|---|---|
| Chaînes | concat (||), trim, length, substr |
| Numériques | ceil, floor, round, trunc, sqrt, avg, max |
| Date | to_date, to_char, day, month, year, next_day |
| Transformation | decode, coalesce, nvl |
2. Curseurs
SELECT. Il contient les données de la requête, ligne par ligne.
Deux types de déclaration :
- implicite :
SELECT … INTO … FROM …;(résultat unique) - explicite :
CURSOR c IS …;avec OPEN / FETCH / CLOSE (résultat multiple)
Curseurs implicites
BEGIN
-- SELECT INTO direct (1 seule ligne attendue)
SELECT nom, prenom INTO v_nom, v_prenom
FROM clients
WHERE id = p_id_client;
-- ou via SQL dynamique
EXECUTE IMMEDIATE r_req INTO v_res USING v_num;
END;
Curseurs explicites — cycle de vie
Cycle de vie d'un curseur explicite : OPEN → FETCH en boucle → CLOSE.
CREATE OR REPLACE PROCEDURE nom_procédure
IS
CURSOR cur IS
SELECT * FROM table;
v_item cur%ROWTYPE;
BEGIN
OPEN cur; -- ouverture
LOOP
FETCH cur INTO v_item; -- récupère ligne
EXIT WHEN cur%NOTFOUND; -- fin du parcours
<traitements>
END LOOP;
CLOSE cur; -- fermeture !
END;
Pseudo-attributs d'un curseur
| Attribut | Sens |
|---|---|
cur%FOUND | Vrai si un enregistrement a été récupéré au dernier FETCH |
cur%NOTFOUND | Vrai si aucun enregistrement n'a été récupéré (fin du parcours) |
cur%ISOPEN | Vrai si le curseur est ouvert |
cur%ROWCOUNT | Nombre de FETCH effectués |
Parcours automatique — FOR cursor LOOP
Le
FOR ouvre, fetch et ferme automatiquement. Pas d'oubli de CLOSE, pas de gestion manuelle.
-- Curseur déclaré puis parcouru avec FOR
CREATE OR REPLACE PROCEDURE nom_procédure
IS
CURSOR cur IS
SELECT … FROM …;
BEGIN
FOR v_ligne IN cur LOOP
<traitements>
END LOOP;
END;
-- Curseur implicite (FOR sur SELECT direct)
BEGIN
FOR v_ligne IN (SELECT … FROM …) LOOP
<traitements>
END LOOP;
END;
Curseur paramétré
CREATE OR REPLACE PROCEDURE nom_procédure
IS
CURSOR cur(p_num NUMBER) IS
SELECT * FROM table WHERE no = p_num;
BEGIN
OPEN cur(12);
…
END;
3. Transactions PL/SQL
Permettent d'isoler un ensemble de modifications sur les données ; vérifient les propriétés ACID (revoir CM3).
Les 4 composantes principales
SAVEPOINT nom; | Pose un point de sauvegarde |
COMMIT; | Valide la transaction |
ROLLBACK; | Annule la transaction (revient au dernier état stable connu) |
SET TRANSACTION [READ ONLY|READ WRITE] [NAME 'tr_nom'] | Démarre une transaction nommée |
BEGIN
INSERT INTO …;
INSERT INTO …;
INSERT INTO …;
INSERT INTO …;
ROLLBACK; -- Annule les 4 précédentes
INSERT INTO …;
INSERT INTO …;
SAVEPOINT icionsauve;
INSERT INTO …;
INSERT INTO …;
ROLLBACK TO icionsauve; -- Annule les 2 dernières
END;
BEGIN
SET TRANSACTION NAME 'tr_nom';
DELETE FROM …;
INSERT INTO …;
UPDATE …;
COMMIT;
END;
Transaction autonome — PRAGMA AUTONOMOUS_TRANSACTION
- Une procédure stockée / fonction devrait ne pas réaliser de validation. Charge au code appelant.
- Mais on peut créer une transaction autonome via
PRAGMA AUTONOMOUS_TRANSACTIONqui s'engage indépendamment de la transaction appelante.
CREATE OR REPLACE PROCEDURE nom_procédure
IS
PRAGMA AUTONOMOUS_TRANSACTION;
<bloc variables>
BEGIN
<traitements>
COMMIT; -- Commit indépendant de la transaction appelante
END;
Journaliser une erreur dans une table d'audit même si la transaction métier rollback. Le log persiste ; le métier n'est pas affecté.
4. Gestion des exceptions
Bloc gestion d'erreurs
BEGIN
<traitements à réaliser>
EXCEPTION
WHEN ex1 THEN …
WHEN ex2 THEN …
WHEN OTHERS THEN … -- fourre-tout
END;
Exceptions classiques Oracle
| Exception | Code | Cause |
|---|---|---|
DUP_VAL_ON_INDEX | ORA-00001 | Doublon sur clé primaire / unique |
NO_DATA_FOUND | ORA-01403 | SELECT INTO sans résultat |
TOO_MANY_ROWS | ORA-01422 | SELECT INTO avec plusieurs résultats — il faut un curseur |
ZERO_DIVIDE | ORA-01476 | Division par zéro |
INVALID_NUMBER | ORA-01722 | Conversion en nombre impossible |
Récupérer code et message d'erreur
BEGIN
INSERT INTO …;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('Erreur n° '||SQLCODE||' - '||SQLERRM);
UPDATE …;
END;
SQLCODE= code d'erreur Oracle (ORA-xxxxx)SQLERRM= message d'erreur explicite
Créer ses propres exceptions
- Déclarer une variable
EXCEPTION - Lui associer un code via
PRAGMA EXCEPTION_INIT(nom, code) - La lever avec
RAISE - La capturer dans un
WHEN
CREATE OR REPLACE PROCEDURE verifier_age(p_age NUMBER)
IS
e_age_invalide EXCEPTION;
PRAGMA EXCEPTION_INIT(e_age_invalide, -20001);
BEGIN
IF p_age < 18 THEN
RAISE e_age_invalide;
END IF;
DBMS_OUTPUT.PUT_LINE('Âge valide');
EXCEPTION
WHEN e_age_invalide THEN
DBMS_OUTPUT.PUT_LINE('Âge invalide');
END;
Lever une erreur ponctuelle
RAISE_APPLICATION_ERROR(code, message);
-- code ∈ [-20999, -20000] (strictement < -20000)
Exception levée puis gérée par une procédure appelante
-- Côté procédure qui lève
c_num_monexc CONSTANT NUMBER := -20042;
ex_monexception EXCEPTION;
PRAGMA EXCEPTION_INIT(ex_monexception, -20042);
BEGIN
…
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(c_num_monexc, 'Erreur id : '||p_id);
END;
-- Côté procédure appelante
BEGIN
…
EXCEPTION
WHEN ex_monexception THEN
DBMS_OUTPUT.PUT_LINE('Je gère l\'exception perso');
END;
5. Packages
- Regroupe des fonctionnalités, traitements au sein d'une même entité.
- Concerne : procédures/fonctions, exceptions, curseurs, variables/constantes.
Architecture en deux parties
Le package sépare l'interface publique (declaration) de l'implémentation (body).
Déclaration (header)
CREATE [OR REPLACE] PACKAGE nom_pkg
IS
[déclaration des variables/constantes;]
[déclaration des exceptions;]
[déclaration de procédures;]
[déclaration de fonctions;]
…
END nom_pkg;
Body
CREATE [OR REPLACE] PACKAGE BODY nom_pkg
IS
[déclaration de variables (privées);]
[déclaration et corps de procédures;]
[déclaration et corps de fonctions;]
…
END nom_pkg;
Exemple complet
CREATE OR REPLACE PACKAGE pkg_utils IS
c_PI CONSTANT NUMBER := 3.14159;
vg_user VARCHAR2(10);
PROCEDURE logue(p_str IN VARCHAR2);
PROCEDURE init;
FUNCTION rech(p_id IN user.id%TYPE) RETURN user.usr_no%TYPE;
END pkg_utils;
/
CREATE OR REPLACE PACKAGE BODY pkg_utils IS
PROCEDURE logue(p_str IN VARCHAR2) IS
BEGIN
DBMS_OUTPUT.PUT_LINE(p_str);
END logue;
PROCEDURE init IS
BEGIN
SELECT USER INTO vg_user FROM DUAL;
END init;
FUNCTION rech(p_id IN user.id%TYPE) RETURN user.usr_no%TYPE IS
v_no user.usr_no%TYPE;
BEGIN
SELECT usr_no INTO v_no FROM user WHERE id = p_id;
RETURN v_no;
END rech;
END pkg_utils;
/
• Organisation cohérente du code par domaine fonctionnel
• Encapsulation : exposer seulement ce qui est nécessaire
• Variables globales partagées sur la session
• Optimisation : tout le package est chargé en mémoire à la première utilisation
★ Réviser le chapitre
Pour vérifier ta compréhension
Quand préférer une fonction à une procédure ?
Quand on a besoin de renvoyer une valeur exploitable dans une expression — typiquement utilisable dans un SELECT (SELECT ma_fct(x) FROM dual). Si on veut seulement exécuter un effet de bord (modifier la base, écrire un log), on prend une procédure.
Pourquoi écrire NUMBER et non NUMBER(6,2) dans la signature ?
Parce que les types dans la signature d'une procédure/fonction Oracle s'écrivent sans taille. Sinon erreur de compilation. La taille s'applique seulement aux déclarations de variables dans le corps ou aux colonnes de tables.
Différence entre SELECT … INTO et un curseur explicite ?
SELECT … INTO : pour une seule ligne. Si 0 résultat → NO_DATA_FOUND, si plusieurs → TOO_MANY_ROWS. C'est le curseur implicite.
Curseur explicite : pour plusieurs lignes ou quand on veut contrôler finement le parcours (paramètres, attributs %ROWCOUNT, etc.).
Pourquoi utiliser FOR row IN cursor LOOP plutôt que OPEN/FETCH/CLOSE manuel ?
Pour la simplicité : pas d'oubli de CLOSE, pas de gestion de %NOTFOUND. Le FOR ouvre, fetch, ferme tout seul. Préférer cette forme dans 99% des cas — sauf si on a besoin du contrôle fin (sortir prématurément avec EXIT WHEN, lire %ROWCOUNT, etc.).
Quand utiliser PRAGMA AUTONOMOUS_TRANSACTION ?
Quand une procédure doit commiter indépendamment de la transaction qui l'appelle. Cas typique : journalisation d'audit. La transaction métier peut être annulée, mais on veut garder la trace de ce qui s'est passé dans la table de logs. La transaction autonome rend ça possible.
Quelle est la différence entre RAISE et RAISE_APPLICATION_ERROR ?
RAISE lève une exception déjà déclarée (ex : RAISE e_age_invalide). Elle peut être capturée par un WHEN e_age_invalide THEN dans le bloc englobant.
RAISE_APPLICATION_ERROR(code, msg) lève une erreur ponctuelle avec un code dans [-20999, -20000] et un message. Idéale pour signaler une erreur au client appelant.
Flashcards éclair
Clique pour retourner.
Procédure vs Fonction
Différence clé.
tourne →Fonction :
RETURN type obligatoire, utilisable dans un SELECT.
3 modes de paramètres
Nomme-les.
tourne →IN · OUT · IN OUT
Types dans signature
Quelle particularité ?
tourne →NUMBER et pas NUMBER(6,2).
Curseur explicite
4 étapes.
tourne →Attributs curseur
Les 4.
tourne →%NOTFOUND
%ISOPEN
%ROWCOUNT
Curseur FOR LOOP
Pattern simplifié.
tourne →FOR v IN cur LOOP
…
END LOOP;
-- open/fetch/close autoCurseur paramétré
Syntaxe.
tourne →CURSOR c(p_id NUMBER) IS
SELECT … WHERE id = p_id;
OPEN c(12);Exceptions classiques
3 à connaître.
tourne →DUP_VAL_ON_INDEX (doublon clé)NO_DATA_FOUND (rien trouvé)TOO_MANY_ROWS (plusieurs lignes)
SQLCODE / SQLERRM
À quoi servent-ils ?
tourne →SQLCODE = code Oracle (ORA-xxxxx)SQLERRM = message d'erreur
Exception perso
3 étapes.
tourne →e EXCEPTION;
PRAGMA EXCEPTION_INIT(e, -20001);
-- puis
RAISE e;RAISE_APPLICATION_ERROR
Plage du code.
tourne →Transaction autonome
Pragma magique.
tourne →PRAGMA AUTONOMOUS_TRANSACTION;Package — 2 parties
Nommer.
tourne →Body (description, implémentation)
Création package
Syntaxe minimale.
tourne →CREATE OR REPLACE PACKAGE p IS
PROCEDURE f;
END p;
/
CREATE OR REPLACE PACKAGE BODY p IS
PROCEDURE f IS BEGIN … END;
END p;Quiz éclair
p_nom IN VARCHAR2(50) est :SELECT … INTO qui ne renvoie aucune ligne lève :EXIT WHEN cur%NOTFOUND;.RAISE_APPLICATION_ERROR ?Pour la feuille recto-verso
- Procédure :
CREATE PROCEDURE nom (p IN type, p OUT type, p IN OUT type) IS … BEGIN … END; - Fonction : ajoute
RETURN typeet instructionRETURN val; - Types SANS taille dans la signature
- Appel :
EXEC proc(args)·SELECT fct(args) FROM dual· ou direct dans un bloc PL/SQL - Curseur implicite :
SELECT … INTO v FROM …;(1 ligne uniquement) - Curseur explicite :
CURSOR c IS …;+OPEN c; LOOP FETCH c INTO v; EXIT WHEN c%NOTFOUND; … END LOOP; CLOSE c; - Curseur FOR :
FOR row IN cursor LOOP … END LOOP;(open/close auto) - Curseur paramétré :
CURSOR c(p NUMBER) IS …;·OPEN c(12); - Attributs :
%FOUND,%NOTFOUND,%ISOPEN,%ROWCOUNT - Transaction :
COMMIT;·ROLLBACK [TO sp];·SAVEPOINT sp;·SET TRANSACTION NAME 'tr'; - Transaction autonome :
PRAGMA AUTONOMOUS_TRANSACTION;+COMMIT;en fin - Bloc EXCEPTION :
EXCEPTION WHEN ex1 THEN … WHEN OTHERS THEN … - Exceptions classiques :
DUP_VAL_ON_INDEX·NO_DATA_FOUND·TOO_MANY_ROWS - Codes :
SQLCODE(numéro) ·SQLERRM(message) - Créer exception :
e EXCEPTION; PRAGMA EXCEPTION_INIT(e, -20001);·RAISE e; RAISE_APPLICATION_ERROR(code, msg)· code ∈ [-20999, -20000]- Package = déclaration (public) + body (implémentation)
CREATE PACKAGE pkg IS … END pkg;puisCREATE PACKAGE BODY pkg IS … END pkg;