PL/SQL Procedural Language Extensions to SQL Il permet : - l'utilisation d'un sous-ensemble du langage SQL, - la mise en œuvre de structures procédurales, - la gestion des erreurs. L'une des plus importantes caractéristiques de PL/SQL est la possibilité de manipuler les données lignes par lignes. Basé sur ADA
Bloc PL/SQL Section en-tête nécessaire pour les blocs nommés (procédures, fonctions, paquetages et triggers). Section déclaration, mot clef DECLARE, description des types et variables utilisées dans le bloc. Facultative. Section corps du bloc PL/SQL, mot clef BEGIN, instructions du programme et éventuellement la section de traitements des erreurs. Obligatoire. Elle se termine par le mot clef END. Section traitements des erreurs, mot clef EXCEPTION, contient les instructions de gestion des erreurs. Facultative.
En-tête de bloc nommé : PROCEDURE nom [(liste de paramètres)] IS AS FUNCTION nom [(liste de paramètres)] RETURN type IS AS DECLARE {déclaration des variables, des constantes, des exceptions et des curseurs} BEGIN {instructions SQL, PL/SQL} EXCEPTION {traitement des erreurs} END [nom bloc]
Variables : section DECLARE nombre, caractère, date et valeurs booléennes variable par référence (%TYPE) nom_variable nom_relation.nom_attribut%type DECLARE emp_id EMP.empno%TYPE new_emp emp_id%type BEGIN dictionnaire de données table EMP empno NUMBER(5) enom VARCHAR2(30) embauche DATE
Enregistrements 1. Déclaration du type TYPE nom_type IS RECORD (nom_champ 1 type_champ 1, nom_champ 2 type_champ 2,...); nom_variable nom_type; 2. Par référence à une structure de table nom_variable nom_relation%rowtype
Tableau C est un vecteur Non borné (pas de limite prédéfinie au nombre de ligne) Éléments homogènes Indexés par des entiers TYPE tableau IS TABLE OF datatype [NOT NULL] INDEX BY BINARY_INTEGER; mon_tableau tableau; datatype = type scalaire à partir de la v.2.3 on autorise le type enregistrement
Le corps du programme Les sections corps et traitements des erreurs peuvent comporter des instructions SQL : - les requêtes de mise à jour : INSERT, DELETE et UPDATE - les requêtes d'interrogation : SELECT... (tout est permis) - les commandes de gestions des transactions : COMMIT, ROLLBACK des instructions d'affectation (:=) des instructions de contrôle itératif et alternatif des instructions de gestion de curseurs des instructions de gestion des erreurs. Chaque instruction se termine par le caractère ";".
Alternatives 3 formes d alternative : IF condition THEN... [ELSIF condition THEN...] [ELSE...] END IF;
Répétitives (1/3) LOOP LOOP...... IF condition pour s arrêter THEN EXIT; EXIT WHEN condition pour s arrêter; END IF;... END LOOP; END LOOP;
Répétitives (2/3) À ne pas déclarer FOR variable_indice IN [REVERSE] min.. max LOOP... END LOOP;
Répétitives (3/3) WHILE condition pour continuer LOOP... END LOOP ;
Les curseurs Un curseur PL/SQL permet de récupérer et de traiter les données de la base dans un programme PL/SQL - ligne par ligne. Dès l'instant où on exécute une instruction SQL, il y a création d'un curseur. Le curseur est une zone de travail ("context-area") de l'environnement utilisateur, qui contient des informations permettant l'exécution d'un ordre SQL (texte source de l'ordre, forme "traduite" de l'ordre, statuts, plan d'exécution...).
Les curseurs statiques Un curseur statique référence toujours le même ordre SQL, connu au moment de la compilation. Dans les curseurs statiques, on distingue : les curseurs implicites créés automatiquement lors d'une requête SQL. Ils sont gérés par le système de façon transparente à l'utilisateur. les curseurs explicites, entièrement gérés par le programme, qui permettent de traiter un à un (et non plus comme un ensemble) les lignes résultat d'une requête SELECT.
Utilisation d un curseur Déclaration d'un curseur Section DECLARE d'un bloc PL/SQL Permet de définir la requête SELECT et de l'associer à un curseur. CURSOR nom_curseur IS requête; Un curseur peut être défini avec des paramètres : CURSOR nom_curseur (nom_paramètre [:= valeur par défaut] [,...]) IS requête;
Exemple 1: CURSOR compagnie_cur IS SELECT compagnie_id FROM compagnie; Exemple 2: CURSOR compagnie_cur (id_in IN number) IS SELECT nom FROM compagnie WHERE compagnie_id = id_in;
Ouverture d un curseur L'ordre OPEN permet - d'allouer un espace mémoire pour le curseur - et de positionner des verrous éventuels OPEN nom_curseur; ou OPEN nom_curseur (paramètres effectifs);
Fermeture d un curseur CLOSE nom_curseur; Libère l espace mémoire Enlève les verrous
Traitement des lignes Les lignes obtenues par la requête SQL sont distribuées une à une par exécution d'un ordre FETCH inclus dans une structure répétitive. FETCH nom_curseur INTO liste_variables;
Requêtes ne renvoyant qu une et une seule ligne SELECT INTO liste de variables FROM... ;
Exemple : DECLARE CURSOR c IS SELECT Nom, Salaire FROM Employé ; v_nom Employé.nom%TYPE ; v_salaire Employé.salaire%TYPE ; BEGIN OPEN c ; LOOP FETCH c INTO v_nom, v_salaire; EXIT WHEN (c%notfound); Traitement END LOOP; CLOSE c; END;
Boucle FOR de type curseur Utilisation : dès que l'on doit récupérer et traiter chacun, sans exception, des enregistrements d'un curseur. FOR indice_enregistrement IN nom_curseur LOOP... END LOOP; OPEN ET CLOSE sont implicites.
Exemple : DECLARE CURSOR c IS SELECT Nom, Salaire FROM Employé ; v_nom Employé.nom%TYPE ; v_salaire Employé.salaire%TYPE ; BEGIN FOR i IN c LOOP v_nom := i.nom ; v_salaire := i.salaire ; traitement ; END LOOP ; END ;
Statuts d un curseur %FOUND Vrai si le dernier FETCH a pu obtenir une nouvelle ligne résultat %NOT FOUND Vrai si le dernier FETCH n a pas pu obtenir une nouvelle ligne résultat %ISOPEN Vrai si le curseur est ouvert %ROWCOUNT renvoie le nombre de lignes traitées par l'ordre SQL il évolue à chaque ligne distribuée Un statut est associé à un curseur par curseur implicite SQL%... curseur explicite nom_curseur%...
Modification des données CURRENT OF nom_curseur Permet de modifier ou de détruire la ligne distribuée par FETCH FOR UPDATE [OF nom_attribut, ] Gestion des verrous exclusifs
DECLARE CURSOR c IS SELECT Nom, Salaire, Commission FROM Employé FOR UPDATE OF Commission; v_nom Employé.nom%TYPE ; v_salaire Employé.salaire%TYPE ; v_commission Employé.commission%TYPE ; BEGIN OPEN c ; LOOP FETCH c INTO v_nom, v_salaire, v_commission; EXIT WHEN (c%notfound); IF v_commission IS NULL THEN UPDATE Employé SET commission = v_salaire * 0.1 WHERE CURRENT OF c; END IF; END LOOP; CLOSE c; COMMIT; END;
Gestion des erreurs Le but est d'affecter un traitement approprié aux erreurs qui apparaissent lors de l'exécution d'un bloc PL/SQL. 2 types d erreurs : - systèmes - utilisateurs
Les erreurs systèmes Détectées par le moteur PL/SQL. BEGIN... dès que l'erreur système est rencontrée, passage automatique à la section EXCEPTION pour traiter l'erreur EXCEPTION WHEN nom_erreur 1 THEN traitement erreur 1 WHEN nom_erreur 2 THEN traitement erreur 2 WHEN OTHERS THEN traitement des autres erreurs. END;
Erreurs utilisateurs 1/2 La résolution s'effectue en trois étapes : 1. Définir l'erreur dans la section DECLARE 2. Définir la procédure de traitement de l'erreur dans la section EXCEPTION 3. Déclencher la détection d'erreurs : une erreur utilisateur doit être explicitement déclenchée par l'ordre RAISE nom_erreur
Erreurs utilisateurs 2/2 DECLARE nom_erreur EXCEPTION; BEGIN... IF (anomalie) THEN RAISE nom_erreur; END IF;... EXCEPTION WHEN nom_erreur THEN... END;
Reprise sur exception Après exécution de la procédure d erreur, l exécution revient au bloc de niveau immédiatement supérieur à celui qui contient la procédure EXCEPTION. Un seul bloc = sortie du programme Solution = encapsulation dans un «bloc virtuel»
Unités de programmes nommées procédures & fonctions CREATE PROCEDURE nom_procédure [(nom_paramètre MODE type_données [:=value],...)] IS AS CREATE FUNCTION nom_fonction [(nom_paramètre MODE type [ := value,...)] RETURN type IS AS MODE := IN le programme ne peut que lire le paramètre OUT le programme ne peut qu écrire le paramètre IN OUT le programme peut lire et écrire le paramètre
Curseurs dynamiques Les instructions ne sont déterminées qu au moment de l exécution. Implémentés grâce au paquetage DBMS_SQL CREATE PROCEDURE drop_table (table_nom IN VARCHAR2) AS cid INTEGER; BEGIN -- Ouverture d un curseur et on retourne son identifiant cid := DBMS_SQL.OPEN_CURSOR ; DBMS_SQL.PARSE(cid, DROP TABLE table_nom, dbms_sql.v7) ; DBMS_SQL.CLOSE_CURSOR(cid) ; -- On a ici un curseur dynamique EXCEPTION WHEN OTHERS THEN DBMS_SQL.CLOSE_CURSOR(cid) ; END drop_table;
Triggers (déclencheurs) Leur utilité Gestion de l intégrité des données Contrôle des mises à jour des données Table journal Champs dérivés Audit complexe Duplication de données (réplication synchrone asymétrique)
Triggers (déclencheurs) 1/8 Le principe Modèle basé sur la séquence Evénement-Condition-Action : 1. Un trigger est déclenché par un événement, spécifié par le programmeur, en général une insertion, une destruction ou une modification de table. 2. La première action d un trigger est de tester une condition (si elle existe) : si cette condition ne s évalue pas à TRUE, l exécution s arrête. 3. Enfin, l action proprement dite qui est un programme PL/SQL pour Oracle
Triggers (déclencheurs) 2/8 Caractéristiques Un trigger peut être exécuté : - une fois pour un seul ordre SQL - ou à chaque ligne concernée par cet ordre. L action déclenchée peut intervenir - avant l événement - ou après l événement.
Trigger par ordre 3/8 CREATE TRIGGER nom_du_trigger BEFORE AFTER INSERT UPDATE DELETE ON nom de la table [WHEN condition] Bloc PL/SQL
Trigger par ligne 4/8 CREATE TRIGGER nom du trigger BEFORE AFTER INSERT UPDATE DELETE ON nom de la table FOR EACH ROW [WHEN condition] Bloc PL/SQL
Test sur l évolution de la base 5/8 Possibilité de manipuler les valeurs ancienne (:OLD) et nouvelle (:NEW) de la donnée modifiée CREATE TRIGGER trig1 AFTER INSERT ON T1 FOR EACH ROW WHEN (NEW.a <=10) PAS de : BEGIN INSERT INTO T2 VALUES(:NEW.b, :NEW.a); END;
Prédicats de déclencheurs 6/8 INSERTING UPDATING - DELETING CREATE TRIGGER emp_log_t AFTER INSERT OR UPDATE ON empl FOR EACH ROW DECLARE dmltype CHAR(1) BEGIN IF INSERTING THEN dmltype := I ; INSERT INTO emp_log (empl_no, who, operation) VALUES(:NEW.empl_no, USER, dmltype); ELSE dmltype := U ; INSERT INTO emp_log (empl_no, who, operation) VALUES(:NEW.empl_no, USER, dmltype); END IF; END;
Triggers INSTEAD OF 7/8 Pour réaliser des opérations à travers une vue CREATE TRIGGER nom du trigger INSTEAD OF INSERT UPDATE DELETE ON nom de la vue Oracle déclenche le trigger au lieu d exécuter l ordre sur la vue.
Triggers INSTEAD OF exemple 8/8 Customers_sj(cust, address, credit, location) Customers_pa(cust, address, credit, location) CREATE VIEW all_custormes AS SELECT * from Customers_sj union SELECT * from Customers_pa; -- vue non modifiable CREATE TRIGGER t INSTEAD OF INSERT ON all_customers FOR EACH ROW BEGIN IF (:NEW.location = San José ) THEN INSERT INTO Customers_sj VALUES (:NEW.cust, :NEW.address, :NEW.credit,:NEW. location); ELSE INSERT INTO Customers_pa VALUES (:NEW.cust, :NEW.address, :NEW.credit,:NEW. location); END;
Entrées/Sorties dans un pg PL/SQL package DBMS_OUTPUT CREATE PROCEDURE RIEN AS BEGIN DBMS_OUTPUT.PUT_LINE( Je ne fais rien ); END; / Sous SQLPLUS : SET SERVEROUTPUT ON EXECUTE rien
Bibliographie Oracle PL/SQL guide de programmation Steven Feuerstein Editions O Reilly Oracle PL/SQL précis & concis Steven Feuerstein, Bill Pribyl et Chip Dawes Éditions O Reilly Packages standard de PL/SQL précis & concis Steven Feuerstein, John Beresniewicz et Chip Dawes Éditions O Reilly