Procédures stockées PL/SQL
Une procédure est simplement un programme PL/SQL nommé, compilé et stocké dans la base. On trouvera ci après le code de la plus petite procédure PL/SQL au monde 😉
SQL> CREATE OR REPLACE PROCEDURE proc_mini IS BEGIN NULL; END; / Procedure cree.
Pour être un peu plus précis, ce code est une commande SQL, qui crée la procédure PL/SQL, et donc compile et stocke dans la base le bloc PL/SQL compris entre le BEGIN et le END, en le référencant par ‘proc_mini’.
Et pour exécuter cette procédure de manière autonome :
SQL> EXECUTE proc_mini Procedure executee avec succes.
Voici le célèbre hello.pl (libre et originale adapdation du hello.c) ici sous forme de procédure stockée.
Un exemple un petit peu moins minimaliste, de procédure avec paramètre est donné ci-après :
PROCEDURE cree_client (p_nom VARCHAR2, p_ville VARCHAR2) IS BEGIN INSERT INTO clients (no_cli, nom_cli, ville_cli) VALUES (seq_noclient.NEXTVAL, p_nom, p_ville); COMMIT ; END;
Fonctions PL/SQL
Une fonction est une procédure retournant une valeur.
Globalement une fonction aura la forme suivante
CREATE OR REPLACE FUNCTION nom_fonction (nom_parametre type_parametre) RETURN type_du_retour IS declaration variable locales; BEGIN ... RETURN variable_resultat END; /
Exemple
CREATE OR REPLACE FUNCTION solde (no INTEGER) RETURN REAL IS le_solde REAL; BEGIN SELECT solde INTO le_solde FROM clients WHERE no_cli = no; RETURN le_solde; END; /
On peut par exemple appeler cette fonction et utiliser son résultat au sein d’un ordre SQL :
SQL> SELECT solde(1000) FROM dual ; Solde(1000) ----------- 12024,50
l’appel ‘bête’ de l’exécution d’une fonction comme si c’était une procédure (ouh la la !) ne fonctionne évidemment pas! mais on est souvent tenté de le faire quand même.
Soit la fonction stockée mdp() qui accepte un paramètre numérique. SI on l’appelle sans prendre garde au fait que c’est une fonction, on obtient :
SQL> execute mdp(2); BEGIN mdp(2); END; * ERREUR a la ligne 1 : ORA-06550: line 1, column 7: PLS-00221: 'MDP' is not a procedure or is undefined ORA-06550: line 1, column 7: PL/SQL: Statement ignored
Gestion des droits et programmes stockés
Pour pouvoir créer une procédure stockée (ou une fonction…c’est le même droit pour les deux) il faut le droit ‘CREATE PROCEDURE’.
Si l’on n’a pas ce privilège, voila ce qui se passe:
SQL> connect SCOTT/tiger Connected. SQL> SQL> CREATE PROCEDURE test IS 2 BEGIN 3 NULL; 4 END; 5 . SQL> / CREATE PROCEDURE test * ERROR at line 1: ORA-01031: insufficient privileges
Il faut alors que le DBA donne le droit explicitement à l’utilisateur (ici SCOTT) de créer des procedures:
SQL> CONNECT SYSTEM/ORACLE SQL> GRANT CREATE PROCEDURE TO SCOTT; SQL> CONNECT SCOTT/TIGER SQL> CREATE OR REPLACE PROCEDURE test 2 IS 3 BEGIN 4 NULL; 5 END; 6 . SQL> / Procedure created.
Par défaut le créateur d’un programme stocké a tous les droits sur ce programme:
- Compilation (ALTER no)m_proc COMPILE;
- execution (EXECUTE nom_proc)
- suppression (DROP PROCEDURE nom_proc)
un autre utilisateur (hormis le DBA bien sûr) n’a aucun droit par défaut.
Ainsi si un utilisateur U1 crée une procédure P, le droit EXECUTE doit explicitement être donné à U2 pour qu’il puisse exécuter P (soit par U1 soit par le DBA:
SQL> GRANT EXECUTE ON p TO u2;
Une procédure peut s’exécuter de 2 façons différentes au niveau des droits:
- avec les privilèges du créateur de la procédure (DEFINER’s rights)
- avec les privilèges de l’utilisateur / exécuteur de la procédure (INVOKER’s Rights)
Packages PL/SQL
Un package est un module de programmes incluant procédures et / ou fonctions fonctionnellement dépendantes.
Un package est composé de 2 parties :
– la spécification (introduite par ‘CREATE PACKAGE’) liste les entêtes de procédures et fonctions contenues dans le package,
– le corps du package (introduit par ‘CREATE PACKAGE BODY’) qui contient le code effectif des procédures et fonctions déclarées précédemment.
exemple
SQL> CREATE PACKAGE clients AS -- spécifications du package PROCEDURE insere_client (no INTEGER, nom VARCHAR2, ...); PROCEDURE supprime_client (no INTEGER); ... END; /
SQL> CREATE PACKAGE BODY clients AS -- le corps du package PROCEDURE insere_client (no INTEGER, nom VARCHAR2, ...) IS BEGIN ... INSERT INTO clients VALUES (no, nom, ...); END;
PROCEDURE supprime_client (no INTEGER) IS BEGIN DELETE FROM clients WHERE no_cli = no; END; ... END; -- du package /
Triggers
Un trigger est un morceau de code PL/SQL, stocké dans la base, déclenché lors de l’occurrence d’un événement particulier. Ils permettent notamment de synchroniser des opérations entre plusieurs tables.
On s’intéressera ici principalement aux triggers au niveau TABLE
Avantages et inconvénients des triggers
A l’instar des contraintes d’intégrité, le code des triggers est centralisé dans la base de données, et sera exécuté quel que soit l’outil client (sql developer, application métier, sqlplus). Le comportement sera donc le même partout, et ne dépendra pas du programme client, de plus il ne pourra pas être contourné.
L’inconvénient principal du trigger est qu’il consomme des ressources. Ces dernières peuvent augmenter les temps de traitement, lors dse mises à jour, a fortiori si elles sont en battch et massives.
Note: On est parfois tenté de créer des triggers pour implémenter certaines règles de gestion. La plupart du temps les contraintes remplissent plus efficacement ce rôle.
Principes généraux des triggers
La plupart du temps les triggers sont déclenchés par la modification du contenu d’une table. La liste des événements déclencheurs apparaît ci-après :
| Type d’ordre | ordre déclencheur |
| LMD | DELETE, INSERT, UPDATE |
| LDD / Base de données | CREATE, ALTER, DROP |
| Base de données | SERVERERROR, LOGON, LOGOFF, STARTUP, SHUTDOWN |
Les ordres du LDD et de la gestion de transaction (CREATE, ALTER, DROP, COMMIT, SAVEPOINT, sont interdits pour les triggers autres que base de données (triggers système).
Un déclencheur s’exécute dans le cadre d’une transaction. Il ne peut donc pas contenir d’instruction COMMIT ou ROLLBACK ou toute instruction générant une fin de transaction implicite (ordre DDL)
Syntaxe générale
Create [Or Replace] Trigger <Nom Trigger> [ Before After] <Opération DML> On <Nom Table> [ For Each Row ] [When <Condition >] Declare Begin Exception End ; /
rem: Le corps du trigger est défini par un bloc PL/SQL anonyme
[FOR EACH ROW] – Si est précisé, indique que le trigger est déclenché pour chaque ligne modifiée. Dans le cas contraire, n’est déclenché qu’une seule fois.
Le bloc PL/SQL qui constitue le trigger peut être exécuté avant ou après la vérification des contraintes d’intégrité. Il peut être exécuté pour chaque ligne affectée par l’ordre DML ou bien une seule fois pour la commande.
- BEFORE : le code dans le corps du triggers s’exécute avant les événements de déclenchement LMD
- AFTER : le code dans le corps du triggers s’exécute avant les événements de déclenchement LMD
Seules les valeurs des colonnes de la ligne courante sont accessibles. Oracle utilise deux variables de type enregistrement OLD et NEW.
OLD stocke la valeur avant mise à jour (et n’a bien sûr pas de sens pour un ordre INSERT)
NEW stocke la nouvelle valeur (et n’a bien sûr pas de sens pour un ordre…DELETE)
Ces deux variables pourront être utilisées dans la clause WHEN du trigger et dans la section exécutable. Dans cette dernière , elle seront prfixées par un opérateur ‘.’
La clause WHEN (condition) permet également de restreindre le champ du trigger en imposant une condition.
Exemple
Créons un déclencheur très basique qui ne fait qu’afficher le numéro et le nom d’un employé que l’on veut supprimer de la table EMP
SQL> CREATE OR REPLACE TRIGGER TRG_BDR_EMP 2 BEFORE DELETE -- avant supression 3 ON EMP -- sur la table EMP 4 FOR EACH ROW -- pour chaque ligne 5 Declare 6 LC$Chaine VARCHAR2(100); 7 Begin 8 dbms_output.put_line( 'Suppression de l''employé n° ' || To_char( :OLD.empno ) 9 || ' -> ' || :OLD.ename ) ; 10 End ; 11 / Déclencheur créé.
Supprimons maintenant un employé
SQL> set serveroutput on SQL> delete from emp where empno = 7369 Suppression de l'employé n° 7369 -> SMITH 1 ligne supprimée. SQL> rollback; Annulation (rollback) effectuée.
La DRH annonce que désormais, tout nouvel employé devra avoir un numéro supérieur ou égal à 10000. Il faut donc interdire toute insertion qui ne reflète pas cette nouvelle directive
SQL> CREATE OR REPLACE TRIGGER TRG_BIR_EMP 2 BEFORE INSERT -- avant insertion 3 ON EMP -- sur la table EMP 4 FOR EACH ROW -- pour chaque ligne 5 Begin 6 If :NEW.empno < 10000 Then 7 RAISE_APPLICATION_ERROR ( -20010, 'Numéro employé inférieur à 10000' ) ; 8 End if ; 9 End ; 10 / Déclencheur créé.
Voir un autre exemple avec FOR EACH ROW et sans: historqiue dse modifs de salaire détaillé (avec lignes/noms concernés et simple alerte d’une modif
cf: http://plsql-tutorial.com/plsql-triggers.htm
Manipulation des triggers
- Activer ou désactiver un Trigger: Alter Trigger <Nom_Trigger> [ Enable |Disable ] ;
- Supprimer un Trigger: Drop Trigger <Nom Trigger >;
- Déterminer les triggers de votre BD: Select Trigger_Name From User_triggers


