Ed Corrige PLSQL
Ed Corrige PLSQL
Ed Corrige PLSQL
ED PL/SQL
(Corrigé)
Par la suite on considère que les tables utilisées par les exercices ont été déjà crées et
remplies avec les données nécessaires.
Exercice 2. Soit la table RES(NO). Écrivez un bloc PL/SQL qui inséré les chiffres de 1 à
100 dans cette table.
Solution :
DECLARE
nb NUMBER := 1 ;
BEGIN
LOOP
INSERT INTO RES
VALUES(nb) ;
nb = nb + 1 ;
EXIT WHEN nb > 100 ;
END LOOP
END
Exercice 3. Écrivez un bloc PL/SQL qui affiche la somme des nombres entre 1000 et
10000.
Solution :
DECLARE
somme NUMBER := 0 ;
BEGIN
FOR i IN 1000..10000 LOOP
somme = somme + i ;
END LOOP
DBMS_OUTPUT.PUT_LINE('Somme = ' || somme) ;
END
NFA011
NFA011 – Développement d’applications avec les bases de données
Exercice 4. Écrivez un programme PL/SQL qui affiche le reste de la division de 17664 par
171. Ne pas utilisez la fonction MOD.
Solution :
DECLARE
reste NUMBER = 17664 ;
BEGIN
WHILE reste > 171 LOOP
reste .= reste – 171 ;
END LOOP
DBMS_OUTPUT.PUT_LINE('Le reste de 17664 par 171 est ' || reste)
END
Exercice 6. Écrire une fonction PL/SQL qui prends en entrée un nombre entier n et
retourne le factoriel de ce nombre n!. Implémenter deux versions : itérative et récursive.
La version récursive est basée sur la relation de récurrence : n! = n · [(n – 1)!]
Solution :
CREATE OR REPLACE FUNCTION FACT_ITER (N INTEGER)
RETURN INTEGER
IS
result INTEGER := 1;
BEGIN
for i in 2..N
loop
result := result*i;
end loop;
return result;
END FACT_ITER;
NFA011
NFA011 – Développement d’applications avec les bases de données
RETURN -1;
ELSIF(N = 0) THEN
RETURN 1;
ELSE
RETURN N*FACT1(N - 1);
END IF;
END FACT_REC;
Écrivez en PL/SQL le déclencheur (trigger) qui lors de l’insertion d’une nouvelle ligne dans
la table PARTITION vérifie que la taille totale des partitions sur le disque concerné (y
compris la partition qui est en cours d'être ajoutée) ne dépasse pas la capacité du disque.
Si tel n’est pas le cas, l’enregistrement de la nouvelle cage ne doit pas être fait et un
message doit être affiché pour indiquer cette anomalie.
Solution :
CREATE OR REPLACE TRIGGER VérificationDisque
BEFORE INSERT ON PARTITION
FOR EACH ROW /* nécessaire pour avoir accès à :NEW */
DECLARE
tailleTotale PARTITION.taille%TYPE = 0;
capacitéDisque DISQUE.capacité%TYPE = 0;
BEGIN
SELECT SUM(taille) INTO tailleTotale
FROM PARTITION WHERE nomDisque = :NEW.nomDisque;
NFA011
NFA011 – Développement d’applications avec les bases de données
DECLARE
total NUMBER(2);
BEGIN
UPDATE EMPLOYE
SET salaire = salaire + 200
WHERE DEPARTEMENT = 'Commercial';
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Aucun salaire augmenté');
ELSIF SQL%FOUND THEN
total := SQL%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE( total || ' salaires ont été augmentés ');
END IF;
END;
Exercice 10. Soit la relation EMPLOYE de l'exercice précédent. Écrivez un bloc PL/SQL qui
affiche les noms des employés du département 'Commercial' qui sont âgés de plus de 40 ans. Utilisez un curseur
implicite dans une boucle FOR.
Solution :
BEGIN
FOR emp IN (SELECT * FROM EMPLOYE WHERE AGE >= 40
AND DEPARTEMENT = 'Commercial')
LOOP
DBMS_OUTPUT.PUT_LINE(emp.NOM) ;
END LOOP
END
Exercice 11. Soit la relation EMPLOYE de l'exercice précédent. Écrivez une procédure
PLSQL qui prends en paramètre un NUMBER (age limite) et qui affiche pour chaque
département le nombre des employés qui dépassent l'age limite. Utilisez un curseur avec
paramètre l'age limite.
Solution :
CREATE OR REPLACE PROCEDURE moyenneAge(AgeLim IN NUMBER)
IS
CURSOR CS(Age_Limite NUMBER) IS
SELECT DEPARTEMENT AS DNOM, COUNT(*) AS NB
FROM EMPLOYE
WHERE AGE > Age_Limite
GROUP BY DEPARTEMENT ;
NFA011
NFA011 – Développement d’applications avec les bases de données
BEGIN
FOR DEPT IN CS(AgeLim) LOOP
DBMS__OUTPUT.PUT_LINE(DEPT.DNOM || ' ' || DEPT.NB)
END FOR
END
NFA011
NFA011 – Développement d’applications avec les bases de données
Exercice 15. On considère la table COMPETITION donné dans l'exercice précédent. Écrire
un déclencheur qui vérifie que le code d'une compétition commence par les lettres 'CMP' avant son l'insertion
dans la table COMPETITION.
Solution :
CREATE OR REPLACE TRIGGER VERIFIE_CODE_COMP
BEFORE INSERT OR UPDATE ON COMPETITION
FOR EACH ROW
WHEN (:NEW.CODE_COMP NOT LIKE 'CMP%')
BEGIN
RAISE_APPLICATION_ERROR(-20001,'COMP_CODE doit commencer par CMP');
END;
NFA011
NFA011 – Développement d’applications avec les bases de données
IF NB = 0 THEN
RAISE RECORD_NOT_EXISTS;
ELSE
RETURN NB;
END IF;
EXCEPTION
WHEN RECORD_NOT_EXISTS THEN
DBMS_OUTPUT.PUT_LINE('Pas d'abonnés dans la ville de ' || VILLE);
RETURN 0;
END;
Exercice 17. Créer un déclencheur qui est lancé après chaque nouvelle commande
INSERT dans la table ABONNEMENT de l'exercice précédent. Le déclencheur fait la mis a
jour du nombre d'abonnements dans la table suivante :
TRACK_ABONNEMENTS(MAG_NOM MAGAZINE.MAG_NOM%TYPE,
NB_ABONN INTEGER)
On considère que la table TRACK_ABONNEMENTS contient déjà tous les magazines (ceux
qui n'ont pas d’abonnement on NB_ABONN à 0).
Solution :
CREATE OR REPLACE TRIGGER AUDIT_CUSTOMER
AFTER INSERT ON CUSTOMER
FOR EACH ROW
DECLARE
NB INTEGER;
MNOM MAGAZINE.MAG_NOM%TYPE;
BEGIN
IF NOT EXISTS (SELECT 1 FROM TRACK_ABONNEMENTS T
WHERE T.MAG_NOM =:NEW.MAG_NOM) THEN
RAISE_APPLICATION_ERROR(-20100, 'Le magazine n'existe pas !');
ELSE
UPDATE TRACK_ABONNEMENT SET NB_ABONN = NB + 1
WHERE MAG_NOM = :NEW.MAG_NOM ;
END IF
END;
NFA011
NFA011 – Développement d’applications avec les bases de données
Exercice 19. Modifiez le programme PL/SQL de l’exercice précédent pour qu’il fonctionne
même si plusieurs vols satisfont les contraintes.
Solution :
ACCEPT s_nde PROMPT 'Nombre escales : ';
DECLARE
derniereEscale Escales.Numescale%TYPE;
nbMaxEscales NUMBER := 0;
escaleCourante Escales.Ville_escale%TYPE;
prochaineDestination Vol.Ville_arrivee%TYPE;
destinationFinale Vol.Ville_arrivee%TYPE := 'Paris';
dureeEscaleCourante Escales.Duree_escale%TYPE;
numEscaleCourante Escales.Numescale%TYPE;
volAPrendre Vol%ROWTYPE;
NFA011
NFA011 – Développement d’applications avec les bases de données
OPEN curseur1;
FETCH curseur1 INTO volAPrendre;
IF (curseur1%NOTFOUND OR curseur1%NOTFOUND IS NULL) THEN
DBMS_OUTPUT.PUT_LINE('Aucun vol disponible de ' || escaleCourante);
EXIT boucleEscales;
ELSE -- retourner au maximum 10 propositions de vol
WHILE (curseur1%FOUND AND curseur1%ROWCOUNT <= 10)
LOOP
DBMS_OUTPUT.PUT_LINE('A partir de ' || escaleCourante || ' (durée' ||
dureeEscaleCourante || ' jours) prendre vol ' ||
volAPrendre.Numvol || ' a ' ||
to_char(volAPrendre.Heure_depart, 'HH24:MI'));
FETCH curseur1 INTO volAPrendre;
END LOOP;
END IF;
CLOSE curseur1;
numEscaleCourante := numEscaleCourante + 1;
EXIT WHEN (numEscaleCourante > derniereEscale);
END LOOP;
END IF;
EXCEPTION
WHEN TOO_MANY_ROWS OR NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('La table des escales est mal définie !');
END;
NFA011
NFA011 – Développement d’applications avec les bases de données
villes escales, on ne peut pas s’assurer qu’une solution proposée corresponde réellement
à un tour du globe. Aussi, cette solution ne permet pas d’éviter les boucles, par exemple
le tour suivant est valide : Paris → Vienne → Rome → Vienne → Rome → Paris. Cette
solution avec l’appel récursif dans la boucle FOR du curseur ouvrira autant de curseurs
qu’il y a de niveaux de récursivité et peut donc produire un dépassement de la borne
OPEN_CURSORS (paramètre de l’initialisation d’Oracle) ou même un dépassement
mémoire (une exception STORAGE_ERROR sera levée).
NFA011
NFA011 – Développement d’applications avec les bases de données
Références bibliographiques
Jérôme Gabillaud, Olivier Heurtel, Oracle 11g - SQL, PL/SQL, SQL*Plus, Editions ENI 2009
Joan Casteel, Oracle 11g: PL/SQL Programming, Cengage Learning, 2012
Michael McLaughlin, John Harper, Oracle Database 11g PL/SQL Programming Workbook,
McGraw-Hill 2010
Ressources web :
http://gtuplsql.blogspot.fr
http://alexandre-mesle.com/enseignement/oracle/
http://docs.oracle.com/
Rappel SQL
INSERT INTO Table (...) UPDATE Table SELECT attributs SELECT attributs
VALUES (...) SET col1 = valeur1, FROM Table INTO variables
col2 = valeur2 WHERE condition FROM Table
WHERE condition WHERE condition
Instructions conditionnelles :
IF cond1 THEN CASE expr
– code WHEN val1 THEN
ELSIF cond2 THEN – code
– code WHEN val2 THEN resultat2
… – code
ELSE ELSE
– code – code
END IF END CASE
Boucles :
LOOP WHILE condition FOR i IN val1 .. val2 LOOP
– code LOOP – code
EXIT WHEN condition – code END LOOP
END LOOP END LOOP
FOR item IN curseur LOOP
– code
END LOOP
Curseurs :
CURSOR C IS SELECT_STATEMENT ;
FETCH C INTO variable_record ;
FETCH C INTO liste_variables ;
CLOSE C ;
Procédures et fonctions :
CREATE OR REPLACE PROCEDURE [FUNCTION] nomProcedure(liste_paramètres)
[RETURN TYPE]
NFA011
NFA011 – Développement d’applications avec les bases de données
IS
– Déclarations variables
BEGIN
– code
[RETURN val]
EXCEPTION
– code
[RETURN val]
END
Les paramètres peuvent être IN, OUT ou IN OUT. Exemple :
FONCTION moyenneSalaire (id_departement IN NUMBER) RETURN NUMBER IS ...
Déclencheurs :
CREATE OR REPLACE TRIGGER nomTrigger
[BEFORE | AFTER]
[INSERT [OR] UPDATE [OR] DELETE]
[OF nom_attribut]
ON table
[REFERENCING OLD as O NEW as N]
[FOR EACH ROW]
[WHEN condition]
DECLARE
– declarations
BEGIN
– code
END
Attention : éviter les boucles infinies. Exemple déclencheurs A et B :
INSERT déclenche A
A exécute UPDATE
UPDATE déclenche B
B exécute INSERT
INSERT déclenche A → boucle !
Traitement exceptions :
EXCEPTION
WHEN exception1 THEN
– code
WHEN exception1 THEN
– code
WHEN exception1 THEN
– code
WHEN OTHERS
– code
END
NFA011