Le Langage PL/SQL de Oracle (Brève Introduction)

Najib Tounsi
Ecole Mohammadia d'Ingénieurs, Rabat

Logo EMI
Année 2016/2017
1ère année Génie Informatique http://www.mescours.ma/DB/Polys/SQL/PLSQL/PLSQL.html
Voir aussi PLSQL par l'exemple dans les travaux pratiques

Sommaire

Introduction


Introduction (suite)

 

Introduction (suite)

Introduction (suite)



Structure des blocs PL/SQL


Structure des blocs PL/SQL (suite)

[<Enête de bloc>] 
[DECLARE
<Constantes>
<Variables>
<Cursors>
<Exceptions utilisateurs>]
BEGIN
<Instruction PL/SQL>
[EXCEPTION <Traitement d'exception>]
END;

L'inévitable "Hello world"


SQL>   SET SERVEROUTPUT ON
SQL> BEGIN
2 dbms_output.put_line('Bonjour');
3 END;

4 /

Bonjour

Exemple de Programme

DECLARE
-- Partie optionnelle
 vEname employee.ename%TYPE; vSalary employee.Salary%TYPE;

BEGIN
-- Partie obligatoire
 SELECT ename , salary INTO vEname , vSalary FROM employee WHERE enum = ’E2’; DBMS_OUTPUT.PUT_LINE('Nom = ' || vEname ); DBMS_OUTPUT.PUT_LINE('Salaire = ' || vSalary); EXCEPTION
-- Partie optionnelle WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Erreur : ' || TO_CHAR(sqlcode)); DBMS_OUTPUT.PUT_LINE('Erreur : ' || sqlerrm); END; /

Déclarations de variables

DECLARE
dateEmbauche date; /* initialisation implicite avec null */
nom varchar2(80) := ’Benali’;
trouve boolean; /* initialisation implicite avec null aussi*/
incrSalaire constant number(3,2) := 1.5; /* constante */
. . .
BEGIN . . . END;

Déclaration de variable par référence aux données de la base

NB. Les exemples dans la suite portent sur la table :

Employee (enum, ename, salary, address, dept)

Variable de type colonne

DECLARE
maxSal employee.salary%TYPE;

Variable de type ligne

DECLARE
employeeRec employee%ROWTYPE;

Déclarations de zone cursor

DECLARE
CURSOR empCursor IS
SELECT * FROM employee WHERE dept = 123
DECLARE
CURSOR empCursor (dno number ) IS
SELECT * FROM employee WHERE dept = dno;
OPEN empCursor (123);
DECLARE
CURSOR empCursor IS
SELECT * FROM employee WHERE dept = 123
FOR UPDATE OF salary;

Instructions

DECLARE
quantite integer := 0;
...
BEGIN
quantite := quantite + 5;
...
END

Exemple d'interrogation SELECT mono-tuple

DECLARE
employeeRec employee%ROWTYPE;
maxSal employee.salary%TYPE;
BEGIN
SELECT * INTO employeeRec
FROM employee WHERE enum='E7';
dbms_output.put_line(employeeRec.ename||' '||employeeRec.Salary);
END;

Exemple d'interrogation Select mono-tuple (suite)

BEGIN 
SELECT ename, salary INTO employeeRec.ename, employeeRec.Salary
FROM employee
WHERE enum = 'E8';

dbms_output.put_line(employeeRec.ename||' '||employeeRec.Salary);
END;
BEGIN
SELECT max(salary) INTO maxSal
FROM employee;

dbms_output.put_line('Salaire Maximum: '|| maxsal);
END;

Structure de contrôle if-then-else 

IF <condition> THEN   <séquence d'instructions> 
[ELSIF <condition> THEN <séquence d'instructions> ]
. . .
[ELSE <séquence d'instructions> ]
END IF ;

Structures de contrôles (boucle LOOP )

[<label>] 
WHILE <condition> LOOP
<séquence d'instructions>;
END LOOP [<label>] ;
DECLARE
i number;
BEGIN
i:=0;
WHILE i<8 LOOP
dbms_output.put_line(i);
i:= i+1;
END LOOP ;
END;

Structures de contrôles (boucle LOOP )

[<label name>]
FOR <index> IN [reverse] <lower bound>..<upper bound> LOOP
<séquence d'instructions> ;
END LOOP [<label name>] ;
BEGIN
FOR i IN 4..7 LOOP
dbms_output.put_line(i);
END LOOP ;
END;

Structures de contrôles (boucle LOOP )

LOOP  ... END LOOP ;
EXIT WHEN ...

Structures de contrôles (boucle LOOP  avec cursor)

DECLARE
CURSOR empCursor IS
SELECT * FROM EMPLOYEE;
employeeRec employee%ROWTYPE;
maxSal employee.SALary%TYPE := 0;
BEGIN
OPEN empCursor;
LOOP
/* Accès à chacun des tuples */
FETCH empCursor INTO employeeRec;
EXIT WHEN empCursor%NOTFOUND;
/* traitement du tuple */
IF maxSal < employeeRec.salary THEN
maxSal := employeeRec.salary;
END IF;
/* fin traitement tuple */

END LOOP ;
dbms_output.put_line('Salaire Maximum: '|| maxsal);
CLOSE empCursor;
END;

Même exemple avec WHILE ...

Usage se %FOUND, pour boucler. Mêmes déclarations.


BEGIN 
OPEN empCursor;
FETCH empCursor INTO employeeRec;

WHILE empCursor%FOUND LOOP

/* traitement du tuple */
IF maxSal < employeeRec.salary THEN
maxSal := employeeRec.salary;
END IF;
/* fin traitement tuple */

FETCH empCursor INTO employeeRec;
END LOOP ;

dbms_output.put_line('Salaire Maximum: '|| maxsal);
CLOSE empCursor;
END;

Boucle pour chaque: FOR ... IN...

Boucle pour chaque: FOR ... IN...

BEGIN 
FOR employeeRec IN empCursor LOOP
/* traitement du tuple */
IF maxSal < employeeRec.salary THEN
maxSal := employeeRec.salary;
END IF;
/* fin traitement tuple */
END LOOP;

dbms_output.put_line('Salaire Maximum: '|| maxsal);
END;

Variante de boucle FOR ...in...

BEGIN
FOR untel IN (SELECT * FROM employee )
LOOP
dbms_output.put_line('Num = ' || untel.enum ||
', Nom = ' || untel.ename ||
', Salaire = '|| untel.salary);
END LOOP;
END;
FOR salVar IN  (SELECT salary * 1.07 nouveau FROM employee) LOOP  
/* ... */
END LOOP ;

Cursor donné lors de OPEN.

Usage du mot clé TYPE avec REF CURORSOR.

Declare
-- le curseur
TYPE EmpCurType IS REF CURSOR;
empCur EmpCurType;
-- les variables
nom employee.ename%TYPE ;
Begin
OPEN empCur FOR 'SELECT ename FROM employee' ;
FETCH empCur INTO nom ; -- On FETCH le premier...
dbms_output.put_line( nom ) ;
CLOSE empCur;
End ;

Cursor avec mise à jour

DECLARE CURSOR   empCur is SELECT Salary FROM employee 
WHERE DEPT = 'D1' FOR UPDATE OF salary;
BEGIN
FOR empRec IN empCur LOOP
UPDATE employee
SET salary = empRec.salary * 1.05
WHERE current of empCur ;
END LOOP ;
COMMIT;
END;

Traitement d'Exceptions

WHEN <nom d'exception> THEN   <séquence  d'instructions>; 
DECLARE
employeeRec employee%ROWTYPE;
BEGIN
-- chercher les noms et salaires d'employés d'un département donné

SELECT ename, salary INTO employeeRec.ename, employeeRec.Salary
FROM EMPLOYEE
WHERE dept = '&dnum'; -- à lire avant --
dbms_output.put_line(employeeRec.ename||' '||employeeRec.Salary);

EXCEPTION
WHEN TOO_MANY_ROWS THEN
dbms_output.put_line('Trop d employés.');
END;

Traitement d'Exceptions (suite)

CURSOR_ALREADY_OPEN: tentative d'ouverture de CURSOR   déjà ouvert
INVALID_CURSOR: par exemple FETCH sur un CURSOR déjà fermé
NO_DATA_FOUND: aucun tuple retourné (SELECT INTO ou FETCH)
TOO_MANY_ROWS: SELECT INTO retourne plus d'un tuple
...
ZERO_DIVIDE: tentative de division par zéro
WHEN NO DATA FOUND THEN   rollback;
RAISE <nom d'exception>

Exemple complet

DECLARE
sal employee.salary%TYPE;
num employee.enum%TYPE;
tropGrand exception;
CURSOR empCurseur is SELECT enum , salary
FROM EMPLOYEE
WHERE DEPT = '&dept'
FOR UPDATE OF SALary;
BEGIN
OPEN empCurseur;
LOOP
FETCH empCurseur INTO num, sal;
EXIT WHEN empCurseur%NOTFOUND;
IF sal * 1.05 > 4000 THEN RAISE tropGrand;
ELSE
UPDATE EMPLOYEE SAT salary = sal * 1.05
WHERE CURRENT OF empCurseur ;
dbms_output.put_line (num || ' mis a jour.');
END IF ;
END LOOP ;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('pas trouve');
rollback;
WHEN tropgrand THEN
INSERT INTO veterans VALUES (num, sal);
dbms_output.put_line(num||' '||Sal || ' Trop grand');
COMMIT;
END;

Voir aussi PL/SQL par l'exemple.

Procédures et Fonctions

Les Déclencheurs (Triggers)

En savoir plus