Vous êtes sur la page 1sur 328

Fondements des bases de données

Programmation en PL/SQL Oracle – les transactions

Équipe pédagogique BD

http://liris.cnrs.fr/~mplantev/doku/doku.php?id=lif10_2016a
Version du 13 octobre 2016
Transactions
Problèmes de cohérence et transaction
Un SGBD doit pouvoir supporter :
I plusieurs utilisateurs l’utilisant en parallèle
I effectuant des opérations d’écriture et de lecture
I tout en garantissant la cohérence des données

Une transaction est un ensemble d’ordres (SQL) indivisibles, faisant


passer la base de données d’un état cohérent à un autre en une seule
étape.

Propriétés ACID
Atomicité Une transaction réussi ssi toutes ses opérations réussisent.
Cohérence Une transaction terminée laisse la base dans un état
cohérent où les données sont intègres.
Isolation Les transactions doivent être rendues indépendantes les
unes des autres.
Durabilité Les effets d’uns transaction terminée sont persistant.
Opérations COMMIT et ROLLBACK

ROLLBACK annule entièrement une transaction : toutes les


modifications depuis le début de la transaction sont alors
défaites.
COMMIT valide entièrement une transaction : les modifications
deviennent définitives et visibles à tous les utilisateurs.
SAVEPOINT point de contrôle, état de la base où l’on pourra revenir
plus tard.
A noter
I En cours de transaction, seul l’utilisateur ayant effectué les
modifications les voit.
I En cas de fin anormale d’une tâche utilisateur il y a
automatiquement ROLLBACK des transactions non terminées.
I Une transaction commence (implicitement) à la première opération
SQL rencontrée et dès qu’une transaction est terminée.
I Les commandes de définition de données sont automatiquement
commitées (auto-commit) et valident donc les ordres précédent.
I Un mécanisme de vérouillage permet de gérer les conflits d’accès
parallèle.
Exemple
INSERT INTO t r a n s a c t i o n s VALUES ( 1 , ’ Pas ␣ de ␣ s a v e p o i n t ’ ) ;
−− 1 l i g n e c r e e e .
INSERT INTO t r a n s a c t i o n s VALUES ( 2 , ’ S a v e p o i n t ␣A ’ ) ;
−− 1 l i g n e c r e e e .
SAVEPOINT A ;
−− S a v e p o i n t c r e e .

INSERT INTO t r a n s a c t i o n s VALUES ( 3 , ’ S a v e p o i n t ␣B ’ ) ;


−− 1 l i g n e c r e e e .
SAVEPOINT B ;
−− S a v e p o i n t c r e e .

INSERT INTO t r a n s a c t i o n s VALUES ( 4 , ’ Pas ␣ de ␣ s a v e p o i n t ’ ) ;


−− 1 l i g n e c r e e e .
Exemple
ROLLBACK TO SAVEPOINT B ;
−− A n n u l a t i o n ( r o l l b a c k ) e f f e c t u e e .
SELECT ∗ FROM t r a n s a c t i o n s ;
/∗ ID DESCRIPTION
−−−−−−−−−− −−−−−−−−−−−−−−−−−−−−
1 Pas de s a v e p o i n t
2 Savepoint A
3 Savepoint B ∗/

ROLLBACK TO SAVEPOINT A ;
−−A n n u l a t i o n ( r o l l b a c k ) e f f e c t u e e .
SELECT ∗ FROM t r a n s a c t i o n s ;
/∗ ID DESCRIPTION
−−−−−−−−−− −−−−−−−−−−−−−−−−−−−−
1 Pas de s a v e p o i n t
2 Savepoint A ∗/
Exemple
COMMIT;
−− V a l i d a t i o n e f f e c t u e e .
SELECT ∗ FROM t r a n s a c t i o n s ;
ID DESCRIPTION
−−−−−−−−−− −−−−−−−−−−−−−−−−−−−−
/∗ 1 Pas de s a v e p o i n t
2 Savepoint A ∗/
Triggers et transactions
Le trigger est une extension de l’opération de modification de donnée sur
laquelle il se déclenche, or ces opérations sont atomiques et ne peuvent
pas être divisées :
COMMIT et ROLLBACK sont donc interdits dans le corps des triggers.

Workaround : PRAGMA AUTONOMOUS_TRANSACTION


Permet de déclarer une transaction autonome. Attention aux effets : ici,
si l’insert est annulé, le tuple de la relation log sera bien ajouté !
CREATE OR REPLACE TRIGGER t a b 1 _ t r i g
AFTER i n s e r t ON t a b 1
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO l o g VALUES (SYSDATE , ’ I n s e r t ␣ on ␣TAB1 ’ ) ;
COMMIT; −− o n l y a l l o w e d i n autonomous t r i g g e r s
END;
/
Niveaux d’isolation
Du plus souple (garantie minimum) au plus restrictif :
READ UNCOMMITED Permet à une autre transaction de lire des données
qui ont été changées, mais pas encore validées.
READ COMMITED C’est le paramètre par défaut pour Oracle. Il assure que
chaque requête dans une transaction lit seulement les
données validées.
REPEATABLE READ Ce niveau permet à une transaction de lire les mêmes
données plusieurs fois avec la garantie qu’elle recevra les
mêmes résultats à chaque fois.
SERIALIZABLE Une transaction ne prend en compte que les données
validées avant le démarrage de la transaction.

Niveaux Oracle
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
SET TRANSACTION READ ONLY;
−− s e m b l a b l e a REPEATABLE READ m a i s
−− s a n s a u t o r i s e r l e s m o d i f i c a t i o n s .
Fin du cours.
Introduction au langage PL/SQL

Consultant Formateur
Développement d’applications
Caractéristiques du PL/SQL

 Un langage procédural qui étend SQL. Il est propre à Oracle


 Modulaire, grâce aux blocs, fonctions & procédures, ainsi que les
packages
 Déclaratif : déclaration de variables, constantes, exceptions, de
curseur.
 Fournit les structures itératives et conditionnelles
 Traitement des erreurs (routines ou définies par l’utilisateur)
 Portabilité vers n’importe quel environnement supportant Oracle
 Intégration : pas de problèmes d’intégration entre Oracle et les
environnements de développements
Conventions de programmation

 Conventions du code
 En majuscule
 Les commandes SQL (SELECT, FROM, WHERE etc.)
 Les mots clé PL/SQL (DECLARE, BEGIN, NUMBER etc.)
 En minuscule
 Les noms de variables PL/SQL
 Les noms de colonnes, tables, vues etc.
Conventions de programmation

 Conventions de nommage des variables


Structure d’un bloc PL/SQL

 Un bloc PL/SQL contient trois sections :


 Une section déclarative: optionnelle, contient les déclarations
des variables, constantes, curseurs, exceptions, etc. Commence
par le mot clé DECLARE
 Une section exécutable: obligatoire, contient les requêtes SQL
et les instructions PL/SQL. Commence par le mot BEGIN
 Une section de traitement d’erreurs : optionnelle, contient les
instructions à exécuter au cas où des erreurs se produisent.
Commence par le mot EXCEPTION
 À noter que le bloc se termine par le mot clé END
Structure d’un bloc PL/SQL

 Exemple de bloc PL/SQL

DECLARE
v_sal NUMBER;
BEGIN
SELECT sal INTO v_sal FROM emp
WHERE empno=9854;
EXCEPTION
WHEN NO_DATA_FOUND THEN

END
Les structures de programme
PL/SQL
 Les blocs anonymes

 Les procédures et fonctions stockées

 Les procédures et fonctions d’application

 Les packages

 Les triggers (déclencheurs) de base de données

 Les triggers (déclencheurs) d’application


Les règles syntaxiques

 Un identifiant :
 Max de 30 caractères, doit débuter par une lettre.
 Ne doit pas être un mot réservé.
 Son nom doit être différent des noms de colonnes.

 Les chaînes de caractères et les dates constantes entre simples


quotes.
 Les commentaires sur plusieurs lignes doivent être inclus entre les
caractères " /* " et " */ "
 Les caractères "- -" sont utilisés pour mettre une seule ligne en
commentaire.
La base de données
exemple
Les types de variables

 Il existe deux types de variables qui fonctionnent sous PL/SQL


 Les variables PL/SQL
 Variable scalaire : contenant une valeur unique
 Variable composée : contenant plusieurs valeurs comme le
RECORD, le TABLE, le NESTED TABLE, le VARRAY
 Variable référence : variable qui pointe vers un type de
données
 LOB : variable localisateur d’objets volumineux tel que les
images et les vidéos
 Les variables non PL/SQL
 Les variables de substitution
 Les variables hôtes
Les types de données
scalaires
 Les principaux types de données scalaires
 Les types numériques
 INTEGER, POSITIVE
 PLS_INTEGER, BINARY_INTEGER
 NUMBER(n,d), DECIMAL, REAL
Les types chaine de caractère
 LONG, VARCHAR2(n)
 CHAR(n)
 Le type booléen
 BOOLEAN (true, false ou null)
 Le type date
 DATE
Les variables scalaires

 Déclaration et initialisation
variable_name [CONSTANT] datatype [NOT NULL] [:=|DEFAULT expr];

 datatype : le type de données de la variable, qui est soit


scalaire, composé, référence ou LOB.

 CONSTANT : contraint la variable à être une constante

 NOT NULL : ce mot clé contraint la variable à contenir une


valeur.
 expr : valeur initiale d’une variable, peut être une valeur
littérale, une autre variable ou une expression impliquant des
opérateurs et des fonctions.
Les variables scalaires

 La déclaration de plusieurs variables sur la même ligne est interdite

 Affectation

 variable := expression;

 DEFAULT : contraint la variable à être une constante

 SELECT expression INTO variable FROM...

 Impossible d’affecter la valeur NULL à une variable déclarée NOT


NULL (l’erreur VALUE_ERROR est renvoyée)
 La contrainte NOT NULL doit être suivie par une initialisation
Les variables scalaires

 Le mot clé %TYPE déclare une variable ayant le même type d’une
autre variable ou d’une colonne d’une table ou vue existantes.

Code PL/SQL Commentaires


DECLARE v_job prend le type de la colonne
v_job emp.job%TYPE; job de la table emp.
v_prime NUMBER(5,2) := 500.50; v_prime et est initialisée à 500,45.
v_prime_min %TYPE := v_prime*2; v_prime_min prend le type de la variable
BEGIN

Les variables composées –
le RECORD
 Le mot clé %ROWTYPE déclare une variable RECORD ayant la même
structure que l’enregistrement d’une table/vue.

Code PL/SQL Commentaires


DECLARE Emp_record prend la structure d’un
emp_record emp%ROWTYPE; enregistrement de la table emp.
v_sal NUMBER:=800;
BEGIN Accès aux attributs de l’enregistrement
emp_record.empno:=2564;
emp_record.sal:=v_sal+100;

Les variables composées –
le RECORD
 Le mot clé %ROWTYPE déclare une variable RECORD ayant la même
structure que l’enregistrement d’une table/vue.

Code PL/SQL Commentaires


DECLARE Emp_record prend la structure d’un
emp_record emp%ROWTYPE; enregistrement de la table emp.
v_sal NUMBER:=800;
BEGIN Accès aux attributs de l’enregistrement
emp_record.empno:=2564;
emp_record.sal:=v_sal+100;

 À noter qu’avec la directive %ROWTYPE, les attributs n’héritent pas


la contrainte NOT NULL.
Les variables composées –
le RECORD
 Pour définir un enregistrement personnalisé, il faut déclarer un type
RECORD

 Ensuite, pour l’utiliser, il faut déclarer une variable avec le type


nomRecord
Les variables composées –
le type TABLE
 Le type TABLE permet la déclaration de tableaux dynamiques (sans
taille initiale fixe)
 Une variable de type TABLE inclut deux colonnes, une clé primaire
de type BINARY_INTEGER et une colonne de type scalaire ou RECORD.

Primary key Colonne


… …
1 Jean Tomi
2 Sam Tili
3 Myriam Bou
… …
Les variables composées –
le type TABLE
 Le type TABLE est défini avec cette syntaxe

 La déclaration d’une variable se fait selon cette syntaxe


Les variables composées –
le type TABLE
 Il existe des procédures et des fonctions qui permettent de
manipuler les variables TABLE :
Les variables non-PL/SQL

 Les variables hôtes (dite aussi de session)


 C’est une variable définie dans l’environnement hôte du
programme PL/SQL (SQL*Plus, Forms Developer…)
 Sous SQL*Plus, l’utilisation d’une variable hôte se fait ainsi :
Instructions Commentaires
VARIABLE g_compteur NUMBER; Déclaration de la variable sous SQL*Plus
DECLARE
v_compt NUMBER := 99;
BEGIN
:g_compteur := v_compt+1; Manipulation de la variable (ajout de :)
END;
/
PRINT g_compteur; Affichage de la variable sous SQL*Plus
Les variables non-PL/SQL

 Les variables de substitution


 Une variable de substitution est définie sous SQL*Plus. Sa valeur
est saisie sous SQL*Plus, et est remplacée intégralement dans le bloc
L’utilisation d’une variable de substitution :
Instructions Commentaires
SET SERVEROUTPUT ON Nécessaire à l’affichage
ACCEPT s_nbr PROMPT ‘Saisir un Saisie de la variable de session s_nbr
entier’;
DECLARE
v_doub NUMBER;
BEGIN
v_doub := &s_nbr; Manipulation de la variable (ajout du &)
DBMS_OUTPUT.PUT_LINE(‘Le Affichage de la variable PL/SQL v_doub
résultat est ‘||v_doub);
END;
Les opérateurs et les
fonctions
Les opérateurs
Arithmétiques
+,-,*,/
Logiques
AND , OR, NOT
Concaténation
||
Comparaison
= , != , < , > , <= , >= , LIKE , BETWEEN , IS NULL , IN

Les fonctions
Disponibles dans les instructions PL/SQL
Toutes les fonctions de ligne (lower, substr, length, round,
add_months etc.)
Les blocs imbriqués

Blocs PL/SQL
DECLARE
var1
BEGIN

DECLARE
var2
BEGIN
… Portée
DECLARE de var1
Portée
var3 Portée
de
BEGIN de var2
… var3
END;
END;

END;
Interaction avec la BD –
Les curseurs implicites
Code PL/SQL
 Extraction
ACCEPT des PROMPT
s_empno données‘Saisir
à partirle
de code
la BD de
et stockage dans des
l’employé’;
variables
ACCEPT s_deptno PROMPT ‘Saisir le code de département’;
SELECT select_list
INTO {variable_name [,variable_name,...] | record_name }
DECLARE
FROM table_list
v_ename emp.ename%TYPE;
[WHERE condition] ;
v_sal emp.sal%TYPE;
dept_rec dept%ROWTYPE;
 La requête ne doit retourner qu’une seule ligne, sinon, une erreur
BEGIN
sera générée
SELECT
Règlesename,sal
: INTO v_ename, v_sal
FROM emp
- Les colonnes sélectionnées et les identifiants doivent avoir des noms
WHERE empno=&s_empno;
différents.
-SELECT
Le nombre de variables
* INTO dept_recdans la clause INTO doit être identique au nombre de
FROM dept
colonnes sélectionnées. Ils doivent avoir les mêmes positions.
- WHERE
Le typedeptno=&s_deptno;
de données des identifiants doit correspondre à celui des colonnes.

Pour ce l’utilisation de %TYPE est recommandée
Interaction avec la BD –
La sélection
Code
Si laPL/SQL
requête retourne zéro ou plusieurs lignes, alors une exception
SETgénérée.
est SERVEROUTPUT ON
DECLARE
 TOO_MANY_ROWS
v_sum_sal : si le curseur implicite retourne plusieurs
NUMBER(10,2);
lignes NUMBER NOT NULL := 60;
v_deptno
 NO_DATA_FOUND : s’il ne retourne aucune ligne
BEGIN
SELECT SUM(sal) -- group function
INTO v_sum_sal
FROM emp pas traitée (dans la section EXCEPTION), alors le
Si elle n’est
WHERE
programme deptno = v_deptno;
est interrompu.
DBMS_OUTPUT.PUT_LINE ('The sum salary is ' ||TO_CHAR(v_sum_sal));
END;
/
Interaction avec la BD
La mise à jour – L’insertion

 L’insertion d’une ligne dans une table se produit directement, à


travers la requête INSERT
Code PL/SQL
DECLARE
v_sal NUMBER := 1000;
v_comm NUMBER := 100;
BEGIN
INSERT INTO emp(empno,ename,hiredate,job,sal,comm,mgr,deptno)
VALUES(emp_seq.NEXTVAL,'Ruth',sysdate,‘CLERK‘,v_sal,v_comm,3689,20);
END;
/
Interaction avec la BD
La mise à jour – La modification

 De même pour la modification, elle se produit directement, à travers


la requête UPDATE
Code PL/SQL
DECLARE
v_sal_aug emp.sal%TYPE := 250;
BEGIN
UPDATE emp
SET sal = sal + v_sal_aug
WHERE job = ‘CLERK';
END;
/
Interaction avec la BD
La mise à jour – La suppression

 De même pour la suppression qui se produit à travers la requête


DELETE
Code PL/SQL
DECLARE
v_deptno emp.deptno%TYPE := 10;
BEGIN
DELETE FROM emp
WHERE deptno = v_deptno;
END;
/
Interaction avec la BD
Les curseurs

 Un curseur est une zone mémoire dédiée à l’analyse et l’exécution


d’une requête SQL
 Il existe deux types de curseurs :
 Les curseurs implicites
 Les curseurs explicites
 Le curseur implicite est géré automatiquement par Oracle
 Le curseur explicite est déclaré et géré par le programmeur
Interaction avec la BD
Attributs des curseurs

 Un curseur possède plusieurs attributs qui vous permettent de


contrôler leur output :
Code PL/SQL
Attribut Commentaire
VARIABLE rows_deleted VARCHAR2(30)
SQL%ROWCOUNT
DECLARE Le nombre de lignes retrounés/affectés par la requête
SQL du curseur.
v_deptno NUMBER := 10;
SQL%FOUND
BEGIN Attribut booléen, renvoie TRUE si des lignes ont été
DELETE FROM emp affectées par la requête SQL du curseur
WHERE deptno = v_deptno;
SQL%NOTFOUND La négation de SQL%FOUND
:rows_deleted := SQL%ROWCOUNT||’employés supprimés';
SQL%ISOPEN Renvoie TRUE si le curseur est ouvert. Sa Valeur est
END;
/ toujours FALSE pour les curseurs implicites.
PRINT rows_deleted
Gestion des transactions

 La transaction commence avec une requête de mise à jour. Elle est


soit validée par un COMMIT, soit annulée par un ROLLBACK.

 Le contrôle des points de transactions (SAVEPOINT) est naturellement


possible sous PL/SQL (voir votre cours de Bases de Données)
Les structures de contrôle
Le contrôle conditionnel

 Il existe deux structures de contrôle conditionnelles :


 IF – THEN – ELSE
 CASE – WHEN - THEN
 La syntaxe complète de la structure IF – THEN – ELSE est la suivante :

Syntaxe Exemple
IF condition THEN ...
instructions; IF v_deptno=10 THEN v_comm:=5000;
[ELSIF condition THEN ELSIF v_deptno=20 THEN v_comm:=7500;
instructions;] ELSIF v_deptno=30 THEN v_comm:= 3000;
[ELSE ELSE v_comm:= 2000;
instructions;] END IF;
END IF; ...
Les structures de contrôle
Le contrôle conditionnel

 La syntaxe complète de la structure CASE – WHEN - THEN est la


suivante :
Syntaxe Exemple
CASE selector ...
WHEN expression1 CASE v_job
THEN result1 WHEN ‘CLERK’
THEN v_comm:=100
WHEN expression2
WHEN ‘SALESMAN’
THEN result2
THEN v_comm:=200
... ELSE v_comm:=300;
WHEN expressionN END;
THEN resultN ...
[ELSE resultN+1;]
END;
Les structures de contrôle
Traitement des valeurs NULL

 Ne pas oublier que comparer une valeur avec le NULL, retourne


nécessairement la valeur NULL
Exemple 1 Exemple 2
x:=5; a := NULL;
y:=NULL; b := NULL;
... ...
IF x!=y THEN IF a=b THEN
-- renvoie NULL, et -- renvoie NULL, et pas TRUE
-- pas TRUE sequence_of_statements;
instructions; -- instructions non exécutées
-- ces instructions END IF;
-- ne sont pas
-- exécutées
END IF;
Les structures de contrôle
Les boucles

 Une structure itérative répète un ensemble d’instructions plusieurs


fois
 PL/SQL offre trois types de boucles :
 La boucle basique (LOOP … EXIT WHEN)
 La boucle FOR
 La boucle WHILE
Les curseurs explicites
Définition

 À toute requête SQL, Oracle alloue une zone mémoire SQL privée
appelée curseur

 Un curseur implicite est créé pour toute opération de LMD, et toute


requête SQL retournant une seule ligne, dont le résultat sera affecté à
des variables.

 Un curseur explicite est créé par le programmeur pour gérer le


résultat d’une requête SELECT retournant plusieurs lignes.
Les curseurs explicites
Utilisation

Déclaration Ouverture Fetch Fermeture


• Nommer le •L’instruction •Charger les •L’instruction
curseur OPEN exécute la données d’une CLOSE libère le
requête SELECT ligne dans
Vide curseur actif
• Définir sa
une/plusieurs
requête variables
SELECT

 OPEN : exécute la requête, identifie le résultat et place le curseur sur la première ligne
 FETCH : extrait la ligne courante et avance ensuite le curseur à la ligne suivante
 CLOSE : fermeture du curseur quand la dernière ligne est traitée. Le curseur est désactivé
Les curseurs explicites
Déclaration

 La syntaxe :

CURSOR cursor_name IS requete_select;

 Pas de clause INTO dans la déclaration. L’affectation aux variables


sera faite grâce à l’instruction FETCH
 La requête SELECT peut inclure des sous-requêtes, des jointures, des
fonctions de groupes/lignes etc.
Les curseurs explicites
Ouverture, Fetch et Close

 La syntaxe de l’ouverture :
OPEN cursor_name;
 La syntaxe du FETCH :
FETCH cursor_name INTO [variable1, variable2,…] | record_name ];
Exemple
LOOP
FETCH cursor_name INTO var1, var2;
EXIT WHEN…;

-- Traiter les données extraites

END LOOP;

 La syntaxe de la fermeture :
CLOSE cursor_name;
Les curseurs explicites
Ouverture, Fetch et Close

Attribut Type Commentaire


%ROWCOUNT Numérique Le nombre de lignes retrounés/affectés
par la requête SQL du curseur.
%FOUND Booleén Attribut booléen, renvoie TRUE si le fetch
le plus récent a renvoyé une ligne
%NOTFOUND Booléen La négation de SQL%FOUND
%ISOPEN Booleén Renvoie TRUE si le curseur est ouvert. Sa
Valeur est toujours FALSE pour les
curseurs implicites.

 La syntaxe de la fermeture :
CLOSE cursor_name;
Les curseurs explicites
Exemple 1

 Insérez les employés CLERK et de salaire < 1000 dans emp_tmp


(incluant uniquement les colonnes empno et ename)
Code PL/SQL
DECLARE
CURSOR emp_cursor IS SELECT empno, ename FROM emp WHERE
job=‘CLERK’ AND sal<1000;
emp_record emp_cursor%ROWTYPE;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO emp_record;
EXIT WHEN emp_cursor%NOTFOUND;
INSERT INTO emp_tmp VALUES (emp_record.empno,
emp_record.ename);
END LOOP;
COMMIT;
CLOSE emp_cursor;
END;
Les curseurs explicites
Exemple 2

 Reprise du même exemple avec la boucle


FOR record_name IN cursor_name LOOP
… instructions
END LOOP;
DECLARE
CURSOR emp_cursor IS SELECT empno, ename FROM emp WHERE
job=‘CLERK’ AND sal<1000;
emp_record emp_cursor%ROWTYPE;
BEGIN
FOR emp_record IN emp_cursor LOOP
--ouverture et FETCH implicites
INSERT INTO emp_tmp VALUES (emp_record.empno,
emp_record.ename);
END LOOP;
--sortie de la boucle (après fin du parcours du curseur) et fermeture
du curseur implicites
COMMIT;
END;
Les curseurs explicites
Exemple 3

 Reprise du même exemple avec la boucle


FOR record_name IN cursor_name LOOP
… instructions
END LOOP;
sans déclarer le curseur
BEGIN
FOR emp_record IN (SELECT empno, ename FROM emp WHERE job=‘CLERK’ AND sal<1000) LOOP
--ouverture et FETCH implicites
INSERT INTO emp_tmp VALUES (emp_record.empno,
emp_record.ename);
END LOOP;
--sortie de la boucle (après fin du parcours du curseur) et fermeture
--du curseur implicites
COMMIT;
END;
Les curseurs explicites
paramétrés
 Il s’agit de définir des paramètres au curseur lors de sa déclaration
 Instanciation de ses paramètres lors de l’ouverture du curseur (et
donc lors de l’exécution de la requête du curseur)
 Utilité : ouvrir le curseur avec des valeurs (de paramètres) différentes
 Syntaxe :
CURSOR cursor_name[(param1 datatype , param2 datatype,…)]
IS req_select;
 Ouverture :
OPEN cursor_name(par_val1, par_val2,…);
Les curseurs explicites
paramétrés
 Afficher les employés CLERK du département 20, suivi par leur
nombre, ensuite, faire de même pour les SALESMAN du département 30
DECLARE
CURSOR emp_cursor(p_job VARCHAR2(30), p_deptno NUMBER) IS
SELECT empno, ename FROM emp WHERE job=p_job AND deptno=p_deptno;
emp_record emp_cursor%ROWTYPE;
BEGIN
OPEN emp_cursor(‘CLERK’,20);
LOOP
FETCH emp_cursor INTO emp_record;
EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(emp_record.empno||’ ‘||emp_record.ename);
END LOOP;
DBMS_OUTPUT.PUT_LINE(‘Nombre employés’||emp_cursor%ROWCOUNT);
CLOSE emp_cursor;
OPEN emp_cursor(‘SALESMAN’,30);
……
END;
Les curseurs explicites
La clause FOR UPDATE

 Syntaxe :
CURSOR cursor_name IS req_select FOR UPDATE [OF (col)][NOWAIT]
 Utilisation :
 Pour verrouiller les lignes d’un curseur dès son ouverture, afin de les
mettre à jour
 Les verrous ne seront libérés qu’à la fin de la transaction
(COMMIT/ROLLBACK)
 Le mot clé OF permet de verrouiller certaines colonnes, et non toutes
 NOWAIT fait que Oracle n’attende pas le déverrouillage des lignes à
modifier si elles sont déjà verrouillées par une autre session. Oracle
génère alors une erreur
 Le COMMIT doit être fait après la fermeture du curseur
Les curseurs explicites
La clause WHERE CURRENT OF

 Syntaxe :
Exemple
UPDATE
DECLARESET col=val WHERE CURRENT OF cursor_name;
CURSOR emp_cursor IS
 Utilisation :
SELECT ename, sal, deptno FROM emp WHERE deptno=20
 LaFOR UPDATE
clause OF sal
WHERE NOWAIT;OF, située au niveau de l’instruction de mise à
CURRENT
BEGIN
jourFOR(UPDATE ouINDELETE
emp_rec ), permet
emp_cursor LOOPde référencer la ligne courante d’un
curseurIF emp_rec.sal<2000 THEN
DBMS_OUTPUT.PUT_LINE(emp_rec.ename||’ ‘||emp_rec.sal);
UPDATE emp SET sal=sal*1.1 WHERE CURRENT OF emp_cursor;
END IF;
END LOOP;
COMMIT;
END;
Les curseurs explicites
Exercice

Ecrire un bloc PL/SQL qui permet de :


- Afficher les employés qui travaillent dans le département 10 et dont
les commissions sont égales à 100.
- Affectez-leur le département ACCOUNTING
- Faites de même pour les employés du département 20 et dont les
commissions sont égales à 200.
- Ajoutez à leurs salaires 250
Utiliser un seul curseur paramétré.
Les exceptions

 Une exception est une erreur identifiée qui est déclenchée durant
l’exécution d’un bloc PL/SQL
 L’exception est déclenchée :
 Si une erreur PL/SQL se produit (par exemple, division par zéro)
 Explicitement par l’utilisateur
 Elle est traitée :
 Au niveau du bloc où elle est détectée
 Propagée à l’environnement d’appel
Les exceptions
Syntaxe

DECLARE
….
BEGIN
….
….
EXCEPTION
WHEN exception1 [OR exception2…] THEN
instruction1;
instruction2;
….
[WHEN exception1 [OR exception2…] THEN
instruction1;
instruction2;
….]
[WHEN OTHERS THEN
instruction1;
instruction2;
….]
END;
Les exceptions
Exceptions prédéfinies

 Elles sont traitées en mentionnant leurs noms (prédéfinis) dans le


WHEN
Exception Code Commentaires
CASE_NOT_FOUND ORA-06592 Aucun choix WHEN n’est sélectionné dans la structure
CASE, et pas de clause ELSE.
COLLECTION_IS_NULL ORA-06531 Utilisation d’une méthode autre que EXISTS pour une
collection non initialisée.
CURSOR_ALREADY_OPEN ORA-06511 Ouverture d’un curseur déjà ouvert
DUP_VAL_ON_INDEX ORA-00001 Insertion d’une valeur dupliquée sur une colonne unique
INVALID_NUMBER ORA-01722 Échec de conversion en NUMBER
LOGIN_DENIED ORA-01017 Connexion échouée
NO_DATA_FOUND ORA-01403 Curseur implicite SELECT ne retournant aucune ligne
TOO_MANY_ROWS ORA-01422 Curseur implicite SELECT retournant plusieurs lignes
ZERO_DIVIDE ORA-01476 Division par zéro
Les exceptions
Exceptions prédéfinies

Exemple
SET SERVEROUTPUT ON
DECLARE
v_sal NUMBER;
BEGIN
SELECT sal INTO v_sal FROM emp WHERE ename=‘ALLEN’;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(‘Employé de nom ALLEN inexistant’);
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE(‘Il existe plusieurs employés de nom ALLEN’);
END;
Les exceptions
Exceptions prédéfinies

 Cas d’une même erreur pour différentes instructions

Exemple
SET SERVEROUTPUT ON
DECLARE
v_sal NUMBER;
req NUMBER :=1;
BEGIN
SELECT sal INTO v_sal FROM emp WHERE empno=6398;
req:=2;
SELECT sal INTO v_sal FROM emp WHERE ename=‘WARD’;
EXCEPTION
WHEN NO_DATA_FOUND THEN
IF (req=1) THEN
DBMS_OUTPUT.PUT_LINE(‘Employé de code 6398 inexistant’);
ELSE
DBMS_OUTPUT.PUT_LINE(‘Employé de nom WARD inexistant’);
END;
Les exceptions
Exceptions ORACLE non-prédéfinies

 Il existe trois étapes pour traiter une exception Oracle non prédéfinie
1. Déclarer l’exception : lui donner un nom
e_excpt EXCEPTION;
2. Associer l’exception à l’erreur Oracle : utiliser la routine
PRAGMA EXCEPTION INIT(e_excpt,error_code)
3. Traiter l’exception dans la section EXCEPTION
WHEN e_excpt THEN…
Les exceptions
Exceptions ORACLE non-prédéfinies

Exemple
DEFINE p_deptno = 10
DECLARE
e_emps_remaining EXCEPTION;
PRAGMA EXCEPTION_INIT(e_emps_remaining, -2292);
BEGIN
DELETE FROM departments
WHERE department_id = &p_deptno;
COMMIT;
EXCEPTION
WHEN e_emps_remaining THEN
DBMS_OUTPUT.PUT_LINE (‘Le département inclut des employés!!!');
END;
Les exceptions
SQLCODE et SQLERRM

 SQLCODE retourne le code de l’exception déclenchée


 SQLERRM retourne le message de l’exception déclenchée
Exemple
DECLARE
v_error_code NUMBER;
v_error_message VARCHAR2(255);
BEGIN
...
EXCEPTION
...
WHEN OTHERS THEN
ROLLBACK;
v_error_code := SQLCODE ;
v_error_message := SQLERRM ;
INSERT INTO errors VALUES(v_error_code, v_error_message);
END;
Les exceptions
Exceptions définies par le programmeur

 Il existe trois étapes pour traiter une exception prédéfinie par le


programmeur
1. Déclarer l’exception : lui donner un nom
e_excpt EXCEPTION;
2. Déclencher l’exception au traitement voulu
RAISE e_excpt
3. Traiter l’exception dans la section EXCEPTION
WHEN e_excpt THEN…
Les exceptions
Exception définies par le programmeur

Exemple
DECLARE
e_inv_dept EXCEPTION
BEGIN
UPDATE dept SET dname=‘COMPTABILITE’
WHERE deptno=10;
IF SQL%NOTFOUND THEN
RAISE e_inv_dept;
END IF;
COMMIT;
EXCEPTION
WHEN e_inv_dept THEN
DBMS_OUTPUT.PUT_LINE(‘Le département 10 n’existe pas’);
END;
Les exceptions
Propagation

Exemple
DECLARE
e_excpt EXCEPTION
BEGIN
….
BEGIN
….
RAISE e_excpt;
….
END;
EXCEPTION
WHEN e_excpt THEN
….
END;
Les sous-programmes
Définition

 Ce sont des blocs PL/SQL nommés et capables d’inclure des


paramètres en entrée/sortie.
 Un sous-programme est soit une procédure, soit une fonction.
 Les procédures et les fonctions sont stockées, elles peuvent donc être
partagées par plusieurs utilisateurs.
 À chaque appel de la procédure/fonction, le sous-programmee st
recompilé, au cas où la structure d’un objet (table par exemple) est
modifiée.
Les sous-programmes
Avantages

 Sécurité les droits d’accès ne portent plus sur les objets manipulés
(table, vue etc.), mais sur des programmes stockés.
GRANT EXECUTE ON nom_proc TO user
 Intégrité les traitements dépendants sont encapsulés dans un même
bloc
 Performance réduction du nombre d’appels à la base
 Productivité réutilisation des sous-programmes
Les sous-programmes
Spécification des procédures

CREATE [OR REPLACE] PROCEDURE [schema].nom_proc


[(param1 [IN|OUT|IN OUT] typeSQL [{:=|DEFAULT} EXPRESSION]
[,param2…])]
{IS|AS}
variables_locales
BEGIN
instructions
EXCEPTION
instructions
END nom_proc;
Les sous-programmes
Spécification des procédures

 Exercice : Écrire une procédure qui retourne le nom et le grade de


l’employé le plus expérimenté d’un département donné
CREATE OR REPLACE PROCEDURE plus_exp(p_dpt IN NUMBER, p_nom OUT
VARCHAR2, p_grade OUT NUMBER)
IS
BEGIN
SELECT ename, grade INTO p_nom, p_grade FROM emp, salgrade
WHERE sal BETWEEN losal AND hisal AND deptno=p_dpt AND
hiredate=(SELECT MIN(hiredate) FROM emp WHERE deptno=p_dpt);
END plus_exp;
Les sous-programmes
Appel d’une procédure (1)

 Appel sous l’environnement SQL*PLUS


VARIABLE g_dept NUMBER;
VARIABLE g_nom VARCHAR2(20);
VARIABLE g_grade NUMBER;
BEGIN
:g_dept:=20;
END;
/
EXECUTE plus_exp(:g_dept,:g_nom,:g_grade);
PRINT g_nom;
PRINT g_grade;
Les sous-programmes
Appel d’une procédure (2)

 Appel dans un programme PL/SQL


SET SERVEROUTPUT ON
DECLARE
v_dept NUMBER;
v_nom VARCHAR2(20);
v_grade NUMBER;
BEGIN
v_dept:=20;
plus_exp(v_dept,v_nom,v_grade);
DBMS_OUTPUT.PUT_LINE('Le nom est '||v_nom||' de grade
'||v_grade);
END;
/
Les sous-programmes
Spécification des fonctions

CREATE [OR REPLACE] FUNCTION [schema].nom_proc


[(param1 [IN|OUT|IN OUT] typeSQL [{:=|DEFAULT} EXPRESSION]
[,param2…])]
RETURN typeSQL
{IS|AS}
variables_locales
BEGIN
instructions
EXCEPTION
instructions
END nom_proc;
Les sous-programmes
Spécification des fonctions

 Exercice : Écrire une fonction qui retourne le nombre d’employés d’un


département donné, dont le salaire est supérieur à un certain seuil.
CREATE OR REPLACE FUNCTION nb_emp(p_sal IN NUMBER, p_dpt IN
NUMBER) RETURN NUMBER
IS
v_nb NUMBER;
BEGIN
SELECT COUNT(*) INTO v_nb FROM emp
WHERE sal>p_sal AND deptno=p_dpt;
RETURN v_nb;
END nb_emp;
Les sous-programmes
Appel d’une fonction (1)

 Appel dans l’environnement SQL*PLUS (Avec EXECUTE)


VARIABLE g_dept NUMBER;
VARIABLE g_sal NUMBER;
VARIABLE g_nb NUMBER;
BEGIN
:g_dept:=20;
:g_sal:=1000;
END;
/
EXECUTE :g_nb:=nb_emp(:g_sal,:g_dept);
PRINT g_nb;
 Appel dans l’environnement SQL*PLUS (Dans une requête SQL)
SELECT dname, loc, nb_emp(1000,deptno) FROM dept;
Les sous-programmes
Appel d’une fonction (2)

 Appel dans un programme PL/SQL


SET SERVEROUTPUT ON
DECLARE
v_dept NUMBER;
v_sal NUMBER;
v_nb NUMBER;
BEGIN
v_dept:=20;
v_sal:=1000;
v_nb:=nb_emp(v_sal,v_dept);
DBMS_OUTPUT.PUT_LINE('Le nombre est '||v_nb);
END;
/
Les déclencheurs
Définition

 Un déclencheur est un programme associé à un évènement


particulier (insertion, modification ou suppression) effectué sur une
table ou une vue.
 À la différence d’un sous-programme, un déclencheur n’est pas
exécuté explicitement, il l’est automatiquement à la suite de
l’évènement de mise à jour qui le déclenche.
 Un déclencheur est donc caractérisé principalement par :
 L’évènement de mise à jour qui le déclenche
 Le programme (code) qui est exécuté à la suite de cet évènement
Les déclencheurs
Syntaxe

 Syntaxe de création d’un déclencheur :


CREATE [OR REPLACE] TRIGGER [schema.]nom_declencheur
{BEFORE|AFTER}
{DELETE|INSERT|UPDATE[OF col1[,col2…]]}
[OR {DELETE|INSERT|UPDATE[OF col1[,col2…]]}]…
ON {[schema.]nom_table|nom_vue}
[FOR EACH ROW]
[WHEN condition]
Bloc_PL/SQL
END;
 À noter que:
 :NEW.attribut et :OLD.attribut référencent les valeurs nouvelles/
anciennes d’un attribut
 INSERTING, UPDATING et DELETING sont des variables booléennes qui
renvoient TRUE s’il s’agit de l’évènement déclencheur, et FALSE sinon.
Les déclencheurs
Exemple

 Soit laORtable
CREATE MAJ qui
REPLACE inclut maj_emp
TRIGGER la trace des mises à jour effectuées sur la
AFTER INSERT
table EMP ORtable
. Cette DELETE OR les
inclut UPDATE ON emp
colonnes nature (avec les valeurs INS,
FOR EACH ROW
BEGIN et SUPP), date_maj (qui est la date de la mise à jour), la colonne
MODIF
IF INSERTING THEN
anc_val (qui inclut l’ancienne valeur du code de l’employé modifié ou
INSERT INTO maj VALUES(‘ins’,SYSDATE,null,:NEW.empno);
supprimé)
ELSIF UPDATINGet nouv_val
THEN (qui inclut la nouvelle valeur du code de
INSERT INTO maj
l’employé modifié ou inséré).
VALUES(‘mod’,SYSDATE,:OLD.empno,:NEW.empno);
ELSE -–il s’agit du scénario de la suppression
INSERT INTO maj VALUES(‘supp’,SYSDATE,:OLD.empno,null);
END IF;
END;
Le Tablespace

 Certains objets de la BD consomment de l’espace disque (Tables,


Indexes, etc.)
 Ces types d’objets sont stockés logiquement dans des unités qui
composent la BD, appelés tablespace
 Donc, une BD est divisé en unités logiques de stockage (les
tablespaces)
 Cette organisation d’une BD sous forme de tablespace permet une
administration plus flexible, puisque plusieurs opérations peuvent se
faire au niveau des tablespaces, et non de la totalité de la base :
- Sauvegarde/restauration, Mise en offline/online, etc.
Le Schéma

 Un schéma est un ensemble d’objets créés par un utilisateur (tables,


vues, indexes, synonymes, séquences, déclencheurs, procédures,
fonctions, packages PL/SQL)
 Par exemple, le schéma de l’utilisateur SCOTT = l’ensemble des objets
BD créés par SCOTT
 Il n’existe pas de relation entre schéma et tablespace. Les objets d’un
schéma peuvent être créés sur différentes tablespaces, et vice versa.
Les synonymes

 Un synonyme est un alias d’un objet base de données (tables, vues,


indexes, synonymes, séquences, déclencheurs, procédures, fonctions…)
 Si on crée le synonyme employee sur la table scott.emp, alors
manipuler employee, revient à manipuler scott.emp :
SELECT * FROM employee équivaut à SELECT * FROM scott.emp

 Il existe deux types de synonymes :


 Synonyme privé : accessible uniquement à partir du schéma dans
lequel il a été créé
CREATE SYNONYM nom_syn ON schema.objet

 Synonyme public : accessible à partir de tout schéma


CREATE PUBLIC SYNONYM nom_syn ON schema.objet
Gestion des utilisateurs (1)

 La syntaxe de création d’un utilisateur est la suivante :


CREATE USER nom_ut IDENTIFIED BY mdp
[DEFAULT TABLESPACE nom_def_TS]
[QUOTA {val|UNLIMITED} ON nom_TS]
[PASSWORD EXPIRE]
[ACCOUNT {LOCK|UNLOCK}]
 Exemple :
CREATE USER dev_esce IDENTIFIED BY jh#f@
DEFAULT TABLESPACE devloper_ts
QUOTA 50M ON developer_ts
QUOTA 25M ON appl_ts
PASSWORD EXPIRE
ACCOUNT LOCK;
Gestion des utilisateurs (2)

 La syntaxe de modification d’un utilisateur est la suivante :


ALTER USER nom_ut
[IDENTIFIED BY mdp]
[DEFAULT TABLESPACE nom_def_TS]
[QUOTA {val|UNLIMITED} ON nom_TS]
[PASSWORD EXPIRE]
[ACCOUNT {LOCK|UNLOCK}]
 Exemple :
ALTER USER dev_esce
QUOTA 100M ON developer_ts
ACCOUNT UNLOCK;
 Pour supprimer un utilisateur :
DROP USER nom_ut [CASCADE]
Gestion des utilisateurs (3)

 Il existe deux types de privilèges qu’on peut assigner aux utilisateurs


 Les privilèges systèmes (reliés au langage de définition et de contrôle
de données)
 Assigner un privilège CREATE, ALTER, DROP, GRANT, REVOKE à un
utilisateur spécifié
 Les privilèges objet (reliés au langage de manipulation de données)
 Assigner un privilège SELECT, INSERT, UPDATE, DELETE, EXECUTE
sur un objet spécifié à un utilisateur spécifié
Gestion des utilisateurs (4)

 Les privilèges systèmes :


 Exemple : CREATE SESSION, CREATE TABLE, CREATE VIEW, ALTER USER…
 SYNTAXE d’attribution :
GRANT priv1[,priv2…] TO {user1[,user2…]|PUBLIC}
 SYNTAXE de retirement:
REVOKE priv1[,priv2…] FROM {user1[,user2…]|PUBLIC};
Gestion des utilisateurs (5)

 Les privilèges objet:


 Exemple : INSERT, UPDATE(cols), SELECT etc.
 SYNTAXE d’attribution :
GRANT {priv1[(col1[,col2,…])][,priv2…]|ALL PRIVILEGES} ON
[schema.]obj TO {user1[,user2,…]|PUBLIC}
 SYNTAXE de retirement:
REVOKE {priv1[,priv2…]|ALL PRIVILEGES} ON [schema.]obj
FROM {user1[,user2…]|PUBLIC}
Bibliographie

[1] Christian Soutou. SQL pour Oracle. Éditions Eyrolles, 3è


édition, 576 pages, 2008.
[2] Aurélie Vuaroqueaux. Notions de base du PL/SQL.
Laboratoire supinfo des technologies Oracle, 63 pages, 2003.
oracle - dynamique - curseur implicite

Quelle est la différence entre les curseurs explicites et implicites dans Oracle? (10)

Avec les curseurs explicites, vous avez un contrôle total sur la façon d'accéder aux informations dans
la base de données. Vous décidez quand OUVRIR le curseur, quand FETCH enregistre à partir du
curseur (et donc de la table ou des tables dans l'instruction SELECT du curseur) combien
d'enregistrements aller chercher, et quand FERMER le curseur. Des informations sur l'état actuel de
votre curseur sont disponibles grâce à l'examen des attributs du curseur.

Voir http://www.unix.com.ua/orelly/oracle/prog2/ch06_03.htm pour plus de détails.

Je suis un peu rouillé sur mon jargon du curseur en PL / SQL. Quelqu'un sait-il cela?

Un curseur explicite est celui que vous déclarez, comme:

CURSOR my_cursor IS

SELECT table_name FROM USER_TABLES

Un curseur implicite est créé pour prendre en charge tout SQL en ligne que vous écrivez (statique ou
dynamique).

Ces curseurs implicites de ces jours sont plus efficaces que les curseurs explicites.

http://www.oracle.com/technology/oramag/oracle/04-sep/o54plsql.html

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1205168148688

Chaque instruction SQL exécutée par la base de données Oracle est associée à un curseur, qui est
une zone de travail privée pour stocker les informations de traitement. Les curseurs implicites sont
créés implicitement par le serveur Oracle pour toutes les instructions DML et SELECT.

Vous pouvez déclarer et utiliser des curseurs explicites pour nommer la zone de travail privée et
accéder à ses informations stockées dans votre bloc de programme.

Un curseur implicite est créé automatiquement par Oracle lorsque vous exécutez une requête. Il est
plus simple de coder, mais souffre de

 inefficacité (la norme ANSI spécifie qu'elle doit être extraite deux fois pour vérifier s'il y a plus
d'un enregistrement)

 vulnérabilité aux erreurs de données (si vous obtenez deux lignes, cela déclenche une
exception TOO_MANY_ROWS)

Exemple
SELECT col INTO var FROM table WHERE something;

Un curseur explicite est celui que vous créez vous-même. Cela prend plus de code, mais donne plus
de contrôle - par exemple, vous pouvez juste ouvrir-fetch-close si vous voulez seulement le premier
enregistrement et ne vous souciez pas s'il y en a d'autres.

Exemple

DECLARE

CURSOR cur IS SELECT col FROM table WHERE something;

BEGIN

OPEN cur;

FETCH cur INTO var;

CLOSE cur;

END;

En réponse à la première question Directement de la documentation Oracle

Un curseur est un pointeur vers une zone SQL privée qui stocke des informations sur le traitement
d'une instruction SELECT ou DML spécifique.

Un curseur est une fenêtre SELECTed sur une table Oracle, cela signifie un groupe d'enregistrements
présents dans une table Oracle et répondant à certaines conditions. Un curseur peut également
sélectionner tout le contenu d'une table. Avec un curseur, vous pouvez manipuler les colonnes
Oracle, en les aliasant dans le résultat. Un exemple de curseur implicite est le suivant:

BEGIN

DECLARE

CURSOR C1

IS

SELECT DROPPED_CALLS FROM ALARM_UMTS;

C1_REC C1%ROWTYPE;

BEGIN

FOR C1_REC IN C1

LOOP

DBMS_OUTPUT.PUT_LINE ('DROPPED CALLS: ' || C1_REC.DROPPED_CALLS);

END LOOP;
END;

END;

Avec FOR ... LOOP ... END LOOP vous ouvrez et fermez le curseur de façon automatique, lorsque les
enregistrements du curseur ont été tous analysés.

Un exemple de curseur explicite est le suivant:

BEGIN

DECLARE

CURSOR C1

IS

SELECT DROPPED_CALLS FROM ALARM_UMTS;

C1_REC C1%ROWTYPE;

BEGIN

OPEN c1;

LOOP

FETCH c1 INTO c1_rec;

EXIT WHEN c1%NOTFOUND;

DBMS_OUTPUT.PUT_LINE ('DROPPED CALLS: ' || C1_REC.DROPPED_CALLS);

END LOOP;

CLOSE c1;

END;

END;

Dans le curseur explicite, vous ouvrez et fermez le curseur de manière explicite, en vérifiant la
présence d'enregistrements et en indiquant une condition de sortie.
1.CURSOR: Lorsque PLSQL émet des instructions SQL, il crée une zone de travail privée pour analyser
et exécuter l'instruction sql s'appelle curseur.

2.IMPLICIT: lorsqu'un bloc PL / SQLexecutable génère une instruction sql. PL / SQL crée un curseur
implicite et gère automatiquement signifie impliquer open & close a lieu. Il est utilisé lorsque
l'instruction sql renvoie une seule ligne.Il possède 4 attributs SQL% ROWCOUNT, SQL% FOUND, SQL%
NOTFOUND, SQL% ISOPEN.

3.EXPLICIT: Il est créé et géré par le programmeur. Il a besoin d'ouvrir, de chercher et de fermer à
chaque fois. Il est utilisé lorsque l'instruction sql renvoie plusieurs lignes. Il a également 4 attributs
CUR_NAME% ROWCOUNT, CUR_NAME% FOUND, CUR_NAME% NOTFOUND, CUR_NAME% ISOPEN. Il
traite plusieurs lignes en utilisant la boucle. Le programmeur peut également transmettre le
paramètre au curseur explicite.

 Exemple: Curseur explicite

declare

cursor emp_cursor

is

select id,name,salary,dept_id

from employees;

v_id employees.id%type;

v_name employees.name%type;

v_salary employees.salary%type;

v_dept_id employees.dept_id%type;

begin

open emp_cursor;

loop

fetch emp_cursor into v_id,v_name,v_salary,v_dept_id;

exit when emp_cursor%notfound;

dbms_output.put_line(v_id||', '||v_name||', '||v_salary||','||v_dept_id);

end loop;

close emp_cursor;

end;

Les curseurs implicites nécessitent une mémoire tampon anonyme.

Les curseurs explicites peuvent être exécutés encore et encore en utilisant leur nom. Ils sont stockés
dans l'espace mémoire défini par l'utilisateur plutôt que d'être stockés dans une mémoire tampon
anonyme et peuvent donc être facilement accessibles par la suite.
Je sais que c'est une vieille question, cependant, je pense qu'il serait bon d'ajouter un exemple
pratique pour montrer la différence entre les deux du point de vue de la performance.

Du point de vue des performances, les curseurs implicites sont plus rapides.

Voyons la différence de performance entre les deux:

SQL> SET SERVEROUTPUT ON

SQL> DECLARE

2 l_loops NUMBER := 100000;

3 l_dummy dual.dummy%TYPE;

4 l_start NUMBER;

6 CURSOR c_dual IS

7 SELECT dummy

8 FROM dual;

9 BEGIN

10 l_start := DBMS_UTILITY.get_time;

11

12 FOR i IN 1 .. l_loops LOOP

13 OPEN c_dual;

14 FETCH c_dual

15 INTO l_dummy;

16 CLOSE c_dual;

17 END LOOP;

18

19 DBMS_OUTPUT.put_line('Explicit: ' ||

20 (DBMS_UTILITY.get_time - l_start) || ' hsecs');

21

22 l_start := DBMS_UTILITY.get_time;

23

24 FOR i IN 1 .. l_loops LOOP

25 SELECT dummy
26 INTO l_dummy

27 FROM dual;

28 END LOOP;

29

30 DBMS_OUTPUT.put_line('Implicit: ' ||

31 (DBMS_UTILITY.get_time - l_start) || ' hsecs');

32 END;

33 /

Explicit: 332 hsecs

Implicit: 176 hsecs

PL/SQL procedure successfully completed.

Ainsi, une différence significative est clairement visible.

Plus d'exemples ici .


qu’est-Ce que le CURSEUR en PL/SQL?

Un curseur est un pointeur vers cette zone de contexte. Oracle crée une zone de contexte pour le
traitement d’une instruction SQL qui contient toutes les informations sur l’instruction.

PL / SQL permet au programmeur de contrôler la zone de contexte via le curseur. Un curseur


contient les lignes renvoyées par L’instruction SQL. L’ensemble de lignes que le curseur contient est
appelé ensemble actif., Ces curseurs peuvent également être nommés afin qu’ils puissent être
référencés à partir d’un autre endroit du code.

Dans ce tutoriel, vous allez apprendre-

 Curseur Implicite

 Explicite Curseur

 les Attributs de Curseur

 POUR la Boucle Curseur de l’instruction

Le curseur est de deux types.

 Curseur Implicite

 Explicite Curseur

Curseur Implicite

Chaque fois que toutes les opérations DML dans la base de données, un curseur implicite est créé qui
contient les lignes concernées, en particulier l’opération., Ces curseurs ne peuvent pas être nommés
et, par conséquent, ils ne peuvent pas être contrôlés ou référencés à partir d’un autre endroit du
code. Nous ne pouvons faire référence qu’au curseur le plus récent à travers les attributs du curseur.

curseur explicite

Les programmeurs sont autorisés à créer une zone de contexte nommée pour exécuter leurs
opérations DML afin d’en avoir plus de contrôle. Le curseur explicite doit être défini dans la section
déclaration du bloc PL/SQL, et il est créé pour l’instruction ‘SELECT’ qui doit être utilisée dans le code.

Voici les étapes qui impliquaient de travailler avec des curseurs explicites.,

 déclarer le curseur

déclarer le curseur signifie simplement créer une zone de contexte nommée pour l’instruction
‘SELECT’ définie dans la partie déclaration. Le nom de cette zone de contexte est identique au nom
du curseur.

 ouvrir le curseur

l’ouverture du curseur demandera au PL / SQL d’allouer la mémoire pour ce curseur. Cela rendra le
curseur prêt à récupérer les enregistrements.

 récupération des données à partir du curseur

dans ce processus, L’instruction ‘SELECT’ est exécutée et les lignes récupérées sont stockées dans la
mémoire allouée., Ceux-ci sont maintenant appelés ensembles actifs. La récupération de données à
partir du curseur est une activité au niveau de l’enregistrement qui signifie que nous pouvons
accéder aux données de manière enregistrement par enregistrement.

chaque instruction fetch récupère un ensemble actif et contient les informations de cet
enregistrement particulier. Cette instruction est identique à l’instruction’ SELECT ‘qui récupère
l’enregistrement et affecte la variable dans la clause’ INTO’, mais elle ne lèvera aucune exception.

 fermeture du curseur

Une fois que tout l’enregistrement est récupéré maintenant, nous devons fermer le curseur afin que
la mémoire allouée à cette zone de contexte soit libérée.,

Syntaxe:

 Dans la syntaxe ci-dessus, la déclaration de partie contient la déclaration du curseur et le


variable dans laquelle les données extraites seront assignés.

 le curseur est créé pour L’instruction ‘SELECT’ qui est donnée dans la déclaration du curseur.

 dans la partie exécution, le curseur déclaré est ouvert, récupéré et fermé.

attributs du curseur

le curseur implicite et le curseur explicite ont certains attributs accessibles. Ces attributs donnent
plus d’informations sur les opérations du curseur., Vous trouverez ci-dessous les différents attributs
du curseur et leur utilisation.

attribut de
Description
curseur

il renvoie le résultat booléen « TRUE » si l’opération de récupération la plus récente a récupéré un


%FOUND
enregistrement avec succès, sinon il retournera FALSE.

cela fonctionne à l’opposé de %FOUND, il retournera ‘TRUE’ si l’opération de récupération la plus


%NOTFOUND
récente n’a pu récupérer aucun enregistrement.,

%ISOPEN Elle renvoie la valeur Booléenne « TRUE » si le curseur est déjà ouvert, sinon il renvoie ‘FALSE’

Elle retourne la valeur numérique. Il donne le nombre réel d’enregistrements qui ont été affectés pa
%ROWCOUNT
l’activité DML.

Exemple 1: Dans cet exemple, nous allons voir comment déclarer, ouvrir, d’extraire et de fermer le
curseur explicite.

Nous projetterons tout le nom de l’employé de la table emp à l’aide d’un curseur., Nous utiliserons
également l’attribut cursor pour définir la boucle pour récupérer tout l’enregistrement du curseur.
Sortie

Employee Fetched:BBBEmployee Fetched:XXXEmployee Fetched:YYY Total rows fetched is 3

Explication du Code:

 ligne de Code 2: déclaration du curseur guru99_det pour instruction « SELECT emp_name DE


pge ».

 ligne de Code 3: déclarer la variable lv_emp_name.

 ligne de Code 5: Ouverture du curseur guru99_det.

 ligne de Code 6: définir l’instruction de boucle de base pour récupérer tous les
enregistrements de la table ’emp’.,

 ligne de Code 7: récupère les données guru99_det et affecte la valeur à lv_emp_name.

 ligne de Code 9: utilisation de l’attribut de curseur ‘% NOTFOUND ‘ pour savoir si tout


l’enregistrement du curseur est récupéré. S’il est récupéré, il retournera ‘TRUE’ et le contrôle
quittera la boucle, sinon le contrôle continuera à récupérer les données du curseur et à
imprimer les données.

 ligne de Code 11: condition de sortie pour l’instruction loop.

 ligne de Code 12: affiche le nom de l’employé récupéré.,

 ligne de Code 14: utilisation de l’attribut de curseur ‘%ROWCOUNT’ pour trouver le nombre
total d’enregistrements affectés/récupérés dans le curseur.
 ligne de Code 15: après avoir quitté la boucle, le curseur est fermé et la mémoire allouée est
libérée.

POUR la Boucle Curseur de l’instruction

« POUR la BOUCLE » déclaration peut être utilisé pour travailler avec les curseurs. Nous pouvons
donner le nom du curseur au lieu de la limite de plage dans L’instruction FOR loop afin que la boucle
fonctionne du premier enregistrement du curseur au dernier enregistrement du curseur., La variable
cursor, l’ouverture du curseur, la récupération et la fermeture du curseur se feront implicitement par
la boucle FOR.

Syntaxe:

DECLARECURSOR <cursor_name> IS <SELECT statement>;BEGIN FOR I IN <cursor_name> LOOP . .


END LOOP;END;

 Dans la syntaxe ci-dessus, la déclaration de partie contient la déclaration du curseur.

 le curseur est créé pour L’instruction ‘SELECT’ qui est donnée dans la déclaration du curseur.

 dans la partie exécution, le curseur déclaré est configuré dans la boucle FOR et la variable de
boucle ‘I’ se comportera comme une variable de curseur dans ce cas.,

exemple 1: dans cet exemple, nous projetterons tout le nom de l’employé de la table emp à l’aide
d’une boucle cursor-FOR.

Sortie

Employee Fetched:BBB Employee Fetched:XXXEmployee Fetched:YYY

Explication du Code:

 ligne de Code 2: déclaration du curseur guru99_det pour instruction « SELECT emp_name DE


pge ».

 ligne de Code 4: Construction de la boucle ‘ FOR ‘ pour le curseur avec la variable de boucle
lv_emp_name.

 ligne de Code 5: Impression du nom de l’employé à chaque itération de la boucle.,

 ligne de Code 8: quitter la boucle

Remarque: Dans Cursor-for loop, les attributs du curseur ne peuvent pas être utilisés car l’ouverture,
la récupération et la fermeture du curseur se font implicitement par For loop.
Les déclencheurs Oracle (Triggers)
1) Introduction 9) Déclencheur LDD
2) Événements déclenchant 10) Déclencheur d'instance
3) Mécanisme général 11) Gestion des
4) Privilèges systèmes déclencheurs
5) Syntaxe 12) Nouveautés 11g
6) Nom du déclencheur
7) Option BEFORE ou Sources :
SQL Pour Oracle 3è édition de Christian Soutou Les
AFTER triggers d’Oracle de J. Akoka & I. Wattiau
http://sheikyerbouti.developpez.com/pl_sql/?page=Chap6
8) Déclencheur LMD
Les déclencheurs Oracle (Triggers)

1) Introduction
Les déclencheurs (triggers) sont des programmes associés à des objets dont
l'exécution est lancée automatiquement (déclenchée) lors d'une mise à jour ceuxci.
Ils se programment en PL/SQL; on peut aussi utiliser les langages C, C++, Java
etc ... pour créer des déclencheurs.
Ils permettent de
Programmer des règles de gestion qui n'ont pu être mises en places par des
contraintes au niveau des tables (contraintes multitables ...)
Déporter des contraintes au niveau serveur pour alléger le client
Programmer l'intégrité référentielle et la réplication dans les architectures
distribuées avec l'utilisation de liens de données (database links).
Les déclencheurs existent depuis la
version 6 d'Oracle. sont compilables
depuis la version 7.3
Permettent la mise à jour de vues multitables depuis la version 8 (instead of)
Les déclencheurs Oracle (Triggers)

2) Événements déclenchant
Les événements déclencheurs peuvent être
Une instruction insert, update ou delete sur une table ou une vue.
On parle de déclencheurs LMD
Une instruction create, alter ou drop sur un objet (table, index, séquence,
etc...) On parle de déclencheurs LDD
Le démarrage ou l'arrêt de la base (startup ou shutdown), une erreur
spécifique (no_data_found, dup_val_on_index, etc...), une connexion
ou une déconnexion d'un utilisateur. On parle de déclencheurs d'instances
Les déclencheurs Oracle (Triggers)

3) Mécanisme général
Une fois codé puis compilé, le déclencheur est stocké dans la base
En cas d'événement approprié, si le déclencheur est actif, il s'exécute
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 LMD ou bien une
seule fois pour la commande
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 LDD)
Les déclencheurs Oracle (Triggers)

4) Privilèges systèmes
Pour créer des déclencheurs, il faut disposer des privilèges systèmes
CREATE TRIGGER (présent avec le rôle RESOURCE)
CREATE ANY TRIGGER (pour ceux des autres schémas)
ADMINISTER DATABSE TRIGGER (pour les déclencheurs d'instances)

5) Syntaxe
La syntaxe de création du déclencheur commence par :

CREATE [OR REPLACE] TRIGGER [<schéma>].<nom du déclencheur>
Les déclencheurs Oracle (Triggers)

6) Nom du déclencheur doit être unique


dans un même schéma
peut être le nom d'un autre objet (table, vue, procédure) mais à éviter
Ex : CREATE TRIGGER trigInsertCoureur
Les déclencheurs Oracle (Triggers)

7) Option before ou after


Elle précise le moment de l'exécution du déclencheur (avant ou après
l'opération).
Les triggers after sont plus efficaces que les before parce qu'ils ne
nécessitent pas une double lecture des données.
Avec l’option before, le déclencheur s’exécute avant une éventuelle contrainte
d’intégrité portant sur la même table et/ou colonne.
Avec l’option after, le déclencheur s’exécutera peut être après une éventuelle
contrainte d’intégrité portant sur la même table et/ou colonne.
Ex : CREATE TRIGGER trigInsertCoureur before insert ...
Les déclencheurs Oracle (Triggers)

8) Déclencheur LMD
Ils sont lancés par une opération insert ou update ou delete
Le même déclencheur peut s'activer par les trois opérations
Pour update, on peut spécifier une liste de colonnes. Dans ce cas, le trigger
ne se déclenchera que si l'instruction update porte sur l'une au moins des
colonnes précisée dans la liste.
Ex : CREATE TRIGGER trigInsertCoureur before insert or delete
on tdf_coureur
Les déclencheurs Oracle (Triggers)

8-1) Déclencheur global ou ligne un déclencheur ligne (row trigger) est exécuté
pour chaque ligne concernée
par l'opération LMD. On le distingue par la directive " for each row ".
un déclencheur global ou d'état (statement trigger) ou d'instruction ne
s'exécute qu'une fois par instruction LMD
Ex : CREATE TRIGGER trigInsertCoureur before insert or
update on tdf_coureur for each row
Les déclencheurs Oracle (Triggers)

8-2) Déclencheur ligne avec ce type de déclencheur, on peut avoir accès (suivant
l'opération LMD) à
l'ancienne et/ou à la nouvelle donnée affectant la table. pour un insert,
toutes les colonnes de la ligne insérée sont accessibles.
Elles se nomment :new.<nom de la colonne> pour un delete, toutes les
colonnes de la ligne supprimée sont accessibles.
Elles se nomment :old.<nom de la colonne> pour un update, toutes les
colonnes de la ligne supprimée et insérées sont accessibles. Elles se
nomment :new.<nom de la colonne> :old.<nom de la colonne> il est
possible de restreindre l'exécution du déclencheur avec la clause when. Si
l'expression when n'est pas vérifiée le déclencheur ne s'exécute pas.
Ex CREATE TRIGGER trigInsertCoureur before insert or update on tdf_coureur for
each row when (new.code_tdf = 'FRA')

La clause referencing peut permettre de renommer new et old


Ex CREATE TRIGGER trigInsertCoureur before insert or update on tdf_coureur for
each row referencing NEW as nouveau
Les déclencheurs Oracle (Triggers)

avec Oracle
8-2) Déclencheur ligne (suite)
Lorsque le même déclencheur peut être exécuté à partir d'opérations LMD
différentes, il est possible de tester dans le programme l'événement
déclencheur avec les prédicats inserting, updating[('<colonne>')],
deleting.
Exemple de déclencheur ligne create or replace trigger
SONDAGE_TRIG1
AFTER insert on SONDAGE
for each row begin
dbms_output.put_line('début SONDAGE_TRIG1');
insert into sondage_copie values
(
:new.num,:new.date_naissance,
:new.reponse1, :new.reponse2,:new.val
);
dbms_output.put_line('fin SONDAGE_TRIG1');
end;
Les déclencheurs Oracle (Triggers)

8-3) Table en mutation


Un déclencheur ligne ne peut pas modifier la table concernée (appelée aussi
table mutante) par l'instruction (insert, update ou delete) qui a déclenché ce
trigger
Il peut éventuellement lire la table dans le cas d'un déclenchement par un
insert de type before

8-4) Déclencheur d'état


Ils n'ont pas accès aux valeurs mises à jours (:new et :old) par l'opération
puisqu'ils se déclenchent une seule fois même si la requête LMD met à jour
plusieurs lignes.
Ils présentent l'avantage de pouvoir effectuer des manipulations sur la table
qui a déclenché le trigger.
Les déclencheurs Oracle (Triggers)

8-5) Déclencheur Instead of il permet de mettre à jour une vue multitable


ce qui n'est pas toujours
possible avec une requête LMD standard. En effet la mise à jour n'est possible
dans une vue que si une table est protégée par clé
si on effectue la mise à jour uniquement dans cette table
(voir exemple page suivante) il utilise la
clause for each row (implicite) il n'utilise
pas la les options before et after
il ne permet pas de préciser le nom d'une colonne pour un update
il ne permet pas non plus d'utiliser la clause when
Les déclencheurs Oracle (Triggers)

8-5) Déclencheur Instead of (suite) création d'une vue modifiable (v_article_fournisseur1)

drop view v_article_fournisseur1; create view v_article_fournisseur1


as
select fo.fo_nom, ar.fo_numero,ar_numeroar_nom,ar_poids,ar_couleur,
ar_stock,ar_pa,ar_pv from cdi_article ar join cdi_fournisseur fo on
ar.fo_numero = fo.fo_numero;

drop view v_article_fournisseur2; create view


v_article_fournisseur2 as
select fo.fo_nom, fo.fo_numero,ar_numero,ar_nom,ar_poids,ar_couleur,
ar_stock,ar_pa,ar_pv from cdi_article ar
join cdi_fournisseur fo on ar.fo_numero = fo.fo_numero;
Les déclencheurs Oracle (Triggers)

8-5) Déclencheur Instead of (suite) insertion dans une vue modifiable


(v_article_fournisseur1)

insert into v_article_fournisseur1 (fo_numero,ar_numero,


ar_nom,ar_poids,ar_couleur,ar_stock,ar_pa,ar_pv) values
('F06','A85','GOMME','25','BLANC',20,1,2);

insert into v_article_fournisseur2 (fo_numero,ar_numero,


ar_nom,ar_poids,ar_couleur,ar_stock,ar_pa,ar_pv) values
('F06','A86','GOMME','25','BLANC',20,1,2);

select * from user_updatable_columns where lower(table_name) like


'v_article_fournisseur%' and column_name like '%NUMERO%';

OWNER TABLE_NAME COLUMN_NAME UPDATABLE INSERTABLE DELETABLE


CDI V_ARTICLE_FOURNISSEUR1 FO_NUMERO YES YES YES
CDI V_ARTICLE_FOURNISSEUR1 AR_NUMERO YES YES YES
CDI V_ARTICLE_FOURNISSEUR2 FO_NUMERO
CDI V_ARTICLE_FOURNISSEUR2 AR_NUMERO YES YES YES
Les déclencheurs Oracle (Triggers)

8-5) Déclencheur Instead of (suite) insertion dans une vue modifiable (v_article_fournisseur1)
create or replace trigger art_four_trig1 instead of insert on v_article_fournisseur1
declare
vNbFour int := 0; vNbArt int := 0;
begin
select count(*) into vNbFour from cdi_fournisseur where fo_numero=:new.fo_numero; select count(*) into vNbArt
from cdi_article where ar_numero = :new.ar_numero; if vnbart<>0 and vnbfour<>0 then
raise_application_error (-20001,'l''article et le fournisseur existent déjà'); elsif vnbart=0 then
insert into cdi_article (fo_numero,ar_numero,ar_nom,ar_poids,ar_couleur, ar_stock,ar_pa,ar_pv) values
(:new.fo_numero,:new.ar_numero,:new.ar_nom,
:new.ar_poids,:new.ar_couleur,:new.ar_stock,:new.ar_pa,:new.ar_pv); else
insert into cdi_fournisseur (fo_numero,fo_nom) values (:new.fo_numero,null); end if;
dbms_output.put_line('v_article_fournisseur1 terminé'); end;
Les déclencheurs Oracle (Triggers)

9) Déclencheur LDD
Ils réagissent aux modifications de la structure de la base de données Ils sont sensibles aux options before
et after la directive database précise que le déclencheur peut s'exécuter à partir d'un
événement provoqué par n'importe quel schéma
la directive schema précise que le déclencheur peut s'exécuter à partir d'un
événement provoqué par le schéma lui même
Les ordres LDD pouvant provoquer l'exécution du déclencheur sont : alter, comment, create, drop,
grant, rename, revoke
Ex :
create trigger majTDF before drop on iut123.schema
begin
if to_char(sysdate,'DAY') = 'DIMANCHE' then raise_application_error
(-20001,'pas de destruction le dimanche') ; end if ; end ;
Les déclencheurs Oracle (Triggers)

10) Déclencheur d'instance


Des événements systèmes peuvent provoquer le déclenchement d'un code
PL/SQL
Des événements comme logon, startup, serverrror, suspend
utilisent l'option after
Des événements comme LOGOFF, SHUTDOWN utilisent l'option before
Des événements comme AFTER STARUP et BEFORE SHUTDOWN s'appliquent
avec des déclencheurs de type DATABASE Ex :

create or replace
trigger deconnexion before LOGOFF on DATABASE begin
insert into trace values (user,sysdate);
end;
Les déclencheurs Oracle (Triggers)

11) Gestion des déclencheurs


Voici la liste des opérations de gestion des déclencheurs

ALTER TRIGGER <nom du déclencheur> COMPILE


ALTER TRIGGER <nom du déclencheur> DISABLE
ALTER TRIGGER <nom du déclencheur> ENABLE
ALTER TRIGGER <nom table> ENABLE ALL TRIGGERS
DROP TRIGGER <nom du déclencheur>
Les déclencheurs Oracle (Triggers)

12) Version 11g


Il est possible de créer des déclencheurs directement inactifs (disable)
Il est possible de choisir l'ordre d'exécution des déclencheurs d'un même
événement (utilisation de la directive follows)
Il est possible de créer des déclencheurs composés de plusieurs blocs
sensibles à des événements différents. Cela peut permettre de résoudre le
problème des tables mutantes.
Dans ce chapitre, nous allons discuter des fonctions dans PL/SQL. Une fonction est identique
à une procédure, sauf qu’elle renvoie une valeur. Par conséquent, toutes les discussions du
chapitre précédent sont également vraies pour les fonctions.

Creating a Function
A standalone function is created using the CREATE FUNCTION statement. The
simplified syntax for the CREATE OR REPLACE PROCEDURE statement is as
follows −
CREATE [OR REPLACE] FUNCTION function_name
[(parameter_name [IN | OUT | IN OUT] type [, ...])]
RETURN return_datatype
{IS | AS}
BEGIN
< function_body >
END [function_name];
Where,
 function-name specifies the name of the function.
 [OR REPLACE] option allows the modification of an existing function.
 The optional parameter list contains name, mode and types of the parameters.
IN represents the value that will be passed from outside and OUT represents
the parameter that will be used to return a value outside of the procedure.
 The function must contain a return statement.
 The RETURN clause specifies the data type you are going to return from the
function.
 function-body contains the executable part.
 The AS keyword is used instead of the IS keyword for creating a standalone
function.

Example
The following example illustrates how to create and call a standalone function. This
function returns the total number of CUSTOMERS in the customers table.
We will use the CUSTOMERS table, which we had created in the PL/SQL
Variables chapter −
Select * from customers;

+----+----------+-----+-----------+----------+
| ID | NAME | AGE | ADDRESS | SALARY |
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
| 2 | Khilan | 25 | Delhi | 1500.00 |
| 3 | kaushik | 23 | Kota | 2000.00 |
| 4 | Chaitali | 25 | Mumbai | 6500.00 |
| 5 | Hardik | 27 | Bhopal | 8500.00 |
| 6 | Komal | 22 | MP | 4500.00 |
+----+----------+-----+-----------+----------+
CREATE OR REPLACE FUNCTION totalCustomers
RETURN number IS
total number(2) := 0;
BEGIN
SELECT count(*) into total
FROM customers;

RETURN total;
END;
/
When the above code is executed using the SQL prompt, it will produce the following
result −
Function created.

Calling a Function
While creating a function, you give a definition of what the function has to do. To use
a function, you will have to call that function to perform the defined task. When a
program calls a function, the program control is transferred to the called function.
A called function performs the defined task and when its return statement is executed
or when the last end statement is reached, it returns the program control back to the
main program.
To call a function, you simply need to pass the required parameters along with the
function name and if the function returns a value, then you can store the returned
value. Following program calls the function totalCustomers from an anonymous
block −
DECLARE
c number(2);
BEGIN
c := totalCustomers();
dbms_output.put_line('Total no. of Customers: ' || c);
END;
/
When the above code is executed at the SQL prompt, it produces the following result

Total no. of Customers: 6

PL/SQL procedure successfully completed.

Example
The following example demonstrates Declaring, Defining, and Invoking a Simple
PL/SQL Function that computes and returns the maximum of two values.
DECLARE
a number;
b number;
c number;
FUNCTION findMax(x IN number, y IN number)
RETURN number
IS
z number;
BEGIN
IF x > y THEN
z:= x;
ELSE
Z:= y;
END IF;
RETURN z;
END;
BEGIN
a:= 23;
b:= 45;
c := findMax(a, b);
dbms_output.put_line(' Maximum of (23,45): ' || c);
END;
/
When the above code is executed at the SQL prompt, it produces the following result

Maximum of (23,45): 45

PL/SQL procedure successfully completed.

PL/SQL Recursive Functions


We have seen that a program or subprogram may call another subprogram. When a
subprogram calls itself, it is referred to as a recursive call and the process is known
as recursion.
To illustrate the concept, let us calculate the factorial of a number. Factorial of a
number n is defined as −
n! = n*(n-1)!
= n*(n-1)*(n-2)!
...
= n*(n-1)*(n-2)*(n-3)... 1
The following program calculates the factorial of a given number by calling itself
recursively −
DECLARE
num number;
factorial number;

FUNCTION fact(x number)


RETURN number
IS
f number;
BEGIN
IF x=0 THEN
f := 1;
ELSE
f := x * fact(x-1);
END IF;
RETURN f;
END;

BEGIN
num:= 6;
factorial := fact(num);
dbms_output.put_line(' Factorial '|| num || ' is ' ||
factorial);
END;
/
When the above code is executed at the SQL prompt, it produces the following result

Factorial 6 is 720

PL/SQL procedure successfully completed.


PL / SQL - Date et heure

Dans ce chapitre, nous discuterons de la date et de l'heure en PL / SQL. Il existe deux classes de types
de données liés à la date et à l'heure dans PL / SQL -

Types de données Datetime

Types de données d'intervalle

Les types de données Datetime sont -

DATE

TIMESTAMP

HORAIRE AVEC FUSEAU HORAIRE

HORAIRE AVEC FUSEAU HORAIRE LOCAL

Les types de données Intervalle sont -

INTERVALLE ANNÉE AU MOIS

INTERVALLE JOUR À DEUXIÈME

Valeurs de champ pour les types de données Datetime et Interval

Tous les deux datetime et interval les types de données se composent de fields. Les valeurs de ces
champs déterminent la valeur du type de données. Le tableau suivant répertorie les champs et leurs
valeurs possibles pour les dates-heures et les intervalles.

Nom de domaine Valeurs Datetime valides Valeurs d'intervalle valides

AN -4712 à 9999 (hors année 0) Tout entier différent de zéro

MOIS 01 à 12 0 à 11

JOURNÉE 01 à 31 (limité par les valeurs de MONTH et YEAR, selon les règles du calendrier pour
les paramètres régionaux) Tout entier différent de zéro

HEURE 00 à 23 0 à 23

MINUTE 00 à 59 0 à 59

SECONDE

00 à 59,9 (n), où 9 (n) est la précision des fractions de seconde


La partie 9 (n) n'est pas applicable pour DATE.

0 à 59,9 (n), où 9 (n) est la précision des fractions de seconde d'intervalle

TIMEZONE_HOUR

-12 à 14 (la plage s'adapte aux changements d'heure d'été)

Non applicable pour DATE ou TIMESTAMP.

N'est pas applicable

TIMEZONE_MINUTE

00 à 59

Non applicable pour DATE ou TIMESTAMP.

N'est pas applicable

TIMEZONE_REGION Non applicable pour DATE ou TIMESTAMP. N'est pas applicable

TIMEZONE_ABBR Non applicable pour DATE ou TIMESTAMP. N'est pas applicable

Les types et fonctions de données Datetime

Voici les types de données Datetime -

DATE

Il stocke les informations de date et d'heure dans les types de données caractère et numérique. Il est
composé d'informations sur le siècle, l'année, le mois, la date, l'heure, les minutes et les secondes. Il
est spécifié comme -

HORAIRE

C'est une extension du type de données DATE. Il stocke l'année, le mois et le jour du type de données
DATE, ainsi que les valeurs d'heure, de minute et de seconde. Il est utile pour stocker des valeurs de
temps précises.

HORAIRE AVEC FUSEAU HORAIRE


Il s'agit d'une variante de TIMESTAMP qui inclut un nom de région de fuseau horaire ou un décalage
de fuseau horaire dans sa valeur. Le décalage du fuseau horaire est la différence (en heures et
minutes) entre l'heure locale et UTC. Ce type de données est utile pour collecter et évaluer les
informations de date dans les régions géographiques.

HORAIRE AVEC FUSEAU HORAIRE LOCAL

C'est une autre variante de TIMESTAMP qui inclut un décalage de fuseau horaire dans sa valeur.

Le tableau suivant fournit les fonctions Datetime (où, x a la valeur datetime) -

S. Non Nom de la fonction et description

ADD_MONTHS(x, y);

Ajoute y mois à x.

LAST_DAY(x);

Renvoie le dernier jour du mois.

MONTHS_BETWEEN(x, y);

Renvoie le nombre de mois entre x et y.

NEXT_DAY(x, day);

Renvoie la date et l' heure du jour suivant aprèsx.

5
NEW_TIME;

Renvoie la valeur heure / jour d'un fuseau horaire spécifié par l'utilisateur.

ROUND(x [, unit]);

Les manches x.

sept

SYSDATE();

Renvoie la date / heure actuelle.

TRUNC(x [, unit]);

Tronque x.

Fonctions d'horodatage (où, x a une valeur d'horodatage) -

S. Non Nom de la fonction et description

CURRENT_TIMESTAMP();

Renvoie un TIMESTAMP WITH TIME ZONE contenant l'heure actuelle de la session ainsi que le fuseau
horaire de la session.

EXTRACT({ YEAR | MONTH | DAY | HOUR | MINUTE | SECOND } | { TIMEZONE_HOUR |


TIMEZONE_MINUTE } | { TIMEZONE_REGION | } TIMEZONE_ABBR ) FROM x)
Extrait et renvoie une année, un mois, un jour, une heure, une minute, une seconde ou un fuseau
horaire de x.

FROM_TZ(x, time_zone);

Convertit TIMESTAMP x et le fuseau horaire spécifié par time_zone en TIMESTAMP WITH TIMEZONE.

LOCALTIMESTAMP();

Renvoie un TIMESTAMP contenant l'heure locale dans le fuseau horaire de la session.

SYSTIMESTAMP();

Renvoie un TIMESTAMP WITH TIME ZONE contenant l'heure actuelle de la base de données ainsi que
le fuseau horaire de la base de données.

SYS_EXTRACT_UTC(x);

Convertit TIMESTAMP WITH TIMEZONE x en un TIMESTAMP contenant la date et l'heure en UTC.

sept

TO_TIMESTAMP(x, [format]);

Convertit la chaîne x en TIMESTAMP.

TO_TIMESTAMP_TZ(x, [format]);
Convertit la chaîne x en TIMESTAMP WITH TIMEZONE.

Exemples

Les extraits de code suivants illustrent l'utilisation des fonctions ci-dessus -

Example 1

SELECT SYSDATE FROM DUAL;

Output -

08/31/2012 5:25:34 PM

Example 2

SELECT TO_CHAR(CURRENT_DATE, 'DD-MM-YYYY HH:MI:SS') FROM DUAL;

Output -

31-08-2012 05:26:14

Example 3

SELECT ADD_MONTHS(SYSDATE, 5) FROM DUAL;

Output -

01/31/2013 5:26:31 PM

Example 4

SELECT LOCALTIMESTAMP FROM DUAL;

Output -

8/31/2012 5:26:55.347000 PM

Les types et fonctions de données d'intervalle

Voici les types de données d'intervalle -


IINTERVAL DE L'ANNÉE AU MOIS - Il stocke une période de temps en utilisant les champs datetime
YEAR et MONTH.

INTERVALLE JOUR À SECONDE - Il stocke une période de temps en termes de jours, heures, minutes
et secondes.

Fonctions d'intervalle

S. Non Nom de la fonction et description

NUMTODSINTERVAL(x, interval_unit);

Convertit le nombre x en INTERVALLE DAY TO SECOND.

NUMTOYMINTERVAL(x, interval_unit);

Convertit le nombre x en INTERVALLE ANNÉE EN MOIS.

TO_DSINTERVAL(x);

Convertit la chaîne x en INTERVAL DAY TO SECOND.

TO_YMINTERVAL(x);

Convertit la chaîne x en INTERVAL YEAR TO MONTH.


Les CURSORS (ou curseurs en français) sont des boucles qu’on utilise pour faire des itérations sur le
résultat d’une requête SELECT ligne par ligne.

Exemple, on peut créer un curseur qui va boucler sur la table EMP avec le PRENOM commençant par
B. Pour chaque ligne de ce résultat, on va afficher les informations de cet employé.

Deux Type de curseurs existent :

1 . Les curseurs explicites:

Les curseurs explicites sont déclarés dans la partie de déclaration du Bloc PL/SQL :

DECLARE

CURSOR <nom_du_curseur> IS

SELECT ... FROM ... ;

BEGIN

...

END;

Exemple : Un curseur qui comporte tous les employés de la table EMP qui occupent un poste
de MANAGER

DECLARE

CURSOR curseur_EMPLOYE IS

SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER';

BEGIN

...

END;

Une fois le curseur déclaré, on pourra l’utiliser dans le Bloc PL/SQL après le BEGIN. On commence
d’abord par l’ouvrir avant de la parcourir.

Pour ouvrir un curseur :

DECLARE

CURSOR curseur_EMPLOYE IS

SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER';

BEGIN
OPEN curseur_EMPLOYE ;

...

END;

Après l’ouverture du curseur, nous pouvons le parcourir et faire autant d’itération possible que de
lignes retournées par la requête du curseur. On utilise la boucle LOOP … END LOOP pour les
itérations :

DECLARE

CURSOR curseur_EMPLOYE IS

SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER';

BEGIN

OPEN curseur_EMPLOYE ;

LOOP

...

END LOOP;

END;

Une fois nous somme dans la boucle, on doit affecter la ligne en cours à une ou des variables selon
les colonnes sélectionnées dans notre SELECT. Pour cela on utilise FETCH … INTO :

DECLARE

V_ENAME EMP.ENAME%TYPE;

V_MGR EMP.MGR%TYPE;

V_SAL EMP.SAL%TYPE;

CURSOR curseur_EMPLOYE IS

SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER';

BEGIN

OPEN curseur_EMPLOYE ;

LOOP

FETCH curseur_EMPLOYE INTO V_ENAME, V_MGR, V_SAL;

...
END LOOP;

END;

Enfin, il faudra ajouter la condition de sortie de la boucle. Généralement la condition de sortie est
que nous avons parcouru toutes les lignes retournées :

DECLARE

V_ENAME EMP.ENAME%TYPE;

V_MGR EMP.MGR%TYPE;

V_SAL EMP.SAL%TYPE;

CURSOR curseur_EMPLOYE IS

SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER';

BEGIN

OPEN curseur_EMPLOYE ;

LOOP

FETCH curseur_EMPLOYE INTO V_ENAME, V_MGR, V_SAL;

...

EXIT WHEN NOT curseur_EMPLOYE%FOUND;

/* On peut utiliser aussi NOTFOUND comme suit

EXIT WHEN curseur_EMPLOYE%NOTFOUND;

*/

END LOOP;

END;

Le curseur est mis en place, nous pouvons exploiter et manipuler ses données selon notre
besoin puis fermer ce curseur :

DECLARE

V_ENAME EMP.ENAME%TYPE;

V_MGR EMP.MGR%TYPE;

V_SAL EMP.SAL%TYPE;
CURSOR curseur_EMPLOYE IS

SELECT ENAME, MGR, SAL FROM EMP WHERE JOB = 'MANAGER';

BEGIN

OPEN curseur_EMPLOYE ;

LOOP

FETCH curseur_EMPLOYE INTO V_ENAME, V_MGR, V_SAL;

DBMS_OUTPUT.PUT_LINE('L''employé ' || V_ENAME || ' a un salaire de ' || V_SAL ||

'. Le numéro de son Manager est ' || V_MGR);

EXIT WHEN NOT curseur_EMPLOYE%FOUND;

END LOOP;

CLOSE curseur_EMPLOYE ;

END;

Le résultat de ce Block PL/SQL est le suivant :

L'employé JONES a un salaire de 2975. Le numéro de son Manager est 7839

L'employé BLAKE a un salaire de 2850. Le numéro de son Manager est 7839

L'employé CLARK a un salaire de 2450. Le numéro de son Manager est 7839

Nous pouvons utiliser * dans le SELECT du curseur, dans ce cas, la déclaration de la variable qui
comportera les lignes de curseur sera du même type que la table sélectionnée ROWTYPE. Par-contre
l’appel de chaque colonne sera <nom_curseur>.<nom_colonne> :

DECLARE

V_EMP EMP%ROWTYPE;

CURSOR curseur_EMPLOYE IS

SELECT * FROM EMP WHERE JOB = 'MANAGER';

BEGIN

OPEN curseur_EMPLOYE ;
LOOP

FETCH curseur_EMPLOYE INTO V_EMP ;

DBMS_OUTPUT.PUT_LINE('L''employé ' || V_EMP.ENAME || ' a un salaire de ' ||

V_EMP.SAL || '. Le numéro de son Manager est ' || V_EMP.MGR);

EXIT WHEN NOT curseur_EMPLOYE%FOUND;

END LOOP;

CLOSE curseur_EMPLOYE ;

END;

Le deuxième type de curseur est implicite

2 . Les curseurs implicites:

Les curseurs implicites sont plus facile à utiliser que ceux explicites. On ne déclare pas de curseur, ni
on l’ouvre ni on le ferme. On utilise toujours la boucle FOR :

FOR <nom_curseur> IN (<requte_SELECT>)

LOOP

...

END LOOP;

On reprend le dernier exemple cette fois ci avec un curseur explicite :

DECLARE

BEGIN

FOR curseur_EMPLOYE IN (SELECT * FROM EMP WHERE JOB = 'MANAGER')

LOOP

DBMS_OUTPUT.PUT_LINE('L''employé ' || curseur_EMPLOYE.ENAME ||

' a un salaire de ' || curseur_EMPLOYE.SAL ||

'. Le numéro de son Manager est ' || curseur_EMPLOYE.MGR);

END LOOP;

END;

Le résultat est toujours le même dans ce cas:

L'employé JONES a un salaire de 2975. Le numéro de son Manager est 7839


L'employé BLAKE a un salaire de 2850. Le numéro de son Manager est 7839

L'employé CLARK a un salaire de 2450. Le numéro de son Manager est 7839


RECORDS EN PL/SQL - STRUCTURES DE DONNÉES

Les records sont des variables composites qui peuvent stocker des valeurs de
données de différents types, similaires à un type struct en C, C++ ou Java.En PL
SQL, les record sont utiles pour contenir des données de lignes de table ou certaines
colonnes de lignes de table.
PL/SQL peut gérer les types de records suivants :
 Basés sur une table/vue/curseur
 définis par l'utilisateur.
Vous pouvez définir des types RECORD et déclarer des records définis par
l'utilisateur dans la partie déclarative(DECLARE) de n'importe quel bloc, sous-
programme ou package.
Record basé sur une table|vue|curseur
L'attribut %ROWTYPE vous permet de déclarer un enregistrement PL/SQL qui
représente une ligne dans une table de base de données, sans lister toutes les
colonnes. Votre code continue de fonctionner même après l'ajout de colonnes à la
table. Si vous souhaitez représenter un sous-ensemble de colonnes dans une table
ou des colonnes de différentes tables, vous pouvez définir une vue ou déclarer un
curseur pour sélectionner les colonnes souhaitées et effectuer les jointures
nécessaires, puis appliquer %ROWTYPE à la vue ou au curseur.
Syntaxe

?
1 nom_record nom_cols%ROWTYPE;
Avec :
 nom_record - Un nom approprié pour le record.
 nom_cols - Nom de la table ou la vue ou le curseur
Exemple:

L'exemple suivant crée un record basé sur la table Employes, donc le record contient
la structure de ligne dans cette table :
?
1 DECLARE
2 rec_emp Employes%ROWTYPE;
3 BEGIN
4 -- corps du programme
END;
5
Supposons que nous souhaitons créer un record contenant les colonnes: nom, âge
et nom du département de chaque employé. il est clair que les colonnes proviennent
de différentes tables. Pour créer cet record, il faut d'abord créer une vue ou un
curseur qui sélectionne ces colonnes puis associer ce curseur ou vue au record.
?
1 DECLARE
CURSOR liste_cols IS SELECT E.Nom, E.Age, D.Nom_dep FROM Employes E INNER JOIN Dep
2 rec_emp liste_cols%ROWTYPE;
3 BEGIN
4 -- corps du programme
5 END;
6
Record défini par l'utilisateur
Le type RECORD est défini comme suit :
Syntaxe

?
1 TYPE nom_record IS RECORD (
2 champ1 typedonnees1 [NOT NULL] [:= expression_par_defaut],
3 champ2 typedonnees2 [NOT NULL] [:= expression_par_defaut],
4 ...
5 champN typedonneesN [NOT NULL] [:= expression_par_defaut);
);
6
Avec :
 nom_record - Un nom approprié pour le record.
 champ(i) - Nom de l'attribut ou la colonne
 typedonnees(i) - Type de données associé a chaque attribut
 expression_par_defaut - Une valeur par défaut.
Un record peut être initialisé dans sa déclaration. Vous pouvez utiliser
l'attribut %TYPE pour spécifier le type de données d'un champ. Vous pouvez ajouter
la contrainte NOT NULL à n'importe quelle déclaration de champ pour empêcher
l'attribution de valeurs nulles à ce champ. Les champs déclarés comme NOT NULL
doivent être initialisés.
Pour référencer des champs individuels dans un record, vous utilisez la
notation nom_rec.nom_champ. Par exemple, pour référencer le champ Nom dans
le record rec_emp, vous utiliseriez rec_emp.Nom
Exemple 1:

L'exemple suivant crée un record nommé Personne,


?
1
2 DECLARE
3 -- autres déclarations
TYPE Personne IS RECORD (
4 Id NUMBER NOT NULL
5 Titre VARCHAR(30),
6 Age NUMBER,
7 Departement VARCHAR(30)
8 );
-- autres déclarations
9
10 BEGIN
11 -- Corps du programme
12 END;
13
Vous pouvez maintenant créer n'importe quelle variable de type record, utiliser le
record comme paramètre d'une fonction, procédure et valeur de retour d'une
fonction.
?
1 p1 Personne;
Au lieu d'attribuer des valeurs séparément à chaque champ d'un record, vous pouvez
attribuer des valeurs à tous les champs à la fois:
 Vous pouvez affecter un record défini par l'utilisateur à un autre s'ils ont le même
type de données. (Il ne suffit pas d'avoir des champs qui correspondent
exactement.) Vous pouvez attribuer un record %ROWTYPE à un record défini par
l'utilisateur si leurs champs correspondent en nombre et en ordre, et que les
champs correspondants ont des types de données compatibles.
 Vous pouvez utiliser l'instruction SELECT ou FETCH pour extraire les valeurs de
colonnes dans un record. Les colonnes de la liste de sélection doivent apparaître
dans le même ordre que les champs de votre record.
Exemple 2:

?
1
DECLARE
2
emp1 Employes%ROWTYPE; -- record basé sur la structure d'une ligne de la table
3 BEGIN
4 SELECT * INTO emp1 FROM Employes WHERE Id=3;
5
6 -- afficher les données du record
7 dbms_output.put_line('Nom : '|| emp1.Nom);
dbms_output.put_line('Salaire : '|| emp1.Salaire);
8 dbms_output.put_line('Age : '|| emp1.Age);
9 END;
10
Exemple 3:

Vous pouvez transmettre un record en tant que paramètre de sous-programme tout


comme vous transmettez toute autre variable.
?
1 DECLARE
2 -- autres déclarations
TYPE Personne IS RECORD (
3 Id NUMBER NOT NULL
4 Nom VARCHAR(30),
5 Age NUMBER,
6 Departement VARCHAR(30)
7 );
p1 Personne; -- une variable de type personne
8
9 PROCEDURE afficher(p Personne) IS
10 BEGIN
11 dbms_output.put_line('ID : '|| p.Id);
12 dbms_output.put_line('Nom : '|| p.Nom);
dbms_output.put_line('Age : '|| p.Age);
13 dbms_output.put_line('Departement : '|| p.Departement);
14 END;
15 -- autres déclarations
16 BEGIN
17 -- Initialiser le record p1
p1.Id=1;
18 p1.Nom='Ismail';
19 p1.Age=27;
20 p1.Departement='Informatique';
21
22 -- Afficher les informations de la personne p1
23 afficher(p1);
END;
24
25
26
27
28
PL / SQL - Curseurs
Dans ce chapitre, nous aborderons les curseurs en PL / SQL. Oracle crée une zone de mémoire,
appelée zone de contexte, pour le traitement d'une instruction SQL, qui contient toutes les
informations nécessaires au traitement de l'instruction; par exemple, le nombre de lignes traitées,
etc.

UNE cursorest un pointeur vers cette zone de contexte. PL / SQL contrôle la zone de contexte via un
curseur. Un curseur contient les lignes (une ou plusieurs) renvoyées par une instruction SQL.
L'ensemble de lignes que contient le curseur est appeléactive set.

Vous pouvez nommer un curseur afin qu'il puisse être référencé dans un programme pour récupérer
et traiter les lignes renvoyées par l'instruction SQL, une à la fois. Il existe deux types de curseurs -

 Curseurs implicites

 Curseurs explicites

Curseurs implicites

Les curseurs implicites sont automatiquement créés par Oracle chaque fois qu'une instruction SQL
est exécutée, lorsqu'il n'y a pas de curseur explicite pour l'instruction. Les programmeurs ne peuvent
pas contrôler les curseurs implicites et les informations qu'ils contiennent.

Chaque fois qu'une instruction DML (INSERT, UPDATE et DELETE) est émise, un curseur implicite est
associé à cette instruction. Pour les opérations INSERT, le curseur contient les données qui doivent
être insérées. Pour les opérations UPDATE et DELETE, le curseur identifie les lignes qui seraient
affectées.

En PL / SQL, vous pouvez faire référence au curseur implicite le plus récent comme SQL cursor, qui a
toujours des attributs tels que %FOUND, %ISOPEN, %NOTFOUND, et %ROWCOUNT. Le curseur SQL
a des attributs supplémentaires,%BULK_ROWCOUNT et %BULK_EXCEPTIONS, conçu pour être utilisé
avec le FORALLdéclaration. Le tableau suivant fournit la description des attributs les plus utilisés -

S. Attribut et description
Non

1 %FOUND

Renvoie TRUE si une instruction INSERT, UPDATE ou DELETE affectait une ou plusieurs lignes ou si une instru
ou plusieurs lignes. Sinon, il renvoie FALSE.

2 %NOTFOUND

Le contraire logique de% FOUND. Elle renvoie TRUE si une instruction INSERT, UPDATE ou DELETE n'affecte a
SELECT INTO ne renvoie aucune ligne. Sinon, il renvoie FALSE.

3 %ISOPEN
Renvoie toujours FALSE pour les curseurs implicites, car Oracle ferme automatiquement le curseur SQL aprè
SQL associée.

4 %ROWCOUNT

Renvoie le nombre de lignes affectées par une instruction INSERT, UPDATE ou DELETE, ou renvoyées par une

Tout attribut de curseur SQL sera accessible comme sql%attribute_name comme indiqué ci-dessous
dans l'exemple.

Exemple

Nous utiliserons la table CUSTOMERS que nous avons créée et utilisée dans les chapitres précédents.

Select * from customers;

+----+----------+-----+-----------+----------+

| ID | NAME | AGE | ADDRESS | SALARY |

+----+----------+-----+-----------+----------+

| 1 | Armand | 32 | Tacin | 2000.00 |

| 2 | Kilian | 25 | Paris | 1500.00 |

| 3 | Clarc | 23 | Kota | 2000.00 |

| 4 | Charles | 25 | Marseille | 6500.00 |

| 5 | Hadrien | 27 | Bordeaux | 8500.00 |

| 6 | Louis | 22 | MP | 4500.00 |

+----+----------+-----+-----------+----------+

Le programme suivant mettra à jour le tableau et augmentera le salaire de chaque client de 500 et
utilisera le SQL%ROWCOUNT attribut pour déterminer le nombre de lignes affectées -

DECLARE

total_rows number(2);

BEGIN

UPDATE customers

SET salary = salary + 500;

IF sql%notfound THEN

dbms_output.put_line('no customers selected');

ELSIF sql%found THEN

total_rows := sql%rowcount;
dbms_output.put_line( total_rows || ' customers selected ');

END IF;

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

6 customers selected

PL/SQL procedure successfully completed.

Si vous vérifiez les enregistrements dans la table des clients, vous constaterez que les lignes ont été
mises à jour -

Select * from customers;

+----+----------+-----+-----------+----------+

| ID | NAME | AGE | ADDRESS | SALARY |

+----+----------+-----+-----------+----------+

| 1 | Armand | 32 | Tacin | 2500.00 |

| 2 | Kilian | 25 | Paris | 2000.00 |

| 3 | Clarc | 23 | Kota | 2500.00 |

| 4 | Charles | 25 | Marseille | 7000.00 |

| 5 | Hadrien | 27 | Bordeaux | 9000.00 |

| 6 | Louis | 22 | MP | 5000.00 |

+----+----------+-----+-----------+----------+

Curseurs explicites

Les curseurs explicites sont des curseurs définis par le programmeur pour obtenir plus de contrôle
sur le context area. Un curseur explicite doit être défini dans la section déclaration du bloc PL / SQL.
Il est créé sur une instruction SELECT qui renvoie plusieurs lignes.

La syntaxe pour créer un curseur explicite est -

CURSOR cursor_name IS select_statement;

Travailler avec un curseur explicite comprend les étapes suivantes -

 Déclaration du curseur d'initialisation de la mémoire

 Ouverture du curseur pour allouer la mémoire

 Récupération du curseur pour récupérer les données


 Fermer le curseur pour libérer la mémoire allouée

Déclaration du curseur

La déclaration du curseur définit le curseur avec un nom et l'instruction SELECT associée. Par
exemple -

CURSOR c_customers IS

SELECT id, name, address FROM customers;

Ouverture du curseur

L'ouverture du curseur alloue la mémoire pour le curseur et le rend prêt à récupérer les lignes
retournées par l'instruction SQL. Par exemple, nous allons ouvrir le curseur défini ci-dessus comme
suit -

OPEN c_customers;

Récupération du curseur

La récupération du curseur implique d'accéder à une ligne à la fois. Par exemple, nous allons
récupérer les lignes du curseur ouvert ci-dessus comme suit -

FETCH c_customers INTO c_id, c_name, c_addr;

Fermer le curseur

Fermer le curseur signifie libérer la mémoire allouée. Par exemple, nous fermerons le curseur ouvert
ci-dessus comme suit -

CLOSE c_customers;

Exemple

Voici un exemple complet pour illustrer les concepts de curseurs explicites & minua;

DECLARE

c_id customers.id%type;

c_name customer.name%type;

c_addr customers.address%type;

CURSOR c_customers is

SELECT id, name, address FROM customers;

BEGIN

OPEN c_customers;

LOOP

FETCH c_customers into c_id, c_name, c_addr;

EXIT WHEN c_customers%notfound;

dbms_output.put_line(c_id || ' ' || c_name || ' ' || c_addr);


END LOOP;

CLOSE c_customers;

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

1 Armand Tacin

2 Kilian Paris

3 Clarc Kota

4 Charles Marseille

5 Hadrien Bordeaux

6 LouisMP

PL/SQL procedure successfully completed.


PL / SQL - Enregistrements
Dans ce chapitre, nous aborderons les enregistrements en PL / SQL. UNErecordest une structure de
données qui peut contenir des éléments de données de différents types. Les enregistrements se
composent de différents champs, similaires à une ligne d'une table de base de données.

Par exemple, vous souhaitez garder une trace de vos livres dans une bibliothèque. Vous souhaiterez
peut-être suivre les attributs suivants pour chaque livre, tels que le titre, l'auteur, le sujet, l'ID du
livre. Un enregistrement contenant un champ pour chacun de ces éléments permet de traiter un
LIVRE comme une unité logique et vous permet d'organiser et de mieux représenter ses
informations.

PL / SQL peut gérer les types d'enregistrements suivants -

 Table-based

 Enregistrements basés sur le curseur

 Enregistrements définis par l'utilisateur

Enregistrements basés sur des tables

L'attribut% ROWTYPE permet à un programmeur de créer table-based et cursorbased records.

L'exemple suivant illustre le concept de table-basedrecords. Nous utiliserons la table CUSTOMERS


que nous avons créée et utilisée dans les chapitres précédents -

DECLARE

customer_rec customers%rowtype;

BEGIN

SELECT * into customer_rec

FROM customers

WHERE id = 5;

dbms_output.put_line('Customer ID: ' || customer_rec.id);

dbms_output.put_line('Customer Name: ' || customer_rec.name);

dbms_output.put_line('Customer Address: ' || customer_rec.address);

dbms_output.put_line('Customer Salary: ' || customer_rec.salary);

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

Customer ID: 5

Customer Name: Hadrien

Customer Address: Bordeaux

Customer Salary: 9000


PL/SQL procedure successfully completed.

Enregistrements basés sur le curseur

L'exemple suivant illustre le concept de cursor-basedrecords. Nous utiliserons la table CUSTOMERS


que nous avons créée et utilisée dans les chapitres précédents -

DECLARE

CURSOR customer_cur is

SELECT id, name, address

FROM customers;

customer_rec customer_cur%rowtype;

BEGIN

OPEN customer_cur;

LOOP

FETCH customer_cur into customer_rec;

EXIT WHEN customer_cur%notfound;

DBMS_OUTPUT.put_line(customer_rec.id || ' ' || customer_rec.name);

END LOOP;

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

1 Armand

2 Kilian

3 Clarc

4 Charles

5 Hadrien

6 Louis

PL/SQL procedure successfully completed.

Enregistrements définis par l'utilisateur

PL / SQL fournit un type d'enregistrement défini par l'utilisateur qui vous permet de définir les
différentes structures d'enregistrement. Ces enregistrements se composent de différents champs.
Supposons que vous souhaitiez garder une trace de vos livres dans une bibliothèque. Vous
souhaiterez peut-être suivre les attributs suivants pour chaque livre -

 Title

 Author

 Subject

 ID du livre

Définition d'un enregistrement

Le type d'enregistrement est défini comme -

TYPE

type_name IS RECORD

( field_name1 datatype1 [NOT NULL] [:= DEFAULT EXPRESSION],

field_name2 datatype2 [NOT NULL] [:= DEFAULT EXPRESSION],

...

field_nameN datatypeN [NOT NULL] [:= DEFAULT EXPRESSION);

record-name type_name;

L'enregistrement de livre est déclaré de la manière suivante -

DECLARE

TYPE books IS RECORD

(title varchar(50),

author varchar(50),

subject varchar(100),

book_id number);

book1 books;

book2 books;

Accès aux champs

Pour accéder à n'importe quel champ d'un enregistrement, nous utilisons le point (.)opérateur.
L'opérateur d'accès aux membres est codé comme un point entre le nom de la variable
d'enregistrement et le champ auquel nous souhaitons accéder. Voici un exemple pour expliquer
l'utilisation de record -

DECLARE

type books is record

(title varchar(50),
author varchar(50),

subject varchar(100),

book_id number);

book1 books;

book2 books;

BEGIN

-- Book 1 specification

book1.title := 'C Programming';

book1.author := 'Nuha Ali ';

book1.subject := 'C Programming Tutorial';

book1.book_id := 6495407;

-- Book 2 specification

book2.title := 'Telecom Billing';

book2.author := 'Zara Ali';

book2.subject := 'Telecom Billing Tutorial';

book2.book_id := 6495700;

-- Print book 1 record

dbms_output.put_line('Book 1 title : '|| book1.title);

dbms_output.put_line('Book 1 author : '|| book1.author);

dbms_output.put_line('Book 1 subject : '|| book1.subject);

dbms_output.put_line('Book 1 book_id : ' || book1.book_id);

-- Print book 2 record

dbms_output.put_line('Book 2 title : '|| book2.title);

dbms_output.put_line('Book 2 author : '|| book2.author);

dbms_output.put_line('Book 2 subject : '|| book2.subject);

dbms_output.put_line('Book 2 book_id : '|| book2.book_id);

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -
Book 1 title : C Programming

Book 1 author : Nuha Ali

Book 1 subject : C Programming Tutorial

Book 1 book_id : 6495407

Book 2 title : Telecom Billing

Book 2 author : Zara Ali

Book 2 subject : Telecom Billing Tutorial

Book 2 book_id : 6495700

PL/SQL procedure successfully completed.

Enregistrements en tant que paramètres de sous-programme

Vous pouvez transmettre un enregistrement en tant que paramètre de sous-programme comme


vous transmettez toute autre variable. Vous pouvez également accéder aux champs
d'enregistrement de la même manière que vous avez accédé dans l'exemple ci-dessus -

DECLARE

type books is record

(title varchar(50),

author varchar(50),

subject varchar(100),

book_id number);

book1 books;

book2 books;

PROCEDURE printbook (book books) IS

BEGIN

dbms_output.put_line ('Book title : ' || book.title);

dbms_output.put_line('Book author : ' || book.author);

dbms_output.put_line( 'Book subject : ' || book.subject);

dbms_output.put_line( 'Book book_id : ' || book.book_id);

END;

BEGIN

-- Book 1 specification
book1.title := 'C Programming';

book1.author := 'Nuha Ali ';

book1.subject := 'C Programming Tutorial';

book1.book_id := 6495407;

-- Book 2 specification

book2.title := 'Telecom Billing';

book2.author := 'Zara Ali';

book2.subject := 'Telecom Billing Tutorial';

book2.book_id := 6495700;

-- Use procedure to print book info

printbook(book1);

printbook(book2);

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

Book title : C Programming

Book author : Nuha Ali

Book subject : C Programming Tutorial

Book book_id : 6495407

Book title : Telecom Billing

Book author : Zara Ali

Book subject : Telecom Billing Tutorial

Book book_id : 6495700

PL/SQL procedure successfully completed.


PL / SQL - Exceptions
Dans ce chapitre, nous aborderons les exceptions en PL / SQL. Une exception est une condition
d'erreur lors de l'exécution d'un programme. PL / SQL aide les programmeurs à détecter ces
conditions en utilisantEXCEPTIONbloc dans le programme et une action appropriée est prise contre
la condition d'erreur. Il existe deux types d'exceptions -

 Exceptions définies par le système

 Exceptions définies par l'utilisateur

Syntaxe pour la gestion des exceptions

La syntaxe générale de la gestion des exceptions est la suivante. Ici, vous pouvez lister autant
d'exceptions que vous pouvez gérer. L'exception par défaut sera gérée en utilisantWHEN others
THEN -

DECLARE

<declarations section>

BEGIN

<executable command(s)>

EXCEPTION

<exception handling goes here >

WHEN exception1 THEN

exception1-handling-statements

WHEN exception2 THEN

exception2-handling-statements

WHEN exception3 THEN

exception3-handling-statements

........

WHEN others THEN

exception3-handling-statements

END;

Exemple

Écrivons un code pour illustrer le concept. Nous utiliserons la table CUSTOMERS que nous avons
créée et utilisée dans les chapitres précédents -

DECLARE

c_id customers.id%type := 8;

c_name customerS.Name%type;
c_addr customers.address%type;

BEGIN

SELECT name, address INTO c_name, c_addr

FROM customers

WHERE id = c_id;

DBMS_OUTPUT.PUT_LINE ('Name: '|| c_name);

DBMS_OUTPUT.PUT_LINE ('Address: ' || c_addr);

EXCEPTION

WHEN no_data_found THEN

dbms_output.put_line('No such customer!');

WHEN others THEN

dbms_output.put_line('Error!');

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

No such customer!

PL/SQL procedure successfully completed.

Le programme ci-dessus affiche le nom et l'adresse d'un client dont l'ID est donné. Puisqu'il n'y a
aucun client avec la valeur d'ID 8 dans notre base de données, le programme lève l'exception
d'exécutionNO_DATA_FOUND, qui est capturé dans le EXCEPTION block.

Lever des exceptions

Les exceptions sont déclenchées automatiquement par le serveur de base de données chaque fois
qu'il y a une erreur de base de données interne, mais les exceptions peuvent être déclenchées
explicitement par le programmeur à l'aide de la commande RAISE. Voici la syntaxe simple pour lever
une exception -

DECLARE

exception_name EXCEPTION;

BEGIN

IF condition THEN

RAISE exception_name;

END IF;
EXCEPTION

WHEN exception_name THEN

statement;

END;

Vous pouvez utiliser la syntaxe ci-dessus pour déclencher l'exception standard Oracle ou toute
exception définie par l'utilisateur. Dans la section suivante, nous vous donnerons un exemple sur la
levée d'une exception définie par l'utilisateur. Vous pouvez lever les exceptions standard Oracle de la
même manière.

Exceptions définies par l'utilisateur

PL / SQL vous permet de définir vos propres exceptions en fonction des besoins de votre programme.
Une exception définie par l'utilisateur doit être déclarée puis déclenchée explicitement, à l'aide d'une
instruction RAISE ou de la procédureDBMS_STANDARD.RAISE_APPLICATION_ERROR.

La syntaxe pour déclarer une exception est -

DECLARE

my-exception EXCEPTION;

Exemple

L'exemple suivant illustre le concept. Ce programme demande un identifiant client, lorsque


l'utilisateur entre un identifiant invalide, l'exceptioninvalid_id est soulevé.

DECLARE

c_id customers.id%type := &cc_id;

c_name customerS.Name%type;

c_addr customers.address%type;

-- user defined exception

ex_invalid_id EXCEPTION;

BEGIN

IF c_id <= 0 THEN

RAISE ex_invalid_id;

ELSE

SELECT name, address INTO c_name, c_addr

FROM customers

WHERE id = c_id;

DBMS_OUTPUT.PUT_LINE ('Name: '|| c_name);

DBMS_OUTPUT.PUT_LINE ('Address: ' || c_addr);


END IF;

EXCEPTION

WHEN ex_invalid_id THEN

dbms_output.put_line('ID must be greater than zero!');

WHEN no_data_found THEN

dbms_output.put_line('No such customer!');

WHEN others THEN

dbms_output.put_line('Error!');

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

Enter value for cc_id: -6 (let's enter a value -6)

old 2: c_id customers.id%type := &cc_id;

new 2: c_id customers.id%type := -6;

ID must be greater than zero!

PL/SQL procedure successfully completed.

Exceptions prédéfinies

PL / SQL fournit de nombreuses exceptions prédéfinies, qui sont exécutées lorsqu'une règle de base
de données est violée par un programme. Par exemple, l'exception prédéfinie NO_DATA_FOUND est
déclenchée lorsqu'une instruction SELECT INTO ne renvoie aucune ligne. Le tableau suivant
répertorie quelques-unes des exceptions prédéfinies importantes -

Exception Erreur SQLCODE La description


Oracle

ACCESS_INTO_NULL 06530 -6530 Il est déclenché lorsqu'un objet nul reçoit automatiquement une

CASE_NOT_FOUND 06592 -6592 Il est déclenché lorsqu'aucun des choix de la clause WHEN d'une
et qu'il n'y a pas de clause ELSE.

COLLECTION_IS_NULL 06531 -6531 Il est déclenché lorsqu'un programme tente d'appliquer des mét
EXISTS à une table imbriquée ou varray non initialisée, ou lorsqu
des valeurs aux éléments d'une table imbriquée ou varray non in
DUP_VAL_ON_INDEX 00001 -1 Il est déclenché lorsque des valeurs en double sont tentées d'êtr
un index unique.

INVALID_CURSOR 01001 -1001 Il est déclenché lorsque des tentatives sont effectuées pour effec
non autorisée, comme la fermeture d'un curseur non ouvert.

NUMÉRO INVALIDE 01722 -1722 Il est déclenché lorsque la conversion d'une chaîne de caractères
chaîne ne représente pas un nombre valide.

LOGIN_DENIED 01017 -1017 Il est déclenché lorsqu'un programme tente de se connecter à la


d'utilisateur ou un mot de passe non valide.

AUCUNE DONNÉE 01403 +100 Il est déclenché lorsqu'une instruction SELECT INTO ne renvoie a
DISPONIBLE

NOT_LOGGED_ON 01012 -1012 Il est déclenché lorsqu'un appel de base de données est émis san
données.

PROGRAM_ERROR 06501 -6501 Il est déclenché lorsque PL / SQL a un problème interne.

ROWTYPE_MISMATCH 06504 -6504 Il est déclenché lorsqu'un curseur récupère une valeur dans une
incompatible.

SELF_IS_NULL 30625 -30625 Il est déclenché lorsqu'une méthode membre est appelée, mais q
pas été initialisée.

STORAGE_ERROR 06500 -6500 Il est déclenché lorsque PL / SQL a manqué de mémoire ou que l

TOO_MANY_ROWS 01422 -1422 Il est déclenché lorsqu'une instruction SELECT INTO renvoie plus

VALUE_ERROR 06502 -6502 Il est déclenché lorsqu'une erreur d'arithmétique, de conversion


de taille se produit.

ZERO_DIVIDE 01476 1476 Il est déclenché lorsqu'une tentative est faite pour diviser un nom
PL / SQL - Déclencheurs
Dans ce chapitre, nous aborderons les déclencheurs en PL / SQL. Les déclencheurs sont des
programmes stockés, qui sont automatiquement exécutés ou déclenchés lorsque certains
événements se produisent. Les déclencheurs sont, en fait, écrits pour être exécutés en réponse à l'un
des événements suivants -

 UNE database manipulation (DML) instruction (DELETE, INSERT ou UPDATE)

 UNE database definition (DDL) instruction (CREATE, ALTER ou DROP).

 UNE database operation (SERVERERROR, LOGON, LOGOFF, STARTUP ou SHUTDOWN).

Les déclencheurs peuvent être définis sur la table, la vue, le schéma ou la base de données à laquelle
l'événement est associé.

Avantages des déclencheurs

Les déclencheurs peuvent être écrits aux fins suivantes -

 Générer automatiquement des valeurs de colonne dérivées

 Faire respecter l'intégrité référentielle

 Journalisation des événements et stockage des informations sur l'accès aux tables

 Auditing

 Réplication synchrone des tables

 Imposer des autorisations de sécurité

 Empêcher les transactions invalides

Créer des déclencheurs

La syntaxe pour créer un déclencheur est -

CREATE [OR REPLACE ] TRIGGER trigger_name

{BEFORE | AFTER | INSTEAD OF }

{INSERT [OR] | UPDATE [OR] | DELETE}

[OF col_name]

ON table_name

[REFERENCING OLD AS o NEW AS n]

[FOR EACH ROW]

WHEN (condition)

DECLARE

Declaration-statements

BEGIN

Executable-statements
EXCEPTION

Exception-handling-statements

END;

Où,

 CREATE [OR REPLACE] TRIGGER trigger_name - Crée ou remplace un trigger existant


par trigger_name .

 {AVANT | APRÈS | INSTEAD OF} - Ceci spécifie quand le déclencheur sera exécuté. La clause
INSTEAD OF est utilisée pour créer un déclencheur sur une vue.

 {INSÉRER [OU] | MISE À JOUR [OU] | DELETE} - Ceci spécifie l'opération DML.

 [OF col_name] - Ceci spécifie le nom de la colonne qui sera mis à jour.

 [ON nom_table] - Ceci spécifie le nom de la table associée au déclencheur.

 [REFERENCING OLD AS o NEW AS n] - Cela vous permet de faire référence aux valeurs
nouvelles et anciennes pour diverses instructions DML, telles que INSERT, UPDATE et DELETE.

 [FOR EACH ROW] - Ceci spécifie un déclencheur au niveau de la ligne, c'est-à-dire que le
déclencheur sera exécuté pour chaque ligne affectée. Sinon, le déclencheur ne s'exécutera
qu'une seule fois lorsque l'instruction SQL est exécutée, ce qui est appelé un déclencheur de
niveau table.

 WHEN (condition) - Ceci fournit une condition pour les lignes pour lesquelles le déclencheur
serait déclenché. Cette clause n'est valide que pour les déclencheurs de niveau ligne.

Exemple

Pour commencer, nous utiliserons la table CUSTOMERS que nous avons créée et utilisée dans les
chapitres précédents -

Select * from customers;

+----+----------+-----+-----------+----------+

| ID | NAME | AGE | ADDRESS | SALARY |

+----+----------+-----+-----------+----------+

| 1 | Armand | 32 | Tacin | 2000.00 |

| 2 | Kilian | 25 | Paris | 1500.00 |

| 3 | Clarc | 23 | Kota | 2000.00 |

| 4 | Charles | 25 | Marseille | 6500.00 |

| 5 | Hadrien | 27 | Bordeaux | 8500.00 |

| 6 | Louis | 22 | MP

| 4500.00 |
+----+----------+-----+-----------+----------+

Le programme suivant crée un row-leveldéclencheur pour la table clients qui se déclencherait pour
les opérations INSERT ou UPDATE ou DELETE effectuées sur la table CUSTOMERS. Ce déclencheur
affichera la différence de salaire entre les anciennes valeurs et les nouvelles valeurs -

CREATE OR REPLACE TRIGGER display_salary_changes

BEFORE DELETE OR INSERT OR UPDATE ON customers

FOR EACH ROW

WHEN (NEW.ID > 0)

DECLARE

sal_diff number;

BEGIN

sal_diff := :NEW.salary - :OLD.salary;

dbms_output.put_line('Old salary: ' || :OLD.salary);

dbms_output.put_line('New salary: ' || :NEW.salary);

dbms_output.put_line('Salary difference: ' || sal_diff);

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

Trigger created.

Les points suivants doivent être considérés ici -

 Les références ANCIENNES et NOUVELLES ne sont pas disponibles pour les déclencheurs de
niveau table, vous pouvez plutôt les utiliser pour les déclencheurs de niveau enregistrement.

 Si vous souhaitez interroger la table dans le même déclencheur, vous devez utiliser le mot clé
AFTER, car les déclencheurs peuvent interroger la table ou la modifier à nouveau
uniquement après que les modifications initiales ont été appliquées et que la table est
revenue dans un état cohérent.

 Le déclencheur ci-dessus a été écrit de telle manière qu'il se déclenchera avant toute
opération DELETE, INSERT ou UPDATE sur la table, mais vous pouvez écrire votre déclencheur
sur une ou plusieurs opérations, par exemple BEFORE DELETE, qui se déclenchera chaque fois
qu'un enregistrement sera supprimé à l'aide de l'opération DELETE sur la table.

Déclencher un déclencheur

Faisons quelques opérations DML sur la table CUSTOMERS. Voici une instruction INSERT, qui créera
un nouvel enregistrement dans la table -

INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)

VALUES (7, 'Kriti', 22, 'HP', 7500.00 );


Lorsqu'un enregistrement est créé dans la table CUSTOMERS, le déclencheur de création ci-
dessus, display_salary_changes sera déclenché et affichera le résultat suivant -

Old salary:

New salary: 7500

Salary difference:

Comme il s'agit d'un nouvel enregistrement, l'ancien salaire n'est pas disponible et le résultat ci-
dessus est nul. Exécutons maintenant une autre opération DML sur la table CUSTOMERS.
L'instruction UPDATE mettra à jour un enregistrement existant dans la table -

UPDATE customers

SET salary = salary + 500

WHERE id = 2;

Lorsqu'un enregistrement est mis à jour dans la table CUSTOMERS, le déclencheur de création ci-
dessus, display_salary_changes sera déclenché et affichera le résultat suivant -

Old salary: 1500

New salary: 2000

Salary difference: 500


PL / SQL - Packages
Dans ce chapitre, nous aborderons les packages en PL / SQL. Les packages sont des objets de schéma
qui regroupent les types, variables et sous-programmes PL / SQL liés de manière logique.

Un colis aura deux parties obligatoires -

 Spécification du paquet

 Corps ou définition du package

Spécification du paquet

La spécification est l'interface avec le package. C'est juste DECLARES les types, variables, constantes,
exceptions, curseurs et sous-programmes qui peuvent être référencés de l'extérieur du package. En
d'autres termes, il contient toutes les informations sur le contenu du package, mais exclut le code
des sous-programmes.

Tous les objets placés dans la spécification sont appelés public objets. Tout sous-programme ne
figurant pas dans la spécification du package mais codé dans le corps du package est appelé
private objet.

L'extrait de code suivant montre une spécification de package ayant une seule procédure. Vous
pouvez définir de nombreuses variables globales et plusieurs procédures ou fonctions dans un
package.

CREATE PACKAGE cust_sal AS

PROCEDURE find_sal(c_id customers.id%type);

END cust_sal;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

Package created.

Corps du paquet

Le corps du package contient les codes des différentes méthodes déclarées dans la spécification du
package et d'autres déclarations privées, qui sont masquées dans le code en dehors du package.

le CREATE PACKAGE BODY L'instruction est utilisée pour créer le corps du package. L'extrait de code
suivant montre la déclaration du corps du package pour le cust_sal package créé ci-dessus. J'ai
supposé que nous avions déjà créé la table CUSTOMERS dans notre base de données comme
mentionné dans le chapitre PL / SQL - Variables .

CREATE OR REPLACE PACKAGE BODY cust_sal AS

PROCEDURE find_sal(c_id customers.id%TYPE) IS

c_sal customers.salary%TYPE;

BEGIN
SELECT salary INTO c_sal

FROM customers

WHERE id = c_id;

dbms_output.put_line('Salary: '|| c_sal);

END find_sal;

END cust_sal;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

Package body created.

Utilisation des éléments du package

Les éléments du package (variables, procédures ou fonctions) sont accessibles avec la syntaxe
suivante -

package_name.element_name;

Considérez que nous avons déjà créé le package ci-dessus dans notre schéma de base de données, le
programme suivant utilise le find_sal méthode de la cust_sal paquet -

DECLARE

code customers.id%type := &cc_id;

BEGIN

cust_sal.find_sal(code);

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il vous invite à entrer l'ID client et lorsque vous
entrez un ID, il affiche le salaire correspondant comme suit -

Enter value for cc_id: 1

Salary: 3000

PL/SQL procedure successfully completed.

Exemple

Le programme suivant fournit un package plus complet. Nous utiliserons la table CUSTOMERS
stockée dans notre base de données avec les enregistrements suivants -

Select * from customers;


+----+----------+-----+-----------+----------+

| ID | NAME | AGE | ADDRESS | SALARY |

+----+----------+-----+-----------+----------+

| 1 | Ray | 32 | Tacin | 3000.00 |

| 2 | Kilian | 25 | Paris | 3000.00 |

| 3 | Clarc | 23 | Kota | 3000.00 |

| 4 | Charles | 25 | Marseille | 7500.00 |

| 5 | Hadrien | 27 | Bordeaux | 9500.00 |

| 6 | Louis | 22 | MP | 5500.00 |

+----+----------+-----+-----------+----------+

La spécification du paquet

CREATE OR REPLACE PACKAGE c_package AS

-- Adds a customer

PROCEDURE addCustomer(c_id customers.id%type,

c_name customerS.No.ame%type,

c_age customers.age%type,

c_addr customers.address%type,

c_sal customers.salary%type);

-- Removes a customer

PROCEDURE delCustomer(c_id customers.id%TYPE);

--Lists all customers

PROCEDURE listCustomer;

END c_package;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il crée le package ci-dessus et affiche le résultat
suivant -

Package created.

Création du corps du package

CREATE OR REPLACE PACKAGE BODY c_package AS


PROCEDURE addCustomer(c_id customers.id%type,

c_name customerS.No.ame%type,

c_age customers.age%type,

c_addr customers.address%type,

c_sal customers.salary%type)

IS

BEGIN

INSERT INTO customers (id,name,age,address,salary)

VALUES(c_id, c_name, c_age, c_addr, c_sal);

END addCustomer;

PROCEDURE delCustomer(c_id customers.id%type) IS

BEGIN

DELETE FROM customers

WHERE id = c_id;

END delCustomer;

PROCEDURE listCustomer IS

CURSOR c_customers is

SELECT name FROM customers;

TYPE c_list is TABLE OF customers.Name%type;

name_list c_list := c_list();

counter integer :=0;

BEGIN

FOR n IN c_customers LOOP

counter := counter +1;

name_list.extend;

name_list(counter) := n.name;

dbms_output.put_line('Customer(' ||counter|| ')'||name_list(counter));

END LOOP;

END listCustomer;
END c_package;

L'exemple ci-dessus utilise le nested table. Nous discuterons du concept de table imbriquée dans le
prochain chapitre.

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

Package body created.

Utilisation du package

Le programme suivant utilise les méthodes déclarées et définies dans le package c_package .

DECLARE

code customers.id%type:= 8;

BEGIN

c_package.addcustomer(7, 'Rajnish', 25, 'Chennai', 3500);

c_package.addcustomer(8, 'Sylvain', 32, 'Paris', 7500);

c_package.listcustomer;

c_package.delcustomer(code);

c_package.listcustomer;

END;

Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -

Customer(1): Ray

Customer(2): Kilian

Customer(3): Clarc

Customer(4): Charles

Customer(5): Hadrien

Customer(6): Louis

Customer(7): Henri

Customer(8): Sylvain

Customer(1): Ray

Customer(2): Kilian

Customer(3): Clarc
Customer(4): Charles

Customer(5): Hadrien

Customer(6): Mathieu

Customer(7): Henri

PL/SQL procedure successfully completed


$ sqlplus ou sqldeveloper

User :system

Password : oracle
1. Voici la table Employees :

+-------------+------------+-----------+----------+
| Employee_id | First_name | Last_name | Salary |
+-------------+------------+-----------+----------+
| 100 | Bob | Kinto | 1000000 |
| 2 | Jerry | Kansxo | 6000000 |
| 3 | Philip | Jose | 8900000 |
| 4 | John | Abraham | 2000000 |
| 5 | Michael | Mathew | 2200000 |
| 6 | Alex | chreketo | 4000000 |
| 7 | Yohan | Soso | 1230000 |
+-------------+------------+-----------+----------+
Oracle : Langage PL/SQL

1 Introduction à PL/SQL
PL/SQL est un langage de programmation procédural et structuré.

1.1 Langage de programmation

Il contient un ensemble d'instructions permettant de mettre en oeuvre les principes de l'algorithmique.


- Déclaration de variables, de types, de fonctions, de procédures
- Instructions d'aectation, conditionnelles, itératives
- Fonctions numériques et de manipulations de chaînes de caractères, de dates

1.2 Intégration du langage SQL

- Commande SELECT
- Commande INSERT, UPDATE, DELETE
- Commande de gestion de transaction COMMIT, ROLLBACK, SAVEPOINT
- Utilisation de fonctions SQL : TO_CHAR, TO_NUMBER, SUBSTR, ...

1.3 Gestion de curseurs

- Instructions DECLARE, OPEN, FETCH, CLOSE

1.4 Gestion des erreurs

- Gestion standard des erreurs par ORACLE


- Dénition et gestion de traitements d'erreurs propres au développeur

1.5 PL/SQL dans le monde ORACLE

- disponible dans les outils de développement ORACLE*FORMS, SQL*plus, ...


- dénition de CI, de triggers, de procédures stockées.

2 Les blocs PL/SQL


Le bloc est l'unité de programmation PL/SQL.

Structure d'un bloc SQL


DECLARE
/* Déclaration des variables, des types, des curseurs, fonctions et procédures */
BEGIN
/* Instructions PL/SQL ; tout instruction est terminée par ; */
EXCEPTION
/* Traitement des erreurs */
END ;
Les blocs peuvent être imbriqués.

2.1 DECLARE

2.1.1 Déclarations de variables


 Variables simples
Syntaxe
Nom_var [CONSTANT] Type_Donnée [NOT NULL] [ :=expr_pl/sql] ;
Exemples :

COMPTEUR Number;

TOTAL Number(8,2);

1
QUOTA Number Default 10;

NBR_MIN CONSTANT Number :=1;

NBR_MAX Number Default NBR_MIN*QUOTA;

V_NOM Varchar2(50);

V_NUM Number NOT NULL :=1;


 Variables %TYPE
Syntaxe
Nom_var [CONSTANT] Identiant%TYPE [NOT NULL] [ :=expr_pl/sql] ;
Déclaration d'une variable faisant référence à l'identiant. Le type de la variable sera celui de l'identiant. L'iden-
tiant doit être le nom d'une variable préalablement dénie dans le bloc ou le nom d'un attribut d'une table de
la base de données.
Exemples :
V_COMPT COMPTEUR%TYPE;

V_NOMP PRIX.NOMP%TYPE;
 Variables %ROWTYPE
Syntaxe
Nom_var [CONSTANT] Identiant%ROWTYPE [NOT NULL] [ :=expr_pl/sql] ;
Déclaration d'une variable correspondant à une matrice ayant la même structure (nom et type) que ceux de l'iden-
tiant. L'identiant doit être le nom d'une variable curseur préalablement dénie dans le bloc ou le nom d'une table
de la base de données. Un attribut ou champ de la matrice sera accédé par la syntaxe Nom_var.Nom_attribut.
Exemples :
V_PRIX PRIX%ROWTYPE;

V_CURS CURSEUR1%ROWTYPE;
 Variables Curseur
Le programmeur ne déclare dans la section DECLARE que les curseurs dits explicites c'est à dire une zone mémoire
associée à une requête select qui permet de récupérer les n-uplets de la requête.
Syntaxe
CURSOR nom_curseur [(nom_paramètre type_paramètre)] IS requête_select [FOR UPDATE OF nom_attribut]
Exemples :
CURSOR CURSEUR1 IS
Select nomp,cout
From Prix
Where nomf='SAMACO';

CURSOR Bonclient(V_solde number(8)) IS


Select nomc,adresc
From client
Where solde>V_solde;

2.1.2 Déclaration de Types


 Type spécique RECORD
Syntaxe
TYPE Nom_type IS RECORD (nom_champ1 Type_Donnée [NOT NULL], (nom_champ2 Type_Donnée [NOT
NULL],....) ;
Type_Donnée tous les types de variables possibles NOT NULL les champs correspondants devront être initialisés
Exemples :
TYPE T_Date IS RECORD
(JOUR INTEGER,
MOIS INTEGER,
ANNEE INTEGER);

V_Date T_Date;

TYPE T_Client IS RECORD

2
(NOM VARCHAR2(30),
PRENOM VARCHAR2(30),
DATE_NAIS T_Date);

 Type spécique TABLE


Syntaxe
TYPE Nom_type IS TABLE OF type_attribut [NOT NULL], INDEX BY BINARY_INTEGER ;

type_attribut # Type de données de l'attribut concerné

NOT NULL # Ne pas insérer de valeur nulle dans un n-uplet de la table

INDEX BY BINARY_INTEGER # Création d'un attribut contenant l'indice du


n-uplet de type obligatoire BINARY_INTEGER
Exemple :
TYPE ADRESSE IS TABLE OF
CHAR(20)
INDEX BY BINARY_INTEGER;

TABLE_ADRESSE ADRESSE;

INDICE BINARY_INTEGER;

2.1.3 Déclaration de Procédures


Les procédures doivent être déclarées en n de section DECLARE.
Une procédure peut être considérée comme un sous_bloc réalisant un traitement spécique. Une procédure ne peut
être utilisée que dans le bloc qui la déclare. Une procédure est constituée de deux parties : l'en_tête et le bloc de
déclaration.
Syntaxe
PROCEDURE Nom_procédure [ (Paramètre1, ...) ] IS
 déclaration de variables locales
BEGIN
 instructions [EXCEPTIONS
 gestion des erreurs]
END [Nom_procédure] ;
Paramètres
nom_param [IN | OUT | IN OUT ] TYPE [ := exp_pl/sql]
IN modications non prises en compte (par valeur)
OUT valeur en entrée non utilisable
IN OUT modications prises en compte (par référence)
TYPE tous les types de variable
Exemple :

PROCEDURE SUP_Client (N IN Number) IS


.....
BEGIN
/* code de la suppression*/
END SUP_Client;

2.1.4 Fonctions
Les fonctions doivent être déclarées en n de section DECLARE.
Une fonction peut être considérée comme un sous_bloc réalisant un traitement spécique retournant une valeur à
la n du traitement. Une fonction ne peut être utilisée que dans le bloc qui la déclare. Une procédure est constituée
de deux parties : l'en_tête et le bloc de déclaration.
Syntaxe
FUNCTION Nom_fonction [ (Paramètre1, ...) ] RETURN TYPE IS
 déclaration de variables locales

3
BEGIN
 instructions [EXCEPTIONS
 gestion des erreurs]
END [Nom_fonction] ;
Paramètres
nom_param [IN | OUT | IN OUT ] TYPE [ := exp_pl/sql]
IN modications non prises en compte (par valeur)
OUT valeur en entrée non utilisable
IN OUT modications prises en compte (par référence)
TYPE tous les types de variable
Exemple :

FUNCTION TOT_Com (Prix IN Number, QTE IN Number) RETURN REAL IS


.....
BEGIN
/* calcul du total*/
END TOT_Com;

2.2 Instructions du corps de bloc

Le corps d'un bloc commence par le mot réservé BEGIN et nit sur le mot réservé END ;

2.2.1 Aectations
L'aectation classique :=
L'ordre SELECT

2.2.2 Conditionnelles
IF < condition >
THEN < instruction >;
END IF ;
IF < condition >
THEN < instruction > ;
ELSE < instruction > ;
END IF ;
Condition
< Opérande >< Opérateur >< Opérande >
Opérateurs utilisables
= ! = <> < <= > >=
IN NOT IN
OR AND NOT

2.2.3 Itératives
Trois formes
Label LOOP
Instructions ;
Test d'arrêt ;
END LOOP Label ;
FOR < variable > IN < Borne_inf > .. < Borne_sup >
LOOP
Instructions ;
END LOOP ;
WHILE < Condition >
LOOP
Instructions ;
END LOOP ;

4
2.2.4 Ordres SQL
On distingue deux groupes d'ordres SQL, celui des ordres simples associés à des curseurs implicites, celui des ordres
de sélection plus complexes nécessitant des curseurs explicites.
 Les curseurs implicites
Ils sont associés aux ordres SELECT, INSERT, DELETE et UPDATE. Ils sont déclarés automatiquement par
ORACLE lors de l'exécution de la requête. (Attention un seul enregistrement doit être résultat pour une requête
SELECT)
Variables ORACLE positionnées
%FOUND %NOTFOUND %ROWCOUNT %ISOPEN

Utilisation dans les instructions conditionnelles ou d'affectation précédées par SQL


exemple SQL%FOUND

Gestion des erreurs NO_DATA_FOUND ou TOO_MANY_ROWS


 Les curseurs explicites
Les curseurs explicites sont utilisés en quatre phases : - Déclaration - Ouverture (OPEN) - Lecture d'un enregis-
trement (FETCH INTO) - Fermeture (CLOSE)
Variables ORACLE positionnées

%FOUND %NOTFOUND %ROWCOUNT %ISOPEN

Utilisation dans les instructions conditionnelles ou d'affectation


précédées par le nom du curseur associé
exemple CURSEUR1%NOTFOUND

 Les ordres SQL relatifs aux transactions


COMMIT ROLLBACK SAVEPOINT

3 Notion de Package
Un Package est un ensemble nommé d'objets associés (TYPES, VARIABLES, FONCTIONS, PROCEDURES)
stocké dans la base.
Un Package comporte deux parties
- une partie SPECIFICATION visible depuis les applications externes

CREATE [OR REPLACE] PACKAGE [Schéma.]Nom_package IS Spécification_pl/sql

- une partie CORPS invisible depuis les applications externes

CREATE [OR REPLACE] PACKAGE BODY [Schéma.]Nom_package IS pl/sql_package_body

L'exécution d'une procédure d'un package s'eectue par la commande

EXECUTE nom_package.nom_procédure(liste_paramètres effectifs)

5
Revision PL-SQL Guide SQL*Plus

C’est quoi SQL* Plus


- est un outil en ligne de commande permettant de se connecter à une base de données
Oracle afin d'y exécuter des ordres SQL ou des procédures PL/SQL.

Les principales fonctionnalités


- permet d'envoyer des commandes SQL et PL/SQL au SGBDR
- exécuter ses propres commandes internes

Pourquoi préférer une invite SQL*Plus a Toad ou a SQL Developer ?


- parce qu’il peut s'avérer plus simple et plus rapide de lancer une invite SQL*Plus plutôt
que l'artillerie lourde d'un client graphique.

C’est quoi une base de données ?


- Une base de données permet de stocker de l’information, sous quelque forme que ce
soit.
- l’important étant que les données soient stockées de façon structurée selon une norme
précise afin de nous permettre de les consulter, de les mettre à jour ou encore d’en
insérer de nouvelles.

C’est quoi un SGBD


- Logiciel qui permet de gérer une base de données
- permettre d’interroger la base ou encore de gérer la sécurité (les droits d’accès) et
l’intégrité des données.

Modèle de fonctionnement de SGBD


- les SGBD fonctionnent sur le modèle « client-serveur »

Fonctionnement du modèle client-serveur


- Le client, qui envoie ses requêtes au serveur ;
- le serveur, dont la tâche est d’attendre les requêtes du client, avant de les traiter et de
lui envoyer une réponse.
- Ils peuvent être installées sur un seul et même ordinateur ou bien sur deux machines
distinctes qui vont communiquer via le réseau.
-
La commande de connexion à SQL*Plus
- sqlplus nom_utilisateur/mot_de_passe@service

Les noms de services disponibles sont paramétrés dans le fichier


- tnsnames.ora (utiliser dans sql*plus, toat et sql developer) : il permet de donner un nom
court et simple, un « alias », à la connexion. Cet alias peut alors être utilisé dans les
applications clientes telles que SQL*Plus.
- se situe par défaut dans le répertoire <ORACLE HOME>/network/admin/
- le répertoire ORACLE HOME étant le répertoire qui accueille votre installation
- exemple de contenu
XE = ->le service
(DESCRIPTION = ->
(ADDRESS = (PROTOCOL = TCP)(HOST = shigerum-pc)(PORT = 1521)) -> chaîne de
connexion
(CONNECT_DATA =
SERVER = DEDICATED)
(SERVICE_NAME = XE)
)
)

Déconnexion
- La commande de déconnexion est la commande exit.

SQL*Plus accepte trois types de commandes :


- les commandes SQL : le langage de requête utilisé pour extraire les données de la base
;
- les commandes PL/SQL : langage procédural propre à Oracle, utilisé pour intégrer des
requêtes SQL à des traitements procéduraux (c'est-à-dire en utilisant des variables, des
boucles, des structures de contrôles, etc.) ;
- les commandes SQL*Plus : commandes internes de SQL*Plus, permettant entre autre
de le paramétrer, comme nous allons le voir dans un instant

Activer un utilisateur
- Par défaut, cet utilisateur est désactivé
- ALTER USER HR ACCOUNT UNLOCK;

Donner un nouveau mot de passe a l’utilisateur


- ALTER USER HR IDENTIFIED BY MotDePasseTopSecurite2;

C’est quoi une variable?


- est une zone mémoire dans laquelle on stocke une valeur afin de la réutiliser par la suite,
dans diverses situations.

Types de variable sql*plus


- les variables utilisateurs, utilisées essentiellement pour les requêtes SQL, mais aussi
pour le fonctionnement interne de SQL*Plus ;
- les variables de lien, utilisées cette fois pour les commandes PL/SQL.

C’est quoi une variable utilisateur ?


- Les variables utilisateurs sont des variables tout ce qu'il y a de plus classiques : on les
définit et on leur attribue une valeur avant de pouvoir les réutiliser.
- Ex : SELECT * FROM &ma_table;
- Au sein d'une requête SQL, on utilise une variable en préfixant son nom d'une
esperluette (« & »).
Définir une variable.
- on utilise la commande DEFINE (également abrégée DEF)
- DEF[INE] [nom_de_la_variable = contenu_de_la_variable]
- DEF ma_table = "REGIONS"
- Une définition de variable est une commande interne à SQL*Plus, il n'y a donc pas de
« ; » finale

Supprimer une variable.


- on utilise la commande UNDEFINE (ou UNDEF ) suivie du nom de la variable à
supprimer.

C’est quoi une variable de lien ?


- Le principe est globalement le même pour les variables de lien, si ce n'est qu'elles ne se
gèrent pas avec les mêmes commandes et qu'elles sont faites pour être utilisées dans
des instructions PL/SQL.

Définir une variable.


- On définit une variable de lien grâce à la commande VARIABLE (ou VAR)
- VAR[IABLE] [nom [NUMBER | CHAR(n)]]
- Au sein des instructions PL/SQL, la variable doit être utilisée en préfixant son nom de
deux points (« : »)

Exécution d'un script


- SQL*Plus permet d'exécuter des requêtes préalablement écrites dans des fichiers dont
l'extension est .sql.
- On utilise pour cela les commandes START nom_du_fichier.sql
- ou encore @nom_du_fichier.sql.

chemin absolu
- Le chemin absolu est le chemin complet, depuis la racine du disque dur vers le nom du
fichier
- Ex : SQL>@C:\Users\Matthieu\test.sql

chemin relatif
- Le chemin relatif est le chemin partant du répertoire dans lequel vous vous trouviez au
moment où vous avez lancé le programme sqlplus.
- Ex : SQL>@test.sql
Utilisation de variables utilisateurs dans les scripts
- il est tout à fait possible d'utiliser des variables utilisateurs au sein des scripts .sql.

Commande ACCEPT
- vous pouvez indiquer un message à l'utilisateur pour l'inviter à entrer la valeur de la
variable.
- Ex : ACCEPT ma_table PROMPT "Quelle table voulez-vous interroger ? "
- crée la variable
- Sans elle, la valeur de la variable était automatiquement demandée mais celle-ci était
oubliée à la fin de l'exécution du script. À présent, la variable est définie et apparaît
bien dans la liste des variables (commande DEF).

C’est quoi un buffer ?


- Le buffer est une zone mémoire contenant la dernière commande SQL (ou PL/SQL)
envoyée. Grâce à lui, il va être possible de retravailler la dernière requête envoyée ou
encore de la sauvegarder.

Utilisation du buffer
- le buffer contient la dernière requête envoyée et non la dernière ligne uniquement.
- Tapez la commande LIST (ou simplement L) pour obtenir le contenu du buffer
- Pour rejouer le contenu du buffer, on utilise la commande RUN (ou plus court, /).

Modifier le contenu du buffer


- L'une des fonctionnalités les plus intéressantes du buffer est sans doute la possibilité de
modifier une partie de son contenu.
- On utilise pour cela la commande CHANGE (abrégée en C)
- Ex : C[HANGE]/texte_a_remplacer/[texte_de_remplacement]
- C/regions/jobs

Enregistrer le contenu du buffer dans un fichier


- la commande SAVE (ou S tout court)
- Ex : S[AVE] nom_du_fichier { [CREATE] | [REPLACE] | [APPEND] }
- L'emplacement de destination du fichier est le répertoire dans lequel vous vous trouviez
au moment du lancement de SQL*Plus
- Le nom du fichier est bien sûr le nom du fichier qui recevra le contenu du buffer.
- L'option CREATE n'est pas nécessaire si le fichier n'existe pas déjà : il sera créé
d'office.
- s'il existe, il vous faudra utiliser l'option REPLACE.
- l'option APPEND sert à ajouter le contenu du buffer à la suite d'un fichier déjà existant
- La sauvegarde de buffer est très utile car elle permet de créer facilement des scripts
comme on en a vu précédemment.
Paramétrer l'affichage des résultats
- La commande SET permet de positionner certaines options de l'environnement
- Ex : SET nom_du_parametre valeur_données

PAGESIZE : Longueur des pages


- Parmi les problèmes d'affichage fréquemment rencontrés, on trouve la répétition
régulière des noms de colonnes
- Cela provient du fait que SQL*Plus affiche les résultats sous forme de « pages » de
longueur définie.
- pour éviter ce problème, il faut augmenter le nombre de lignes par page à l'aide du
paramètre « PAGESIZE », celui-ci étant par défaut à 14
- Ex : SQL> SET pagesize 100

LINESIZE : Longueurs de lignes


- Par défaut, SQL*Plus affiche les résultats sur une largeur de 80 caractères.
- Pour remédier à cela, on préférera souvent agrandir cette largeur à l'aide du paramètre
« LINESIZE »
- SQL> SET LINESIZE 300

TIMING : Chronométrage des requêtes


- SET TIMING [ON | OFF] pour afficher ou masquer le temps d'exécution d'une requête
- SET TIME [ON | OFF] permet d'ajouter l'heure avant l'invite de commandes SQL>

SQLPROMPT : Modification de l'invite de commande SQL>


- SET SQLPROMPT "texte" permet de placer le texte voulu à la place (texte à placer
entre guillemets dans la commande)
- Par défaut, l'invite de commande est paramétré pour afficher SQL>
- Ex : SQL> SET SQLPROMPT "SALUT> "
SALUT>
- Il est conseillé, comme je l'ai fait ci-dessus, de positionner un caractère bien visible en
fin de prompt (le chevron « > ») ainsi qu'un espace, afin que les requêtes ne « collent »
pas à l'invite de commandes.

BTITLE, TTITLE : Afficher un titre et un pied de page au résultat


- TTITLE et BTITLE permettent d'afficher respectivement une en-tête et un pied de
page. Elles s'utilisent de la même façon
- Ex TTITLE [option] [texte]

LEFT, CENTER et RIGHT


- qui permettent de définir l'alignement du texte dans la page.
- TTITLE OFF Pour ne plus afficher l'en-tête ou le pied de page

COLUMN : Paramétrer l'affichage d'une colonne en particulier


- ne s'applique qu'à l'une des colonnes à la fois.
- On ne paramètre donc pas l'affichage de toutes les colonnes mais bien celui de l'une
d'entre elle, que l'on désigne par son nom.
- COLUMN nom_de_la_colonne FORMAT A10

Format des colonnes de « nombres »


- L'option FORMAT de la commande COLUMN est également utilisée pour les colonnes
de type « nombre »
- le « 9 » correspond à l'affichage de n'importe quel chiffre ;
- le « 0 » quant à lui correspond à l'affichage d'un zéro non significatif.

Affichage des valeurs null


- COLUMN SCORE NULL VIDE

Effacer ou désactiver les mises en forme d'une colonne


- Pour effacer tous les paramétrage liés à une colonne, on utilise le paramètre CLEAR,
- COLUMN nom_de_la_colonne CLEAR
- il est possible de désactiver (et ré-activer) les mises en formes d'une colonne avec ON
ou OFF.
- COLUMN nom_de_la_colonne { ON | OFF }

Enregistrer les paramètres dans un fichier


- Pour que nos paramétrages soient pris en compte à chaque nouvelle session, on les
enregistre alors dans le fichier <ORACLE_HOME>/sqlplus/admin/glogin.sql
(<ORACLE_HOME> représentant le chemin vers le répertoire « Oracle Home »).
- Le principe est le même avec le contenu du buffer et les variables (variables utilisateur
et variables de lien) : tous ces éléments sont perdus à la déconnexion. Vous pouvez
donc les définir dans le fichier glogin.sql, bien que cela ait peut-être moins d'intérêt que
pour les paramètres d'affichage.

Exporter les résultats dans un fichier


- SQL*Plus permet d'enregistrer les retours d'une requête ou d'un script directement dans
un fichier. Principal avantage : il n'y a plus besoin de copier-coller le résultat de la
requête à la main dans un fichier, avant de l'envoyer au patron qui le demandait pour
hier. Couplé aux possibilités d'affichages vues dans la sous-partie précédente, on génère
des fichiers quasi-exploitables en l'état.
Écrire dans un fichier
- Écrire dans un fichier avec SQL*Plus, c'est comme filmer avec une caméra :
l'enregistrement commence lorsqu'on appuie sur un bouton et il se poursuit jusqu'à ce
qu'on le coupe. Ici, le bouton est la commande SPOOL (ou SPO)
- SPO[OL] { nom_du_fichier | OFF }
- Le fichier sera placé dans le répertoire courant, c'est-à-dire où vous vous trouviez lors
du lancement de SQL*Plus. Le principe est le même qu'avec les scripts vus plus tôt.
-

Si vous avez positionné un grand « LINESIZE », l'export dans un fichier va souffrir d'un petit
problème : des espaces seront ajoutés à droite des résultats de requête. Si par exemple vous
avez positionné le « LINESIZE » à 1000 et qu'une ligne de votre résultat ne fait que 100
caractères de large, alors 900 espaces seront ajoutés !

Si l'on ne fait pas attention, ça ne se « voit » pas. En revanche le fichier sera plus volumineux
(les espaces sont tout de même des caractères). De plus, essayez de copier-coller cela dans un
document Word ou dans un mail, vous verrez le résultat…

Bref, il y a un moyen très simple d'éviter le problème, il suffit de demander à SQL*Plus de ne


plus afficher ces espaces grâce au paramètre TRIMSPOOL de la commande SET :

1
SET TRIMSPOOL { ON | OFF }
Si ce paramètre est positionné à « ON », les espaces ne s'afficheront pas.

C'est un paramètre tellement utile et courant (en fait je ne vois pas bien la raison de le laisser
à « OFF ») que je vous conseille vivement de l'ajouter à votre fichier glogin.sql.

Que dirirez-vous de quelques petits exercices ?


Pour terminer ce tutoriel consacré à SQL*Plus, je vous propose une petite série d'exercices,
histoire de mettre en pratique ce que nous avons appris. Je vous conseille fortement d'essayer
de les faire vous-même, sans regarder la solution. Toutes les informations nécessaires pour
les réussir sont dans le tutoriel.

Exercice 1 : Lancement de requête et buffer


Énoncé
Se connecter avec le user HR (afin d'avoir accès à ses tables). Exécuter la requête suivante
dans SQL*Plus :

1
SELECT * FROM REGIONS;
Visualiser ensuite le contenu du buffer.

Utiliser le buffer pour modifier la requête en SELECT * FROM DEPARTMENTS;. Envoyer


alors la commande contenu dans le buffer.

Solution
C:\Users\Matthieu>sqlplus

SQL\*Plus: Release 11.2.0.2.0 Production on Ven. DÚc. 14 10:21:24 2012

Copyright (c) 1982, 2010, Oracle. All rights reserved.

Enter user-name: hr
Enter password:

Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - Production

SQL> SELECT * FROM REGIONS;


REGION_ID REGION_NAME
---------- -------------------------
1 Europe
2 Americas
3 Asia
4 Middle East and Africa

SQL> list
1* SELECT * FROM REGIONS
SQL> c/REGIONS/DEPARTMENTS
1* SELECT * FROM DEPARTMENTS
SQL> /

DEPARTMENT_ID DEPARTMENT_NAME MANAGER_ID LOCATION_ID


------------- ------------------------------ ---------- -----------
10 Administration 200 1700
20 Marketing 201 1800
30 Purchasing 114 1700
40 Human Resources 203 2400
50 Shipping 121 1500
60 IT 103 1400
70 Public Relations 204 2700
80 Sales 145 2500
90 Executive 100 1700
100 Finance 108 1700
110 Accounting 205 1700

DEPARTMENT_ID DEPARTMENT_NAME MANAGER_ID LOCATION_ID


------------- ------------------------------ ---------- -----------
120 Treasury 1700
130 Corporate Tax 1700
140 Control And Credit 1700
150 Shareholder Services 1700
160 Benefits 1700
170 Manufacturing 1700
180 Construction 1700
190 Contracting 1700
200 Operations 1700
210 IT Support 1700
220 NOC 1700

DEPARTMENT_ID DEPARTMENT_NAME MANAGER_ID LOCATION_ID


------------- ------------------------------ ---------- -----------
230 IT Helpdesk 1700
240 Government Sales 1700
250 Retail Sales 1700
260 Recruiting 1700
270 Payroll 1700

27 rows selected.
Exercice 2
Énoncé
Créer un script contenant la requête suivante :

1
SELECT * FROM REGIONS;
Se connecter à SQL*Plus avec l'utilisateur HR et lancer ce script.

Solution
La création du script consiste simplement en la création d'un fichier portant l'extension .sql.
Par exemple, je crée un fichier mon_script.sql sur mon Bureau et j'y place la requête :
SELECT * FROM REGIONS;

Dans l'invite de commande (Linux ou Windows peu importe), je me déplace tout d'abord
dans le répertoire contenant mon script et je lance SQL*Plus.

Il ne reste plus qu'à lancer le script à l'aide de la commande START ou bien avec la
commande courte @ :

1
2
3
4
5
6
7
8
SQL> @mon_script.sql

REGION_ID REGION_NAME
---------- -------------------------
1 Europe
2 Americas
3 Asia
4 Middle East and Africa

Autre méthode, j'utilise le chemin absolu vers le fichier (dans ce cas il n'est pas nécessaire de
se placer dans le réperoire contenant le script avant le lancement de SQL*Plus) :

SQL> @/home/shigerum/Desktop/mon_script.sql
REGION_ID REGION_NAME
---------- -------------------------
1 Europe
2 Americas
3 Asia
4 Middle East and Africa
Exercice 3
Énoncé
Modifier le script précédent pour que le nom de la table soit une variable et que celle-ci soit
demandée avec un prompt « Quelle table voulez-vous interroger ? » lors du lancement du
script.

Solution
Il suffit d'ajouter un appel à la commande ACCEPT en début de script :

1
2
ACCEPT var PROMPT "Quelle table voulez-vous interroger ? "
SELECT * FROM &var;
Ainsi, la variable est créée et sa valeur est demandée :

1
2
3
4
5
6
7
8
9
10
11
SQL> @mon_script.sql
Quelle table voulez-vous interroger ? REGIONS
old 1: SELECT * FROM &var
new 1: SELECT * FROM REGIONS

REGION_ID REGION_NAME
---------- -------------------------
1 Europe
2 Americas
3 Asia
4 Middle East and Africa
De plus, si on affiche les variables définies (commande DEF), alors on constate que la
variable VAR est bien toujours en place :

1
2
3
4
5
6
7
8
9
10
SQL> DEF
DEFINE _DATE = "18-DEC-12" (CHAR)
DEFINE _CONNECT_IDENTIFIER = "XE" (CHAR)
DEFINE _USER = "HR" (CHAR)
DEFINE _PRIVILEGE = "" (CHAR)
DEFINE _SQLPLUS_RELEASE = "1102000200" (CHAR)
DEFINE _EDITOR = "ed" (CHAR)
DEFINE _O_VERSION = "Oracle Database 11g Express Edition Release 11.2.0.2.0 -
64bit Production" (CHAR)
DEFINE _O_RELEASE = "1102000200" (CHAR)
DEFINE VAR = "REGIONS" (CHAR)Exercice 4
Énoncé
Modifier à nouveau le script pour que cette fois le résultat soit enregistré dans un fichier.
Faire en sorte que l'affichage soir correct, même en interrogeant la table EMPLOYEES.

Solution
Tout d'abord on ouvre la commande SPOOL avec le nom du fichier cible avant la requête,
puis on la referme en fin de script :

1
2
3
4
SPOOL resultat.txt
ACCEPT var PROMPT "Quelle table voulez-vous interroger ? "
SELECT * FROM &var;
SPOOL OFF
Ici le chemin utilisé est relatif, le fichier resultat.txt va alors être créé dans le répertoire dans
lequel vous étiez lors du lancement de SQL*Plus. Vous pouvez également indiquer un
chemin absolu.

Si vous exécutez ce script en utilisant une table qui contient de nombreuses colonnes (comme
la table EMPLOYEES), alors le résultat ne sera pas exploitable car la longueur des lignes est
par défaut trop courte. On utilise donc le paramètre « LINESIZE » pour agrandir cette
dernière. Je mets par exemple la valeur 10000 afin d'être tranquille.

Problème : de nombreux espaces sont insérés à droite de mon résultat. Le fichier est alors très
lourd pour le peu de données qu'il contient (environ 1 Mo dans mon cas, pour seulement une
centaine de lignes !). Je mets donc à « ON » le paramètre « TRIMSPOOL ».
Enfin, pour éviter que les noms de colonnes ne se répètent régulièrement, je positionne
également le paramètre « PAGESIZE » à une valeur élevée.

Finalement, voici à quoi ressemble mon script :

SET TRIMSPOOL on
SET LINESIZE 10000
SPOOL resultat.txt
ACCEPT var PROMPT "Quelle table voulez-vous interroger ? "
SELECT * FROM &var;
SPOOL OFFSQL*Plus est un outil en lignes de commandes permettant d'établir une
connexion entre le client et le serveur de base de données dans le but d'envoyer des requêtes
et de recevoir des résultats.

Qu'avons-nous vu dans ce tutoriel ?

Les requêtes peuvent être tapées directement dans l'invite de commande ou bien à partir de
scripts (fichiers en .sql).
SQL*Plus gère deux types de variables : les variables utilisateurs (pour les requêtes SQL et
les commandes internes de SQL*Plus) et les variables de lien (pour les commandes PL/SQL).
Certains outils de SQL*Plus peuvent être très utile, comme le buffer ou l'export des résultats
dans des fichiers (SPOOL).
Enfin, de nombreux paramètres sont à définir pour paramétrer l'affichage des résultats. Ces
paramétrages peuvent être généraux ou s'appliquer à des colonnes particulières.
Avec tout cela, vous devriez pouvoir vous débrouiller sans trop de soucis avec SQL*Plus.
COURS ET EXERCICES SQL

Introduction
Une base de données est un ensemble de tables (ou aussi relation - d'où le nom de base de
données relationnelles), dont le contenu est la description d'entités (clients, produits,
étudiants, matières, ...) ou d'associations (achat d'un produit par un client, note d'un étudiant à
une matière, ...).

Elle a pour but de simplifier le fonctionnement d'une organisation ou d'une entreprise via une
ou plusieurs applications informatiques.

Nous devons utiliser ce qu'on appelle un Système de Gestion de Bases de Données (ou
SGBD) pour gérer ces bases et faire toutes les opérations nécessaires sur celles-ci (création,
insertion, modification, interrogation, suppression).

Exemple de table
Voici un exemple de table simple, décrivant des étudiants. Chaque ligne est appelée un tuple
ou enregistrement. Chaque colonne est appelée un attribut ou une variable.

Table Etudiant:

IdEtu Nom Prenom Sexe Age


1 Remin Norbert H 19
3 Constant Raphaelle F 20
4 Fleurot Isabelle F 19
5 Yannic Sandrine F 18
6 Josse FrancisH 20
Modèle relationnel
Afin de créer des bases de données, il a été défini un modèle relationnel permettant d'établir
des contraintes de construction de celles-ci. Ces contraintes ont pour but de limiter le plus
possible les problèmes à gérer plus tard. On parle de règles d'intégrité structurelle.

Contrainte de domaine
Tout attribut a un domaine de valeurs, qui sont ici les suivants :
IdEtu : entier,
Nom, Prenom : chaîne de caractères
Sexe : un seul caractère, éventuellement limité à H et F
Age : entier aussi
Ceci a pour but d'éviter au maximum les erreurs introduites lors d'insertion de données.

Unicité de la clé
Comme nous pouvons le remarquer dans l'exemple de table, le premier attribut IdEtuest un
entier différent pour chaque ligne. C'est ce qu'on appelle une clé primaire. Celle-ci est un
attribut (ou groupe d'attributs) permettant d'identifier chaque ligne de la table de manière
unique. Cela permettra de faire des liens entre lignes de plusieurs tables sans ambiguïté.

Contrainte d'entité
Une clé primaire ne peut pas avoir de valeur NULL (non-présence de l'information). Il doit
absolument y avoir une valeur pour l'attribut, ou pour chaque attribut dans le cas d'une clé
multiple.

Contrainte de référence
On peut imaginer la table suivante :

Table Note :

IdEtu Matière Note


1 SQL 19
1 Stat 11
2 Stat 7
... ... ...
L'attribut IdEtu présent ici fait référence à l'attribut IdEtu de la table Etudiant vue plus haut.
On parle aussi de clé externe ou de clé étrangère. C'est un attribut d'une relation devant
apparaître comme clé primaire dans une autre relation.
La contrainte de référence veut que toute valeur présente dans la colonne IdEtu de la table
Note doit être présente dans la colonne IdEtu de la table Etudiant. Cela évite de mettre une
note à un étudiant qui n'existe pas.

Langage SQL
Le langage SQL est la norme internationale pour interroger une base de données
relationnelles (comme Oracle, SQL Server, SQLite, DB2, ...). L'intérêt de ce langage est qu'il
est très puissant et très bien documenté (nombreux livres et sites internet dédiés).

Il permet, via ce qu'on appelle des requêtes, de créer, manipuler, interroger, modifier,
supprimer toutes les données présentes. Dans ce cours, nous nous limiterons à son usage pour
l'interrogation de données.

Calculs
Il est possible d'ajouter des expressions dans le SELECT :

SELECT 1 + 1, 10 * 2;
Requêtage de données
La requête la plus simple est celle permettant de récupérer l'ensemble des données d'une
table. Toute requête d'interrogation de données commence par le mot-clé SELECT et termine
normalement par un point-virgule (";").

Voici la requête permettant de récupérer la liste des clients présents dans la base de données
(ici, nous utilisons une BD nommée Comptoir2000).

SELECT *
FROM Client;
Voici quelques explications :

Le terme SELECT indique donc que nous souhaitons récupérer des données ;
Le caractère * indique que l'on veut tous les attributs de la table ;
Le terme FROM permet d'indiquer à partir de quelle table nous devons récupérer les données.
Limitation des résultats
Il est parfois utile de n'avoir que les premières lignes d'une table, pour comprendre son
contenu par exemple. Dans ce cas, il est possible d'ajouter en fin de requête le terme LIMIT
suivi du nombre de lignes souhaité.

SELECT *
FROM Client
LIMIT 3;
Fenêtrage des résultats
Il est possible de ne récupérer que les lignes à partir d'une certaine position dans la liste avec
la clause OFFSET. Elle est souvent combinée avec la clause LIMIT pour faire de la
pagination dans les résultats d'une requête.

SELECT *
FROM Client
LIMIT 3 OFFSET 3;
Ordre des résultats
De même, on est souvent appelé à faire un tri des données (ascendant ou descendant). Le
terme ORDER BY, à placer en fin de requête aussi, mais avant le LIMIT si nécessaire,
permet de réaliser un tel tri. Il faut bien noter que le tri ne se fait qu'à l'affichage et que la
table n'est en rien modifiée. De plus, celui-ci peut se faire sur tout type de données.

Il est possible d'indiquer de deux façons le ou les attributs à prendre en compte pour le tri :

par leur nom


par leur position dans la table
Les deux requêtes suivantes sont les mêmes et permettent toutes deux d'avoir la liste des
employés triés dans l'ordre croissant de leur nom (attribut Nom placé en 2ème position). Le
terme ASC peut être spécifié mais n'est pas nécessaire car il est appliqué par défaut.

SELECT *
FROM Employe
ORDER BY Nom;
SELECT *
FROM Employe
ORDER BY 2;
SELECT *
FROM Employe
ORDER BY Nom ASC;
Dans un souci de clarté, il est tout de même préférable d'utiliser la première option.

Ensuite, pour modifier le type de tri, il est possible d'ajouter le terme DESC pour indiquer
qu'on souhaite un tri décroissant. Par défaut, c'est donc un tri croissant qui est fait.

SELECT *
FROM Employe
ORDER BY Nom DESC;
Il est possible d'avoir plusieurs critères de tri. Pour cela, il faut séparer les différents attributs
(nom ou position) par une virgule (,). La requête suivante permet donc d'avoir les employés
triés d'abord par leur fonction, puis par leur nom.

SELECT *
FROM Employe
ORDER BY Fonction, Nom;
Voici la même requête que précédemment, avec les fonctions triées par ordre alphabétique
décroissant.

SELECT *
FROM Employe
ORDER BY Fonction DESC, Nom;
Combinaition de ORDER BY avec LIMIT
La limitation doit être combilée avec le tri de la clause ORDER BY ci-dessous afin de
pouvoir limiter les résultats dans un ordre précis.

La requête ci-dessous permet de retrouver le premier employé embauché :

SELECT *
FROM Employe
ORDER BY NoEmp ASC
LIMIT 1 OFFSET 1;
La requête ci-dessous permet de retrouver le second employé embauché :

SELECT *
FROM Employe
ORDER BY NoEmp ASC
LIMIT 1 OFFSET 2;
L'offset permet de récupérer la nième ligne de résultat.

Exercices
1. Lister le contenu de la table Produit
2. N'afficher que les 10 premiers produits
3. Lister les produits triés par leur prix unitaire (attribut PrixUnit) décroissant
4. Lister les trois produits les plus chers
5. Lister les employés (table Employe) triés par ville décroissante puis par nom croissant

Projection
Une projection est une sélection de colonnes d'une table, sur la base d'une liste d'attributs
placés après le SELECT et séparés par une virgule.

La requête suivante permet d'avoir uniquement les noms et les prénoms des employés.

SELECT Nom, Prenom


FROM Employe;
Projections et calculs
Il est tout à fait possible de mélanger projections et calculs dans une requête SQL

SELECT DateCom, Port, Port * 2


FROM Commande;
Doublons
Lors d'une projection, on est souvent en présence de doublons dans les résultats, i.e. deux
lignes ou plus identiques.

Par exemple, lorsqu'on liste les fonctions des employés, on a plusieurs fois chaque fonction
existante.

SELECT Fonction
FROM Employe;
Or, dans ce cas, on est souvent intéressé par la liste des valeurs uniques. Pour l'obtenir, il est
possible d'ajouter le terme DISTINCT juste après le SELECT, pour supprimer ces doublons.

SELECT DISTINCT Fonction


FROM Employe;
Ceci fonctionne aussi lorsqu'on a indiqué plusieurs attributs dans le SELECT.

Renommage
Pour améliorer la présentation, il est possible de renommer un attribut (et on le verra plus tard
le résultat de calcul), avec le terme AS placé après l'attribut à renommer et suivi du nouveau
nom.

SELECT DISTINCT Fonction AS "Fonctions existantes"


FROM Employe;
Exercices
1. Lister uniquement la description des catégories de produits (table Categorie)
2. Lister les différents pays des clients
3. Idem en ajoutant les villes, le tout trié par ordre alphabétique du pays et de la ville
4. Lister les villes avec le code postal des clients en éliminant les doublons

Restriction
Une restriction est une sélection de lignes d'une table, sur la base d'une condition à respecter,
définie à la suite du terme WHERE. Cette condition peut être une combinaison de
comparaisons à l'aide de AND, de OR et de NOT (attention donc aux parenthèses dans ce
cas).
Opérateurs classiques
Nous disposons bien sûr de tous les opérateurs classiques de comparaison :

=
<>
>
>=
<
<=
Cette requête permet de lister tous les employés ayant la fonction de représentant.

SELECT *
FROM Employe
WHERE Fonction = "Représentant(e)";
Si l'on souhaite le complément de cette requête, i.e. tous les employés qui ne sont pas
représentants, on utilise le symbole <> pour indiquer une non-égalité (ce qui revient à faire
NOT(Fonction = "Représentant(e)")).

SELECT *
FROM Employe
WHERE Fonction <> "Représentant(e)";
Combinaison des filtres : AND
Comme indiqué précédemment, il est possible de combiner des comparaisons avec
l'opérateur AND. La requête suivante permet d'avoir les représentants masculins, avec un
numéro d'employé inférieur strictement à 8.

SELECT *
FROM Employe
WHERE Fonction = "Représentant(e)"
AND TitreCourtoisie = "M."
AND NoEmp < 8;
Combinaison des filtres : OR
Deux comparaisons peuvent être utilisés en complément l'une de l'autre, ce qui permet de
récupérer les lignes de l'une ou de l'autre.

SELECT *
FROM Employe
WHERE Fonction = "Représentant(e)"
OR TitreCourtoisie = "M."
Combinaisons multiples
Lorsque l'on accumule plusieurs comparaisons avec AND et OR, il faut parfois placer des
parenthèses pour définir la priorité des expressions les unes par rapport aux autres :

SELECT *
FROM Employe
WHERE (Fonction = "Représentant(e)"
AND TitreCourtoisie = "M.")
OR NoEmp < 8;
La requête ci-dessus ne donne pas les mêmes résultats que celle ci-dessous :

SELECT *
FROM Employe
WHERE Fonction = "Représentant(e)"
AND (TitreCourtoisie = "M."
OR NoEmp < 8);
Comparaison de chaînes de caractères
Pour les comparaisons de chaînes de caractères, il est important de faire attention à la casse
(i.e. minuscule/majuscule). Par définition, un "a" est donc différent d'un "A". Pour remédier à
ce problème, il existe les fonction UPPER() et LOWER() pour transformer une chaîne en
respectivement majuscule et minuscule.

SELECT *
FROM Employe
WHERE UPPER(Ville) = "SEATTLE";
Données manquantes
Une donnée manquante en SQL est repérée par un NULL. Il y a plusieurs raisons, bonnes ou
mauvaises, pour avoir des données manquantes, et il est parfois utile de tester leur présence.
Pour cela, nous allons utiliser le terme IS NULL comme condition.

Par exemple, pour lister les employés dont la région n'est pas renseignée, nous devons
exécuter la requête suivante.

SELECT *
FROM Employe
WHERE Region IS NULL;
Opérateurs spécifiques
Les deux premiers opérateurs définis ci-après sont particulièrement utiles pour limiter la taille
de la requête. Le dernier est lui utile pour comparer une chaîne de caractères à une pseudo-
chaîne.

Opérateur BETWEEN
Cet opérateur permet de définir un intervalle fermé dans lequel l'attribut doit avoir sa valeur.
La condition suivante est équivalente à NoEmp >= 3 AND NoEmp <= 8.

SELECT *
FROM Employe
WHERE NoEmp BETWEEN 3 AND 8;
Opérateur IN
Cet autre opérateur permet de définir une liste de valeurs entre parenthèses et séparées par
des virgules. La condition suivante est équivalente à TitreCourtoisie = 'Mlle' OR
TitreCourtoisie = 'Mme'.

SELECT *
FROM Employe
WHERE TitreCourtoisie IN ('Mlle', 'Mme');
Opérateur LIKE
Comme précisé avant, l'opérateur LIKE permet de comparer une chaîne de caractère à une
pseudo-chaîne, dans laquelle nous pouvons ajouter deux caractères spécifiques :
% : une suite de caractères, éventuellement nulle
_ : un et un seul caractère
Par exemple, la requête suivante permet de récupérer les employés dont le nom commence
par un "D".

SELECT *
FROM Employe
WHERE Nom LIKE 'D%';
La requête suivante permet elle d'avoir tous les employés qui ont un prénom de 5 lettres.

SELECT *
FROM Employe
WHERE Prenom LIKE '_____';
Il faut noter que l'opérateur LIKE est insensible à la casse, i.e. il ne tient pas compte des
minuscules/majuscules.

Opérateur NOT
L'opérateur NOT permet d'inverser n'importe laquelle des conditions que l'on peut définir
avec BETWEEN, IN, LIKE, IS NULL

Si l'on veut uniquement les employés pour lesquels l'information est présente, nous devrons
utiliser la négation avec IS NOT NULL.

SELECT *
FROM Employe
WHERE Region IS NOT NULL;
La requête suivante permet de récupérer les employés dont le nom ne commence pas par un
"D".

SELECT *
FROM Employe
WHERE Nom NOT LIKE 'D%';
Voici comment récupérer les employés dont le matricule est strictement supérieur à 8 et
strictement inférieur à 3

SELECT *
FROM Employe
WHERE NoEmp NOT BETWEEN 3 AND 8;
Exercices
1. Lister les clients français installés à Paris
2. Lister les clients suisses, allemands et belges
3. Lister les clients dont le numéro de fax n'est pas renseigné
4. Lister les clients dont le nom contient "restaurant" (nom présent dans la colonne
Societe)
5. Lister les produits dont le prix est entre 90 et 100€
6. Lister les produits fournis par les fournisseurs 16, 18 et 19
7. Lister les produits de la catégorie 1 (colonne CodeCateg) dont des unités sont
commandés (colonne UnitesCom)
8. Lister les produits en stock (colonne UnitesStock) du fournisseur N°24 qui ne sont pas
en commande et qui sont conditionnés par 500ml
9. Lister tous les clients dont le nom contient "sp"

Exercices complémentaires
1. Lister tous les produits vendus en bouteilles ou en canettes
2. Lister les fournisseurs français, en affichant uniquement le nom, le contact et la ville,
triés par ville
3. Lister les produits (nom en majuscule et référence) du fournisseur n° 8 dont le prix
unitaire est entre 10 et 100 euros, en renommant les attributs pour que ce soit explicite
4. Lister les numéros d'employés ayant réalisé une commande (cf table Commande) à
livrer en France, à Lille, Lyon ou Nantes
5. Lister les produits dont le nom contient le terme "tofu" ou le terme "choco", dont le
prix est inférieur à 100 euros (attention à la condition à écrire)
6. Lister les commandes ayant été passées entre le '2014-08-01' et le '2014-08-31'
7. Lister les commandes passées après le '2016-06-01' livrées par le messager numéro 2
au Danemark avec un port entre 100 et 200

Calculs arithmétiques
Calcul simple
Il est possible d'effectuer des calculs arithmétiques dans un SELECT, à l'aide des opérateurs
classiques : +, -, *, /, ().
Voici un premier exemple de calcul dans une requête. On additionne les unités en stock avec
les unités commandées, pour chaque produit.

SELECT *, UnitesStock + UnitesCom


FROM Produit;
Bien évidemment, on peut vouloir faire aussi un projection en même temps, en n'affichant
que la référence du produit.

SELECT RefProd, UnitesStock + UnitesCom


FROM Produit;
Renommage
Pour plus de lisibilité, il est d'usage de renommer un calcul, grâce à AS. Si l'on désire mettre
des accents (en français) et/ou des espaces, il est nécessaire d'ajouter les "..." (ou '...').

SELECT RefProd AS Reference,


UnitesStock + UnitesCom AS "Unités disponibles"
FROM Produit;
Vous remarquerez qu'il est possible de passer à la ligne suivante dans un SELECT pour
rendre le code plus simple à lire. En effet, en SQL, on peut écrire tout sur une même ligne,
jusqu'à un mot par ligne. Le moteur du SGBD supprime les espaces inutiles et les sauts de
lignes avant l'exécution de la requête. Mais il est important d'avoir un code lisible (débugage
plus simple, compréhension de celui-ci par un autre aisée, réutilisation facilitée, ...).

Dans cet exemple, nous utilisons la multiplication * pour calculer le montant en stock pour
chaque produit, égal au prix unitaire multiplié par la quantité de produits en stock.

SELECT RefProd,
PrixUnit * UnitesStock AS "Montant en stock"
FROM Produit;
Combinaison de clauses
Puisque nous sommes dans une requête SELECT, nous pouvons bien évidemment utiliser
toutes les restrictions que l'on désire, dans le WHERE.
SELECT RefProd,
PrixUnit * UnitesStock AS "Montant en stock indisponible"
FROM Produit
WHERE Indisponible = 1;
Et bien évidemment, on peut aussi trier le résultat, à l'aide de ORDER BY, et se limiter à n
lignes, à l'aide de LIMIT. Nous avons donc ici les trois produits indisponibles avec le plus
haut montant en stock.

SELECT RefProd,
PrixUnit * UnitesStock AS "Montant en stock"
FROM Produit
WHERE Indisponible = 1
ORDER BY 2 DESC
LIMIT 3;
Calcul complexe
Les calculs peuvent être un peu plus complexes, grâce à l'utilisation des parenthèses. Par
exemple, considérons que nous voulons garder au moins 10 unités de chaque produit. Nous
calculons dans la requête suivante le montant en stock directement disponible, en tenant
compte de la contrainte précédente.

SELECT RefProd,
PrixUnit * (UnitesStock - 10)
FROM Produit
WHERE UnitesStock >= 10;
Toute expression mathématique combinant les opérateurs classiques est donc acceptable à ce
niveau.

Arrondi
Il est possible d'obtenir l'arrondi d'un réel grâce à la fonction ROUND(). Dans l'exemple ci-
dessous, nous calculons une augmentation de 5% des prix des produits.

SELECT RefProd,
ROUND(PrixUnit * 1.05) AS "Nouveau Prix"
FROM Produit;
L'arrondi ci-dessus est à l'entier. Si l'on désire un arrondi à 2 décimales (et donc les centimes
dans notre cas), il faut ajouter un 2 comme second paramètre de la fonction ROUND().

SELECT RefProd,
ROUND(PrixUnit * 1.05, 2) AS "Nouveau Prix"
FROM Produit;
Exercices
1. La table DetailCommande contient l'ensemble des lignes d'achat de chaque
commande. Calculer, pour la commande numéro 10251, pour chaque produit acheté
dans celle-ci, le montant de la ligne d'achat en incluant la remise (stockée en
proportion dans la table). Afficher donc :

le prix unitaire,
la remise,
la quantité,
le montant remisé Arrondir les valeurs à deux chiffres après la virgule.
2. Ajouter pour chaque ligne de la requête ci-dessus :

le montant de TVA à 20% sur cette ligne


le montant T.T.C. (somme du prix remisé et du montant de T.V.A) de la ligne Arrondir les
valeurs à deux chiffres après la virgule.

Traitement conditionnel
Nous avons vu comment se restreindre à un sous-ensemble d'une table via les restrictions
dans la partie WHERE d'une requête. Il existe aussi une possibilité de faire un traitement
conditionnel avec le terme CASE. Dans un SELECT, celui-ci va nous permettre de
conditionner la valeur d'une colonne par les valeurs d'autres colonnes.

Comparaison à des valeurs (égalité)


La première utilisation de cette commande est de comparer un attribut à un ensemble de
valeurs. Puisque la comparaison est l'égalité, ceci concerne principalement des attributs de
type texte ou avec un nombre de valeurs restreint. Dans ce cas, l'ordre des comparaisons à
l'aide de WHEN n'a aucune importance.

Dans l'exemple ci-dessous, nous voulons afficher les titres de courtoisie au complet pour
"Mlle", "Mme" et "M.". Pour "Dr.", nous voulons le garder par contre. Vous pouvez donc
voir la syntaxe de cette commande.

SELECT NoEmp, Nom, Prenom, TitreCourtoisie,


CASE TitreCourtoisie
WHEN "Mlle" THEN "Mademoiselle"
WHEN "Mme" THEN "Madame"
WHEN "M." THEN "Monsieur"
ELSE TitreCourtoisie
END AS Titre
FROM Employe;
On peut aussi l'utiliser pour mettre un message en fonction de la valeur d'un attribut. Ci-
dessous, nous affichons à partir de quel niveau de stock le produit doit être commandé pour
réapprovisionnement. Si c'est à 0, on indique qu'il n'y a pas de niveau minimum. Sinon, on
utilise la valeur stockée dans NiveauReap pour créer le message.

SELECT Refprod, Nomprod, NiveauReap,


CASE NiveauReap
WHEN 0 THEN "Pas de niveau minimum"
ELSE "Réapprovisionnement à partir de " || NiveauReap || " unités restantes"
END AS Reapprovisionnement
FROM Produit;
Comparaison à des valeurs (infériorité ou supériorité)
Pour pouvoir comparer un attribut avec des valeurs mais autrement que via l'égalité, il est
aussi possible d'utiliser un CASE.

Dans l'exemple ci-dessous, nous comparons le prix unitaire des produits à deux valeurs
seuils. Si le prix est inférieur ou égal à 50, alors le produit est considéré dans la gamme des
petits prix. Ensuite, on teste pour savoir s'il est inférieur ou égal à 500 et si oui, le produit sera
dans la gamme moyenne. Enfin, par défaut, le produit sera dans la gamme de luxe.
SELECT Refprod, Nomprod, PrixUnit,
CASE
WHEN PrixUnit <= 50 THEN 'Petits prix'
WHEN PrixUnit <= 500 THEN 'Gamme moyenne'
ELSE "Produits de luxe"
END AS Gamme
FROM Produit;
Ceci revient à écrire en algo ce qui suit :

SI (PrixUnit <= 50) ALORS


gamme = "Petits prix"
SINON
SI (PrixUnit <= 500) ALORS
gamme = "Gamme moyenne"
SINON
gamme = "Produits de luxe"
FIN SI
FIN SI
Dans le deuxième test, il n'est pas nécessaire de tester si le prix est supérieur strictement à 50,
car on est dans la partie SINON du premier test.

Comparaison entre attributs


Enfin, il est aussi possible d'utiliser ce test CASE pour comparer plusieurs attributs entre eux.

Dans l'exemple ci-dessous, nous voulons afficher un message en fonction de l'action à


réaliser ou non pour le réapprovisionnement. Si le produit a déjà été comamndé (i.e.
UnitesCom > 0), alors on l'indique. Par contre, s'il ne l'est pas mais que le stock est inférieur
au niveau de réapprovisionnement, alors on indique qu'il faut commander le produit. Pour les
produits qui ne sont plus en stock (et dont le niveau de réapprovisionnement est égal à 0), on
indique juste qu'il n'y a plus de produits à disposition. Pour les autres, il n'y a, a priori, rien à
faire.
SELECT Refprod, UnitesStock, UnitesCom, NiveauReap,
CASE
WHEN UnitesCom > 0 THEN "Déjà commandé"
WHEN UnitesStock < NiveauReap THEN "A commander"
WHEN UnitesStock == 0 THEN "Plus en stock"
ELSE "rien à faire"
END AS Informations
FROM Produit;
Exercices
1. A partir de la table Produit, afficher la référence et le message "Produit non
disponible" lorsque l'attribut Indisponible vaut 1, et "Produit disponible" sinon.
3. Dans la table DetailCommande, indiquer les infos suivantes en fonction de la remise
si elle vaut 0 : "aucune remise"
si elle vaut entre 1 et 5% (inclus) : "petite remise"
si elle vaut entre 6 et 15% (inclus) : "remise modérée"
sinon :"remise importante"
4. Indiquer pour les commandes envoyées si elles ont été envoyées en retard (date
d'envoi DateEnv supérieure (ou égale) à la date butoir ALivAvant) ou à temps

Fonctions sur chaînes de caractères


Concaténation
La première opération que l'on souhaite faire avec des chaînes de caractères est la
concaténation : le regroupement des deux chaînes en une seule. Par exemple, la concaténation
de "bon" et de "jour" donne la chaîne "bonjour". L'opérateur dédié en SQL est ||. L'exemple
ci-dessous nous permet d'avoir le nom et le prénom dans une seule chaîne.

SELECT NoEmp, Nom || Prenom


FROM Employe;
Avec la requête ci-dessus, les deux chaînes sont collées, i.e. il n'y a pas d'espace entre les
deux. Pour cela, il est tout à fait possible de concaténer en une expression plusieurs chaînes
pour introduire un espace, comme ci-après.

SELECT NoEmp, Nom || " " || Prenom


FROM Employe;
Extraction d'une sous-chaîne
Une commande intéressante sur les chaînes est la commande SUBSTR(chaine, debut,
longueur) qui permet d'extraire une sous-chaîne d'une chaîne, en partant du caractère précisé
dans debut et sur une longueur précisé par longueur. Dans l'exemple ci-dessous, nous
extrayons l'initiale du prénom.

SELECT NoEmp, Nom || " " || SUBSTR(Prenom, 1, 1)


FROM Employe;
Et on ajoute un "." pour indiquer que c'est une initiale. Il n'y a pas de limite sur le nombre de
chaînes que l'on peut concaténer en une seule expression.

SELECT NoEmp, Nom || " " || SUBSTR(Prenom, 1, 1) || "."


FROM Employe;
Majuscule/Minuscule
Pour pouvoir transformer une chaîne en majuscule (et respectivement en minuscule), nous
avons à disposition la commande UPPER(chaine) (et resp. LOWER(chaine)). Cette
commande, comme toutes les autres, peut aussi être utilisé dans un WHERE.

SELECT NoEmp, UPPER(Nom) || " " || SUBSTR(Prenom, 1, 1) || "."


FROM Employe;
Longueur d'une chaîne
La commande LENGTH(chaine) permet de renvoyer la longueur de la chaîne (i.e. le nombre
de caractères, y compris les espaces).

SELECT NoEmp, Nom, LENGTH(Nom)


FROM Employe;
Modification d'une sous-chaîne
La commande REPLACE(chaîne, sc1, sc2) permet de remplacer la sous-chaîne sc1 par la
sous-chaîne sc2 dans la chaîne de caractères passée en premier paramètre. Ci-dessous, nous
remplaçons donc le terme "Chef" par le terme "Responsable" dans les titres de fonction des
employés.

SELECT Nom, Prenom, Fonction,


REPLACE(Fonction, "Chef", "Responsable")
FROM Employe;
Recherche d'une sous-chaîne
Pour rechercher la première apparition d'une sous-chaîne dans une chaîne, nous disposons de
la commande INSTR(chaîne, souschaine). Celle-ci recherche donc à quelle position dans la
chaîne se trouve la première occurence de la sous-chaîne. Si la sous-chaîne n'est pas présente,
la fonction renvoie 0.

Ci-dessous, nous cherchons la présence du terme "Ave." dans l'adresse des employés.

SELECT Nom, Adresse,


INSTR(Adresse, "Ave.")
FROM Employe;
Suppression de caractères
Les données saisies dans les tables contiennent parfois des espaces ou d'autres caractères
indésirables au début ou à la fin des données. La fonction TRIM() et ses variantes RTRIM()
et LTRIM() permettent de supprimer ces caractères. La requête suivante retire les points à la
fin des adresses.

SELECT Nom, Adresse,


RTRIM(Adresse, ".")
FROM Employe;
Transformation en chaîne de caractères
La fonction QUOTE() permet de transformer un littéral ou une valeur d'une colonne en texte
en ajoutant des ' autour.

SELECT QUOTE('mot')
FROM Employe;
SELECT QUOTE(Nom), QUOTE(Prenom)
FROM Employe;
Exercices
Dans une même requête, sur la table Client :
1. Concaténer les champs Adresse, Ville, CodePostal et Pays dans un nouveau champ
nommé Adresse complète, pour avoir :
Adresse, CodePostal Ville, Pays
2. Extraire les deux derniers caractères des codes clients
3. Mettre en minuscule le nom des sociétés
4. Remplacer le terme "marketing" par "mercatique" dans la fonction des contacts
5. Indiquer la présence du terme "Chef" dans la fonction du contact
6. Sélectionner les noms et prénoms des clients en mettant la première lettre de leur nom
en majuscule
7. Développez une requête permettant d'extraire le nom de domaine l'adresse email
nfp107@charon.org

Fonctions sur les dates


Nous disposons en SQL (comme dans d'autres langages) de plusieurs formats pour les dates.
Soit nous avons uniquement la date (jour, mois et année - stockée sous la forme d'un entier
représentant le nombre de jours depuis une date de référence, souvent le 1/1/1970). Il existe
aussi un format où la date, l'heure et le fuseau horaire sont stockées (précisemment, le
nombre de secondes depuis la même date de référence et le fuseau horaire).

Nous allons voir ici quelques fonctions utiles pour les dates : DATE() pour générer des dates,
STRFTIME() pour obtenir des informations à partir d'une date.

Vous trouverez sur cette page plus d'informations sur les fonctions disponibles.

Formats
Pour être interprétées par SQLite, les dates et heures peuvent être formatées de la façon
suivantes :

YYYY-MM-DD
YYYY-MM-DD HH:MM
YYYY-MM-DD HH:MM:SS
YYYY-MM-DD HH:MM:SS.SSS
YYYY-MM-DDTHH:MM
YYYY-MM-DDTHH:MM:SS
YYYY-MM-DDTHH:MM:SS.SSS
HH:MM
HH:MM:SS
HH:MM:SS.SSS
now
DDDDDDDDDD
Génération de dates
En premier lieu, si nous désirons avoir la date du jour (de l'exécution de la requête bien sûr),
nous pouvons exécuter cette requête. Par défaut, la date est affichée au format "YYYY-MM-
DD".

SELECT DATE("now");
La commande DATE() peut prendre d'autres paramètres après le premier contenant la date
(ou "now"), permettant de modifier cette date :

Modifier Fonction
+/- NNN days Ajoute ou retire des jours
+/- NNN months Ajoute ou retire des mois
+/- NNN years Ajoute ou retire des années
start of month Début du mois spécifié
start of year Début de l'année spécifiée
start of day Début du jour spécifié
weekday N Avance ou recule au jour
unixepoch Date au format Unix DDDDDDDDDD
localtime Ajuste une date UTF au fuseau horaire
utc Ajuste une date locale sur UTC
Pour weedday, les jours sont numérotés à partir de 0 : 0=Dimanche, 1=Lundi, 2=Mardi, ...

La requête suivante permet d'avoir la date de la veille.

SELECT DATE("now", "-1 day");


Il est possible de cumuler plusieurs modificateurs pour, par exemple, obtenir le dernier jour
du mois dernier.
SELECT DATE("now", "start of month", "-1 day");
Ou d'obtenir la date du mois prochain moins un jour

SELECT date('now','start of month','+1 month','-1 day');


La commande DATE() accepte aussi en premier paramètre une date au bon format, pour par
exemple lui appliquer une modification par la suite. Nous avons ici la date du lendemain de la
commande.

SELECT DATE(DateCom, "+1 day")


FROM Commande;
Génération d'heures
La fonction TIME() permet de récupérer l'heure courante du serveur. Le format par défaut est
HH:MM:SS.

SELECT TIME();
SELECT TIME("14:10:00");
La commande TIME() peut prendre d'autres paramètres après le premier contenant l'heure,
permettant de modifier cette heure.

La modification de l'heure se fait avec les modifieurs suivants :

Modifier Fonction
+/- NNN hours Ajoute ou retire des heures
+/- NNN minutes Ajoute ou retire des minutes
+/- NNN.NNNN seconds Ajoute ou retire des secondes
SELECT TIME("14:10:00","+3 hours");
SELECT
time('10:20:30','+1 hours','+20 minutes')
Dates et heures
La fonction DATETIME() permet de combiner la date et l'heure dans une seule valeur.
SELECT datetime("now") as "Date et heure courante";
Pour ajuster la valeur, il est possible d'utiliser tous les modifieurs que nous avons vu
précédemment :

SELECT datetime("now", "start of month", "+564 minutes") as "Date et heure";


SELECT datetime("now", "start of month", "+1 day", "+1 hour", "+20 minutes")
AS "Date et heure";
Informations à partir d'une date
La commande STRFTIME() permet elle d'obtenir des informations à partir d'une date. On
indique l'information désirée par un caractère précédé d'un "%"

Masque Fonction
%d Jour du mois : 00
%f Fractions de secondes : SS.SSS
%H Heure : 00-24
%j Jour de l'année : 001-366
%J Numéro de jour julien
%m Mois: 01-12
%M Minute: 00-59
%s Secondes since 1970-01-01s
%S Secondes: 00-59
%w Day of week 0-6 with Sunday==0
%W Week of year: 00-53
%Y Year: 0000-9999
%% %
Dans l'exemple ci-après, on récupère l'année ("%Y"), le mois ("%m") et le jour ("%d") de la
date actuelle. Il est aussi possible de les combiner pour écrire la date dans un format plus
classique pour nous.

SELECT DATE("now") AS "Aujourd'hui",


STRFTIME("%Y", "now") AS "Année",
STRFTIME("%m", "now") AS "Mois",
STRFTIME("%d", "now") AS "Jour",
STRFTIME("%d/%m/%Y", "now") AS "Nouveau format";
Il existe d'autres informations potentiellement intéressantes sur les dates, comme le jour de la
semaine ("%w"), le jour dans l'année ("%j") ou la semaine dans l'année ("%W").

SELECT DATE("now") AS "Aujourd'hui",


STRFTIME("%w", "now") as "Jour de la semaine",
STRFTIME("%j", "now") as "Jour de l'année",
STRFTIME("%W", "now") as "Semaine dans l'année";
Il faut noter que le jour de la semaine a une valeur entre 0 (pour le dimanche) et 6 (pour le
samedi). Par contre, le jour de l'année a une valeur entre 1 et 366. Le numéro de semaine dans
l'année commence lui aussi à 0 jusqu'à 52 (voire 53).

Nous disposons de deux autres informations très utiles pour les différences de dates :

le nombre de secondes depuis le 1/1/1970 avec "%s"


le jour julien (cf page Wikipedia) avec
soit "%J" dans la fonction STRFTIME()
soit la fontion %JULIANDAY()
SELECT DATE("now"),
STRFTIME("%s", "now") as "Nb secondes depuis 1/1/1970",
STRFTIME("%J", "now") as "Jour julien",
JULIANDAY("now") as "Jour julien";
Ainsi, il est possible de calculer le nombre de jours entre deux dates de 2 manières. Pour
rappel, une journée dure 24 heures de 60 minutes, chacune faisant 60 secondes, ce qui fait
qu'une journée dure 86400 secondes (24 $\times$ 60 $\times$ 60).

Dans l'exemple ci-dessous, nous calculons le nombre de jours qu'ont duré les Jeux
Olympiques 2016 (du 5 au 21 août).

SELECT JULIANDAY("2016-08-21") - JULIANDAY("2016-08-05");


On calcule la même différence en utilisant la fonction STRFTIME() et le nombre de secondes
depuis le 1/1/1970.
SELECT (STRFTIME("%s", "2016-08-21") - STRFTIME("%s", "2016-08-05")) / 86400;
Exercices
1. Afficher le jour de la semaine en lettre pour toutes les dates de commande
2. Compléter en affichant "week-end" pour les samedi et dimanche, et "semaine" pour
les autres jour
3. Calculer le nombre de jours entre la date de la commande (DateCom) et la date butoir
de livraison (ALivAvant), pour chaque commande
4. On souhaite aussi contacter les clients 1 an, 1 mois et 1 semaine après leur
commande. Calculer les dates correspondantes pour chaque commande

Fonctions de gestion des valeurs NULL


Il existe des fonctions utiles pour manipuler les valeurs nulles que l'on trouve dans les tables.
Nous allons voir IFNULL() pour remplacer les valeurs nulles par un autre, NULLIF() pour
remplacer une valeur par un null.

Remplacement de valeurs nulles


La fonction IFNULL() va remplacer une valeur nulle par une valeur de notre choix. Ici nous
remplaçons le numéro du Manager par le libellé "Personne" si la valeur est nulle.

SELECT Nom, Prenom, IFNULL(RendCompteA, 'Personne')


FROM Employe;
Suppression de valeurs
A l'inverse, la fonction NULLIF() permet de remplacer une valeur par un NULL. La requête
suivante remplace les Titres "Dr." par un null.

SELECT Nom, Prenom, NULLIF(TitreCourtoisie,'Dr.') FROM Employe;


Exercices
1. Lister tous les fournisseurs (Societe, Tel, Fax, PageAccueil) en remplaçant les
numéros de Fax manquant par la valeur "Fax non disponible" et les sites web
manquants par "Site web non disponible"
2. Lister tous les employés (Nom, Prénom, Fonction) en remplaçant les fonctions
"Représentant(e)" par une valeur NULL
3. Fabriquer un rapport au format CSV affichant, pour chaque produit :
Exercices complémentaires
1. Récupérer l'année de naissance et l'année d'embauche des employés
2. Calculer à l'aide de la requête précédente l'âge d'embauche et le nombre d'années dans
l'entreprise
3. Afficher le prix unitaire original, la remise en pourcentage, le montant de la remise et
le prix unitaire avec remise (tous deux arrondis aux centimes), pour les lignes de
commande dont la remise est strictement supérieure à 10%
4. Calculer le délai d'envoi (en jours) pour les commandes dont l'envoi est après la date
butoir, ainsi que le nombre de jours de retard
5. Rechercher les sociétés clientes, dont le nom de la société contient le nom du contact
de celle-ci
Nom
Disponibilité : "En stock" si le produit est en stock, sinon "En commande"
Quantité en stock
Quantité en commande
Réapprovisionnement "En lot" si UnitesCom > 0, sinon "A l'unité"

Dénombrements
Les dénombrements sont très utilisés en SQL. D'une part, car il est important de savoir
combien il y a de clients, commandes, ... D'autre part, c'est un moyen intéressant de contrôler
son travail lors de l'écriture d'un programme complexe, demandant plusieurs requêtes.

C'est le premier agrégat (ou calcul d'agrégat) à voir. Nous utilisons pour cela la commande
COUNT().

Nombre de lignes d'une table


Pour calculer le nombre de lignes d'une table, quelque soit les valeurs dans celle-ci, le plus
correct est d'utiliser COUNT(*). L'étoile ici indique que l'on prend toute la table, comme dans
une requête SELECT * FROM table.

Nous avons donc ici le nombre de clients contenus dans la base de données.

SELECT COUNT(*)
FROM Client;
Nombre de valeurs d'un attribut
Il est possible de spécifier un attribut dans le COUNT(). Ceci permettra de compte combien il
y a de lignes avec une valeur dans cet attribut. Si on l'utilise sur une clé primaire, nous
devrions obtenir le même résultat que précédemment.

SELECT COUNT(CodeCli)
FROM Client;
Par contre, si on indique un attribut dans lequel il y a des valeurs manquantes (i.e. NULL en
SQL), nous n'aurons pas le même résultat. Nous aurons donc le nombre de valeurs non nulles
de cet attribut. Ici, l'attribut est le numéro de fax du client. Comme il y a quelques clients sans
numéro de fax, nous n'obtenons pas le même résultat.

SELECT COUNT(Fax)
FROM Client;
Nombre de valeurs distinctes d'un attribut
Pour aller plus loin, il est aussi possible d'ajouter la clause DISTINCT avant l'attribut, pour
obtenir le nombre de valeurs distincts de cet attribut. Ci-dessous, nous avons donc le nombre
de valeurs distinctes de l'attribut Pays. Ce qui nous donne le nombre de pays de la clientèle.

SELECT COUNT(DISTINCT Pays)


FROM Client;
Ce nombre correspond au nombre de lignes de la table résultante de la requête suivante.

SELECT DISTINCT Pays


FROM Client;
Restriction dans le dénombrement
Pour dénombrer des sous-ensembles d'une table, il est bien évidemment possible d'ajouter des
restrictions à la requête. Par exemple, ci-dessous, nous calculons le nombre de clients français
présents dans la base.

SELECT COUNT(*)
FROM Client
WHERE Pays = "France";
Exercices
1. Calculer le nombre d'employés qui sont "Représentant(e)"
2. Calculer le nombre de produits de moins de 50 euros
3. Calculer le nombre de produits de catégorie 2 et avec plus de 10 unités en stocks
4. Calculer le nombre de produits de catégorie 1, des fournisseurs 1 et 18
5. Calculer le nombre de pays différents de livraison
6. Calculer le nombre de commandes réalisées le 28/03/2016.

Calculs statistiques simples


Outre les dénombrements, il est possible de faire quelques calculs statistiques de base, tels
que somme, moyenne, minimum et maximum.

Somme
La fonction SUM(attribut) permet donc de faire la somme des valeurs non nulles de l'attribut
passé en paramètre. La requête suivante nous permet d'avoir le nombre total d'unités de
produits en stock.

SELECT SUM(UnitesStock)
FROM Produit;
Bien évidemment, ce calcul peut se faire suite à une restriction, c'est-à-dire sur un sous-
ensemble de la table. Ici, nous calculons le nombre d'unités en stock pour tous les produits de
la catégorie 1.

SELECT SUM(UnitesStock)
FROM Produit
WHERE CodeCateg = 1;
Le mot clé DISTINCT permet de faire la somme des valeurs distinctes sans tenir compte des
doublons

SELECT SUM(DISTINCT PrixUnit)


FROM Produit
WHERE CodeCateg = 1;
Moyenne
Bien qu'avec un SUM() et un COUNT(), on puisse obtenir la moyenne, il existe la fonction
AVG(attribut) (pour average) permettant de la calculer directement. La requête ci-dessous
permet de calculer le prix unitaire moyen des produits.
SELECT AVG(PrixUnit)
FROM Produit;
Le mot clé DISTINCT permet de faire la moyenne des valeurs distinctes sans tenir compte
des doublons

SELECT AVG(DISTINCT PrixUnit)


FROM Produit;
Dans la plupart des cas, il sera nécessaire d'améliorer la lisibilité du résultat en arrondissant
les valeurs, très souvent à 2 décimales, comme ci-après.

SELECT ROUND(AVG(PrixUnit), 2)
FROM Produit;
Médiane
En statistique, il est souvent préférable de calculer la médiane plutôt que la moyenne, ce
qu'on peut faire avec la fonction MEDIAN(attribut), tel que l'on peut voir dans la requête
suivante.

SELECT MEDIAN(PrixUnit)
FROM Produit;
Minimum et maximum
Enfin, deux autres fonctions utiles sont disponibles : MIN(attribut) et MAX(attribut),
permettant d'obtenir respectivement le minimum et le maximum d'un attribut, sans tenir
compte des valeurs nulles. Nous obtenons donc avec cette requête le prix minimum et le prix
maximum.

SELECT MIN(PrixUnit), MAX(PrixUnit)


FROM Produit
Exercices
1. Calculer le coût moyen du port pour les commandes du client dont le code est
"QUICK" (attribut CodeCli)
2. Calculer le coût du port minimum et maximum des commandes
3. Pour chaque messager (par leur numéro : 1, 2 et 3), donner le montant total des frais
de port leur correspondant
4. Afficher le prix minimum et le prix maximum d'un produit
Agrégats selon attribut(s)
Si l'on désire avec un calcul statistique selon un critère (que l'on appellera critère
d'agrégation), il n'est pas nécessaire de répéter la requête autant de fois qu'on a de valeurs
pour le critère. Il existe pour cela la commande GROUP BY, qui permet de faire un calcul
d'agrégat (COUNT(), SUM(), AVG(), ...) pour chaque valeur du critère d'agrégation.

Le GROUP BY doit être obligatoirement placé après le WHERE dans la requête.

Agrégat simple
Le critère d'agrégation est souvent un seul attribut (par exemple, on souhaite le nombre
d'étudiants de chaque sexe).

Utilisation classique
Le premier exemple que nous allons voir est le dénombrement. Nous désirons le nombre de
clients de la société, pour chaque pays d'origine. Donc, nous voudrions voir afficher l'attribut
Pays en plus du compte. La requête suivante, erronée, est celle qu'on pourrait écrire en
premier.

SELECT Pays, COUNT(*)


FROM Client;
Une fois exécutée, on se rend compte qu'elle ne renvoie qu'une seule ligne, avec un seul pays
(celui en dernier dans la table) et le nombre total de clients. Pour être correct, il faut spécifier
le critère d'agrégation (ici le pays) dans la clause GROUP BY, comme ci-dessous.

SELECT Pays, COUNT(*)


FROM Client
GROUP BY Pays;
Ici, le résultat est ordonné par pays. On peut améliorer la lisibilité du résultat en renommant
le dénombrement et en ordonnant de manière décroissante par celui-ci.

SELECT Pays, COUNT(*) AS "Nb clients"


FROM Client
GROUP BY Pays
ORDER BY 2 DESC;
Ce mécanisme fonctionne bien évidemment avec tous les autres calculs d'agrégats que nous
avons vu précédemment (SUM(), AVG(), ...).

Combinaison d'agrégats
Il est aussi possible de calculer directement plusieurs agrégats en une seule requête, comme
ci-dessous. Nous cherchons donc à avoir, pour chaque fournisseur :

le nombre de produits
le prix moyen (arrondi à l'entier)
le prix minimum
le prix maximum
SELECT NoFour,
COUNT(*) AS "Nb produits",
ROUND(AVG(PrixUnit)) AS "Prix moyen",
MIN(PrixUnit) as "Prix minimum",
MAX(PrixUnit) as "Prix maximum"
FROM Produit
GROUP BY NoFour;
Agrégat complexe
Par contre, il arrive que nous souhaitions avoir un critère d'agrégation prenant en compte
plusieurs attributs. Par exemple, on peut vouloir connaître le nombre d'étudiants en DUT
STID par sexe et par année (1ère ou 2ème). Dans ce cas, nous aurons à calculer quatre
valeurs :

le nombre d'hommes en 1ère année


le nombre d'hommes en 2ème année
le nombre de femmes en 1ère année
le nombre de femmes en 2ème année
Dans ce cas, il faut spécifier les attributs à la fois dans le SELECT et dans le GROUP BY.
Ci-dessous, nous cherchons donc à connaître le nombre de produits pour chaque fournisseur
et chaque catégorie. La première ligne nous indique qu'on a 2 produits en stock du
fournisseur 1 et de la catégorie 1.
SELECT NoFour, CodeCateg, COUNT(*)
FROM Produit
GROUP By NoFour, CodeCateg;
Plus généralement, il est obligatoire que les attributs présents dans le SELECT soient aussi
présents dans le GROUP BY. Dans le cas contraire, le résultat ne correspondra pas à ce qu'on
cherche à obtenir et ce n'est pas toujours facile à repérer.

Par exemple ici, en ne mettant pas CodeCateg dans le GROUP BY, on a bien un résultat mais
seul un numéro de catégorie est retenu (au hasard) pour chaque fournisseur. Le fournisseur 4,
qui a trois catégories (6, 7 et 8) avec chacune 1 seul produit, n'est plus que sur une seule ligne
(avec la catégorie 7 affichée, mais bien 3 produits en tout).

SELECT NoFour, CodeCateg, COUNT(*)


FROM Produit
GROUP By NoFour;
Exercices
1. Donner le nombre d'employés par fonction
2. Donner le montant moyen du port par messager
3. Donner le nombre de catégories de produits fournis par chaque fournisseur
4. Donner le prix moyen des produits pour chaque fournisseur et chaque catégorie de
produits fournis par celui-ci
5. Depuis la table DetailCommande, donner le prix total et le montant total de TVA de
chaque commande
6. Donner le prix moyen et le prix médian des produits de la catégorie numéro 2
7. Lister le nombre de client par pays et par ville
8. Lister les cinq employés ayant géré le plus de commandes

Restriction sur agrégats


Il arrive que nous souhaitions restreindre les résultats avec une condition sur un calcul
d'agrégat. Par exemple, on peut vouloir chercher le nombre de clients par pays, uniquement
pour les pays avec plus de 10 clients. Dans la requête suivante, il faudrait donc chercher à la
main les pays avec plus de 10 clients, ce qui est à proscrire car non-automatique.

SELECT Pays, COUNT(*)


FROM Client
GROUP BY Pays;
La première idée serait de faire une restriction sur le COUNT(*) dans la clause WHERE,
comme ci-dessous. Comme vous pourrez le voir, cette requête ne fonctionne pas, car le
COUNT(*) est mal placé.

SELECT Pays, COUNT(*)


FROM Client
WHERE COUNT(*) >= 10
GROUP BY Pays;
Pour effectuer ces restrictions, il est nécessaire d'utiliser la clause HAVING, situé
obligatoirement après le GROUP BY. Dans notre exemple, nous devons donc écrire la
requête suivante.

SELECT Pays, COUNT(*)


FROM Client
GROUP BY Pays
HAVING COUNT(*) >= 10;
Pour améliorer la lisibilité, il est aussi possible de renommer le résultat de l'agrégat, et
d'utiliser ce nouveau nom dans la condition du HAVING.

SELECT Pays, COUNT(*) AS Nb


FROM Client
GROUP BY Pays
HAVING Nb >= 10;
Placement des différentes clauses
Nous avons maintenant vu tous les clauses existantes dans une requête SQL de type
SELECT. Il est important de se souvenir de l'ordre d'apparition de celles-ci, tel que présenté
ci-dessous. Si cet ordre n'est pas respecté, le moteur SGBD ne pourra pas traiter la requête et
renverra une erreur.

SELECT attributs, calculs, agrégats


FROM tables
WHERE conditions
GROUP BY attributs
HAVING conditions
ORDER BY attributs
LIMIT nombre;
Exercices
1. Lister les fournisseurs ne fournissant qu'un seul produit
2. Lister les catégories dont les prix sont en moyenne supérieurs strictement à 150 euros
3. Lister les fournisseurs ne fournissant qu'une seule catégorie de produits
4. Lister les fonctions pour lesquelles la moyenne d'âge des employés dépasse les 45 ans
5. Lister les numéros de commandes et leur prix total lorsque qu'il est supérieur ou égal
à 1000€ en les triant par montant décroissant

Exercices complémentaires
1. Donner la quantité totale commandée par les clients, pour chaque produit
2. Donner les cinq clients avec le plus de commandes, triés par ordre décroissant
3. Calculer le montant total des lignes d'achats de chaque commande, sans et avec
remise sur les produits
4. Pour chaque catégorie avec au moins 10 produits, calculer le montant moyen des prix
5. Donner le numéro de l'employé ayant fait le moins de commandes
6. Lister les numéros de commande en indiquant le nombre de produits s'y trouvant
7. Lister les numéros de commande en indiquant le nombre de produits s'y trouvant
lorsqu'il y a plus de 2 produits dans la commande

Jointures naturelles
Une jointure entre deux tables permet de combiner l'information contenue entre les deux
tables. Comme vous avez pu le remarquer, les données sont découpées de façon très
détaillées dans différentes tables, ceci pour différentes raisons. Pour recouper ces données, il
est nécessaire de les combiner et donc de faire des jointures.

Principe
La jointure naturelle (NATURAL JOIN) permet de recouper les lignes de chaque table ayant
les mêmes valeurs pour les attributs ayant le même nom entre les deux tables.

Par exemple, nous souhaitons connaître le nom de la catégorie de chaque produit. Pour cela,
nous devons joindre les deux tables Produit et Categorie. Dans les deux, il existe l'attribut
CodeCateg. La jointure va donc permettre d'ajouter, pour chaque produit, le nom de la
catégorie (NomCateg) et la descrition (Description).
SELECT *
FROM Produit NATURAL JOIN Categorie;
Bien évidemment, il est possible de réaliser toute autre opération vu précédemment, dont les
projections. Ici, nous nous restreignons à la référence du produit, son nom et la catégorie de
celui-ci.

SELECT RefProd, NomProd, NomCateg


FROM Produit NATURAL JOIN Categorie;
Si on souhaite avoir l'ensemble des colonnes d'une des tables, il est aussi possible de
l'indiquer dans le SELECT avec le formalisme table.*:

SELECT RefProd, NomProd, Categorie.*


FROM Produit NATURAL JOIN Categorie;
Ce processus nous permet aussi de se faire des restrictions sur un attribut d'une table pour
qu'elles soient répercuter sur l'autre. Par exemple, ici, on ne retient que les produits fournis
par des entreprises françaises.

SELECT NomProd, Societe


FROM Produit NATURAL JOIN Fournisseur
WHERE Pays = "France";
Mais il est d'autant plus intéressant quand on veut faire des agrégats. Par exemple, si nous
souhaitons connaître le nombre de produits par catégorie, plutôt que de présenter les codes de
catégorie, nous allons chercher à présenter le nom des catégories. Le résultat sera ainsi plus
parlant.

SELECT NomCateg, COUNT(*) AS "Nb Produits"


FROM Produit NATURAL JOIN Categorie
GROUP BY NomCateg
ORDER BY 2 DESC;
Multiples jointures
Il est bien sûr possible de faire plusieurs jointures naturelles dans une même requête. Pour
cela, il faut ajouter des parenthèses chaque NATURAL JOIN (et les tables concernées).
Ici, nous ajoutons à la fois les informations de Categorie, mais aussi de Fournisseur, à la table
Produit.

SELECT *
FROM (Produit NATURAL JOIN Categorie)
NATURAL JOIN Fournisseur;
Problème possible
Des problèmes peuvent survenir quand les tables ont des noms de variables identiques, mais
qu'on ne souhaite pas à ce qu'elles servent pour la jointure. Dans les tables Client et Employe,
il existe les attributs Ville, Pays, ... présents dans les deux tables. Ceci va perturber la
jointure, puisqu'il va chercher à faire correspondre (i.e. chercher l'égalité) tous les attributs
ayant le même nom.

SELECT *
FROM (Commande NATURAL JOIN Client)
NATURAL JOIN Employe;
Un jointure naturelle n'est donc pas réalisable lorsque :

les tables ont des attributs ayant le même nom qu'on ne cherche pas à mettre en relation
les tables n'ont pas d'attributs avec le même nom
Exercices
1. Récupérer les informations des fournisseurs pour chaque produit
2. Afficher les informations des commandes du client "Lazy K Kountry Store"
3. Afficher le nombre de commande pour chaque messager (en indiquant son nom)
4. Afficher, pour chaque commande, les informations suivantes : Numéro de commande,
Montant, total, Montant de la remise, Montant total remisé
5. Lister les produits et afficher pour chacun le nombre de fois où ils ont été commandés
6. Lister les clients et afficher le nombre de produits différents qu'ils ont commandé

Jointures internes
Principe
Une jointure interne (INNER JOIN) est faite pour pallier aux problèmes de la jointure
naturelle. Ici, nous allons préciser sur quel(s) attribut(s) nous allons chercher l'égalité. Par
contre, si un attribut est présent dans les deux tables, il va falloir préciser auquel on fait
référence en indiquant la table d'origine avant (avec le formalisme table.attribut).
En reprenant le premier exemple précédent, voici la requête reliant les produits avec les
catégories, réalisée avec une jointure interne.

SELECT *
FROM Produit INNER JOIN Categorie
ON Produit.CodeCateg = Categorie.CodeCateg;
De même pour les jointures naturelles, il est possible de réaliser d'autres opérations, en plus
de la jointure.

Si nous souhaitons refaire la requête récupérant les produits (nom du produit et du


fournisseur) fournis par des entreprises françaises, nous aurons la requête suivante.

SELECT NomProd, Societe


FROM Produit INNER JOIN Fournisseur
ON Produit.NoFour = Fournisseur.NoFour
WHERE Pays = "France";
Si on souhaite maintenant le nom de catégorie et le nombre de produits associés, avec une
jointure interne, voici comment faire.

SELECT NomCateg, COUNT(*) AS "Nb Produits"


FROM Produit INNER JOIN Categorie
ON Produit.CodeCateg = Categorie.CodeCateg
GROUP BY NomCateg
ORDER BY 2 DESC;
Alias pour les tables
Pour simplifier l'écriture des requêtes, il est possible de renommer temporairement une table
(valable uniquement dans la requête présente), avec l'opérateur AS.

Reprenons la requête précédent en renommant Produit en P et Categorie en C. Ce processus


est utile quand on écrit des requêtes longues.
SELECT *
FROM Produit AS P INNER JOIN Categorie AS C
ON P.CodeCateg = C.CodeCateg;
Il est aussi possible de ne pas indiquer le terme AS, le renommage sera tout de même pris en
compte.

Ainsi, la requête précédente devient la suivante.

SELECT *
FROM Produit P INNER JOIN Categorie C
ON P.CodeCateg = C.CodeCateg;
Jointures multiples
De même que pour une jointure naturelle, il est possible d'enchaîner les jointures internes,
autant de fois que nécessaire. Ceci peut produire des requêtes très longues et difficiles à lire.

SELECT *
FROM (Produit P INNER JOIN Categorie C
ON P.CodeCateg = C.CodeCateg)
INNER JOIN Fournisseur F
ON P.NoFour = F.NoFour;
Lignes manquantes
Le défaut de ce type de jointure (naturelle ou interne) est qu'une ligne d'une table n'ayant pas
de correspondances dans l'autre table n'est pas conservé dans le résultat.

Si nous comptons le nombre de clients dans la table Client, et le nombre de clients différents
dans la table Commande, nous voyons que ce n'est pas le même résultat. Certains client de la
table Client n'ont pas de commandes associés.

SELECT COUNT(*)
FROM Client;
SELECT COUNT(DISTINCT CodeCli)
FROM Commande;
Exercices
1. Récupérer les informations des fournisseurs pour chaque produit, avec une jointure
interne
2. Afficher les informations des commandes du client "Lazy K Kountry Store", avec une
jointure interne
3. Afficher le nombre de commande pour chaque messager (en indiquant son nom), avec
une jointure interne
4. Afficher une synthèse des commandes à partir des tables correspondantes les champs :
Nom et prénom du client, Numéro de commande, Nom du produit, Nom du
fournisseur du produit, Quantité commandée, Prix total pour produit
5. Afficher pour chaque employé la liste des commandes qu'il a traité avec leur montant
total
6. Afficher la liste des clients ayant commandé pour plus de 100€ du produit référence
numéro 68

Jointures externes
Principe
Comme indiqué précédemment, si une ligne d'une table n'a pas de correspondance dans
l'autre table, celle-ci ne sera pas conservé dans le résultat.

Ainsi, les clients sans commande associée ne seront pas dans la table résultante de la jointure
naturelle ou interne entre Client et Commande.

Pour cela, nous devons utiliser une jointure externe (OUTER JOIN) . Celle-ci a pour but de
garder toutes les lignes des deux tables (ou d'une seule des deux).

Une jointure se fait entre deux tables. Nous parlerons de jointure externe gauche (LEFT
OUTER JOIN) quand nous garderons les lignes de la table à gauche (la première citée donc).
Et nous parlerons de jointure externe droite (RIGHT OUTER JOIN) quand nous garderons
les lignes de la table à droite (la deuxième donc). Enfin, si l'on souhaite garder toutes les
lignes des deux tables, il faut faire une jointure externe complète (FULL OUTER JOIN).

ATTENTION Dans l'outil utilisé ici, seule la jointure externe gauche est implémenter. Pour
obtenir la jointure externe droite, il suffit d'inverser les tables. Pour la jointure complète, nous
devrons utiliser en plus un opérateur que nous verrons plus tard.
Dans notre cas, si nous souhaitons avec les clients avec les détails de commande, tout en
gardant la table de résultat les clients sans commande, nous devons donc réaliser une jointure
externe gauche entre Client (à gauche) et Commande (à droite).

SELECT *
FROM Client Cl LEFT OUTER JOIN Commande Co
ON Cl.CodeCli = Co.CodeCli;
Si vous regardez les lignes pour les clients "ESPAC" et "ETOUR" (entre autres), vous verrez
qu'il n'y a aucune information sur les attributs de la table Commande (à partir de NoCom).

De plus, nous voyons qu'il y a 835 lignes dans la table résultat, correspondant aux 830
commandes plus les 5 clients n'ayant pas de commande associée.

Décompte avec prise en compte du 0


L'intérêt de ce type de jointure réside principalement dans du dénombrement, en gardant
l'information comme quoi certaines modalités de dénombrement n'ont aucune ligne associée.

Le problème est que si on réalise un décompte classique (avec COUNT(*) donc), ces lignes
ne sont pas gardées finalement.

Par exemple, si nous souhaitons connaître le nombre de commandes par client, il serait usuel
de faire la requête suivante. On remarque qu'il y a bien tous les clients (dont ESPAC et
ETOUR) mais tous avec au minimum un décompte à 1. C'est normal car COUNT(*) compte
le nombre de lignes. Et il y a bien au moins une ligne par client.

SELECT Cl.CodeCli, COUNT(*)


FROM Client Cl LEFT OUTER JOIN Commande Co
ON Cl.CodeCli = Co.CodeCli
GROUP BY Cl.CodeCli
ORDER BY 2;
Si l'on veut avoir le nombre de commandes, et donc 0 pour ceux n'ayant aucune commande, il
faut compter le nombre de valeurs non nulles d'un attribut, et de préférence la clé primaire de
la deuxième table.
Ainsi, si nous comptons le nombre de valeurs non nulles de NoCom (avec
COUNT(NoCom)), comme fait dans la requête qui suit, nous avons bien un 0 qui apparaît
pour les clients n'ayant aucune commande associée.

SELECT Cl.CodeCli, COUNT(NoCom)


FROM Client Cl LEFT OUTER JOIN Commande Co
ON Cl.CodeCli = Co.CodeCli
GROUP BY Cl.CodeCli
ORDER BY 2;
Ainsi, ce processus, combiné avec une condition sur l'agrégat (avec HAVING), nous permet
de pouvoir récupérer uniquement les lignes d'une table n'ayant pas de correspondance dans
l'autre.

Par exemple, si nous souhaitons connaître le nom des sociétés clientes pour lesquelles nous
n'avons pas de commandes associées, nous pourrions faire la requête suivante. Ici, Cl.*
permet de récupérer toutes les informations de la table Client.

SELECT Cl.*
FROM Client Cl LEFT OUTER JOIN Commande Co
ON Cl.CodeCli = Co.CodeCli
GROUP BY Cl.CodeCli
HAVING COUNT(NoCom) == 0;
Maintenant, si on ajoute un autre calcul d'agrégat (somme, moyenne, ...), le résultat sera
NULL pour les lignes n'ayant pas de correspondances.

Par exemple, si nous calculons les frais de port moyens pour chaque client, nous n'avons pas
de résultat pour les clients n'ayant aucune commande.

SELECT Cl.CodeCli, COUNT(NoCom), AVG(Port)


FROM Client Cl LEFT OUTER JOIN Commande Co
ON Cl.CodeCli = Co.CodeCli
GROUP BY Cl.CodeCli
ORDER BY 2;
Jointures externes complètes
Les jointures externes peuvent être ouvertes "des deux côtés", en mettant en correspondance
toutes les lignes qui vont ensemble et en ajoutant les autres lignes des autres tables.

Par exemple, nous pouvons lister les produits commandés avec leur client correspondants, en
incluant les clients n'ayant rien commandé et les produits jamais commandés.

Voici un exemple non fonctionnel car non supporté par SQLite dans cet environnement :

SELECT ...
FROM Client Cl FULL OUTER JOIN Commande Co
Exercices
1. Compter pour chaque produit, le nombre de commandes où il apparaît, même pour
ceux dans aucune commande
2. Lister les produits n'apparaissant dans aucune commande
3. Existe-t'il un employé n'ayant enregistré aucune commande ?

Jointures à la main
Comme dit précédemment, le défaut des jointures internes (et dans une moindre mesure
naturelles) est la lourdeur du code écrit dans le cas de plusieurs jointures.

Principe
Produit cartésien
On parle de produit cartésien quand on cherche à coupler toutes les lignes d'une table avec
chaque ligne de l'autre table. Considérons que la table A à nbA lignes, et la table B nbB
lignes. En résultat, nous obtenons donc une table avec nbA * nbB lignes. Ce qui peut vite
faire beaucoup. Par exemple, la table Client fait 94 lignes, la table Commande 830. Le
produit cartésien des deux tables produit une table de 78020 lignes. Il faut donc faire attention
quand on fait une telle opération.

Pour mieux comprendre le produit cartésien, on peut regarder la requête suivante. Elle
associe chaque produit (84 lignes), avec chaque catégorie (8 lignes), ce qui fait une table
résultat de 672 lignes. Vous remarquerez que la requête est un peu lente à être exécutée.

SELECT *
FROM Produit, Categorie;
Restriction sur produit cartésien
Bien évidemment, dans un produit cartésien, toutes les lignes ne sont pas intéressantes dans le
résultat. Celles qui nous intéresse est celles où il y a correspondance entre certains attributs de
chaque table. Une jointure est finalement une restriction sur produit cartésien, celle-ci
apparaissant dans le WHERE.

Pour reprendre notre exemple précédent, pour faire correspondre chaque produit avec sa
catégorie via une jointure à la main, nous allons réaliser la requête suivante.

SELECT *
FROM Produit, Categorie
WHERE Produit.CodeCateg = Categorie.CodeCateg;
Comme précédemment, il est aussi possible de renommer temporairement les tables pour
simplifier l'écriture de la requête.

SELECT *
FROM Produit P, Categorie C
WHERE P.CodeCateg = C.CodeCateg;
Jointures multiples
Nous allons comparer ici les requêtes permettant de réaliser de multiples jointures. Si nous
souhaitons compter pour chaque client et pour chaque messager, le nombre de commandes
passées, nous devons réaliser les requêtes suivantes.

Jointure naturelle
Cette requête ne fonctionne pas, car l'attribut Tel est présent dans Client et dans Messager. Il
va donc chercher l'égalité sur cet attribut aussi. Et il y a peu de chances qu'un client ait le
même numéro de téléphone qu'un messager.

SELECT Societe, NomMess, COUNT(*)


FROM (Client NATURAL JOIN Commande)
NATURAL JOIN Messager
GROUP BY Societe, NomMess;
Jointure interne
Pour qu'elle fonctionne, nous devons passer par des jointures internes. Cette requête s'écrit
donc de la manière suivante. La lecture n'est pas la plus aisée, surtout pour repérer les tables
prises en compte.

SELECT Societe, NomMess, COUNT(*)


FROM (Client Cl INNER JOIN Commande Co
ON Cl.CodeCli = Co.CodeCli)
INNER JOIN Messager M
ON Co.NoMess = M.NoMess
GROUP BY Societe, NomMess;
Jointure à la main
Dans ce type de jointure, nous devons déjà lister dans le FROM les tables nécessaires, puis
dans le WHERE mettre les conditions de jointures. Il en résulte une requête plus facile à
décoder dans la lecture des tables prises en compte.

SELECT Societe, NomMess, COUNT(*)


FROM Client Cl, Commande Co, Messager M
WHERE Cl.CodeCli = Co.CodeCli
AND Co.NoMess = M.NoMess
GROUP BY Societe, NomMess;
Limitations
Ce type de jointure ne permet pas de faire de jointure externe.

Exercices
1. Récupérer les informations des fournisseurs pour chaque produit, avec jointure à la
main
2. Afficher les informations des commandes du client "Lazy K Kountry Store", avec
jointure à la main
3. Afficher le nombre de commande pour chaque messager (en indiquant son nom), avec
jointure à la main

Auto-jointures
Certains modèles de données sont organisés pour permettre l'exécution de requêtes SQL
hiérarchiques.
Principe
Lorsque la table possède une clé étrangère vers elle même, elle s'appelle une clé d'auto-
référencement. Ces clés permettent de faire des auto-jointures pour exécuter des requêtes
récursives.

Syntaxe
Comme il n'est pas possible de joindre deux fois la table sur elle même car le serveur ne
comprends pas ce que l'on souhaite sélectionner. Cette requête renvoie une erreur :
ambiguous column name: Nom

SELECT Nom, Prenom, Nom, Prenom


from Employe, Employe
where RendCompteA = NoEmp;
Exemple d'autojointure
La syntaxe impose d'utiliser les alias de tables pour faire ce genre de requête :

SELECT e.Nom, e.Prenom, m.Nom, m.Prenom


from Employe e, Employe m
where e.RendCompteA = m.NoEmp;
Avec la nouvelle syntaxe :

SELECT e.Nom, e.Prenom, m.Nom, m.Prenom


FROM Employe e INNER JOIN Employe m
ON (e.RendCompteA = m.NoEmp);
En mode externe pour récupérer le patron qui n'a pas de responsable, sa colonne
"RendCompteA" est nulle :

SELECT e.Nom, e.Prenom, m.Nom, m.Prenom


FROM Employe e LEFT OUTER JOIN Employe m
ON (e.RendCompteA = m.NoEmp);
Exercices complémentaires
1. Compter le nombre de produits en stock par fournisseur en affichant le Libellé du
fournisseur
2. Compter le nombre de produits par pays d'origine des fournisseurs
3. Compter pour chaque employé le nombre de commandes gérées, même pour ceux
n'en ayant fait aucune
4. Compter la quantité de produits commandés pour chaque client et pour chaque
catégorie
5. Lister le chiffre d'affaires et la somme des remises faites à chaque client
6. Pour chaque client, lister les catégories avec leur chiffre d'affaire associé
7. Compter le nombre de produits vendus par fournisseur et le chiffre d'affaires associé
8. Produsez un rapport au format texte affichant pour chaque employé : L'employé
N°[Matricule], [Prénom] [Nom] a pour supérieur hiérarchique direct [Prénom] [Nom]

Sous-requêtes
Il est possible et souvent intéressant d'utiliser des sous-requêtes, renvoyant donc une table,
dans une requête. On peut ainsi

éviter des jointures, pouvant être parfois longue à effectuer


faire des calculs et utiliser le résultat dans un autre
...
Il est possible d'utiliser une sous-requêtes dans les clauses SELECT, FROM, WHERE, et
JOIN des requêtes externes.

Dans la clause WHERE


Il est déjà possible de comparer un attribut avec le résultat d'une requête. Ici, nous cherchons
les commandes du client "Bon app". On peut bien sûr réaliser cette opération avec une
jointure, comme ci-dessous.

SELECT NoCom
FROM Commande NATURAL JOIN Client
WHERE Societe = "Bon app";
Si nous avons beaucoup de commandes et de clients, cette jointure peut prendre beaucoup de
temps. On va donc chercher les commandes pour lesquelles CodeCli est égal à celui de
l'entreprise "Bon app". La sous-requête ici nous permet de retrouver cette valeur.

SELECT NoCom
FROM Commande
WHERE CodeCli = (SELECT CodeCli
FROM Client
WHERE Societe = "Bon app");
Dans cet exemple, la requête sur la table Commande est dite externe, la requête sur la table
Client est appellée sous-requête.

Idem mais avec plusieurs retours


Si la recherche concerne plusieurs valeurs, il faut donc utiliser l'opérateur IN, qui teste si une
valeur est présente dans une liste. Ici, nous cherchons les commandes des clients français,
toujours possible avec une jointure.

SELECT NoCom
FROM Commande NATURAL JOIN Client
WHERE Pays = "France";
Pour les mêmes raisons que précédemment, on peut choisir de ne pas faire de jointure et
d'utiliser une sous-requête. Celle-ci va rechercher les code des clients français. Et la requête
va rechercher les commandes pour lesquelles CodeCli est dans la liste renvoyée par la sous-
requête.

SELECT NoCom
FROM Commande
WHERE CodeCli IN (SELECT CodeCli
FROM Client
WHERE Pays = "France");
Dans les clauses FROM et JOIN
On a aussi la possibilité de faire une sous-requête dans la partie FROM de la requête. Ceci
peut permettre de faire une restriction avant la jointure, ou de faire des calculs.
Il est également possible d'utiliser un SELECT à la place d'une table dans la clause FROM :

SELECT NoCom
FROM (SELECT *
FROM Client NATURAL JOIN Commande
WHERE Pays = "France");
En reprenant l'exemple du client "Bon app", on peut aussi faire la requête suivante.

SELECT NoCom
FROM Commande NATURAL JOIN
(SELECT *
FROM Client
WHERE Societe = "Bon app");
Les commandes des clients français peuvent aussi s'obtenir de cette façon.

SELECT NoCom
FROM Commande NATURAL JOIN
(SELECT *
FROM Client
WHERE Pays = "France");
Mais si on souhaite calculer le coût d'une commande, nous sommes obligé de passer par ce
mécanisme. En effet, nous devons d'abord faire la somme des montants pour chaque produit
et ajouter les frais de port au total. Il n'est pas possible de faire tout en une requête, car dans
ce cas, en faisant la jointure entre Commande et DetailCommande, nous dupliquons les frais
de port par autant de fois qu'il y a de produits différents dans la commande. Pour le faire
proprement, il faut donc réaliser la commande suivante.

SELECT NoCom, Port + TotalProd AS Total


FROM Commande NATURAL JOIN
(SELECT NoCom,
SUM(PrixUnit * Qte * (1 - Remise)) AS TotalProd
FROM DetailCommande
GROUP BY NoCom);
Dans le SELECT
La syntaxe ci-dessous permet de sélectionner des données avec une jointure directement dans
la clause SELECT de la requête principale. Cela permet parfois de pivoter des données qui
sont en colonnes pour les sortir en ligne.

SELECT c.NoCom, (SELECT p.NomProd FROM Produit p where p.RefProd=c.RefProd)


FROM DetailCommande c;
Sous-requêtes corrélées
Il est possible de faire référence dans une sous-requête à une valeur de la table (ou des tables)
de la requête initiale.

On peut par exemple chercher les produits pour lesquels il existe une vente (table
DetailCommande) de celui-ci au même prix que le prix actuel (donc celui dans Produit).

Attention, avec cette syntaxe la requête imbriquée est exécutée pour chaque ligne renvoyée
par la requête principale : les performances peuvent être décevantes !

SELECT RefProd, NomProd, PrixUnit


FROM Produit P
WHERE PrixUnit IN (SELECT PrixUnit
FROM DetailCommande
WHERE RefProd = P.RefProd);
Exercices
1. Lister les employés n'ayant jamais effectué une commande, via une sous-requête
2. Nombre de produits proposés par la société fournisseur "Mayumis", via une sous-
requête
3. Lister les clients dont le panier moyen est supérieur au panier moyen de tous les
clients
4. Nombre de commandes passées par des employés sous la responsabilité de "Patrick
Emery"
5. Lister les numéros de commande ayant un montant supérieur à au montant moyen des
commandes
6. Lister les clients ayant commandé pour plus (en valeur) que le client "HANAR"
Interface

Opérateur EXISTS
L'opérateur EXISTS permet de tester si une sous-requête renvoie un résultat ou non. En
faisant en plus référence à une valeur d'un attribut de la table dans la première requête, cela
permet de tester des existences de faits.

Par exemple, si l'on souhaite avoir les clients ayant au moins une commande, on peut faire
comme ci-dessous.

SELECT *
FROM Client Cl
WHERE EXISTS (SELECT *
FROM Commande
WHERE CodeCli = Cl.CodeCli);
On peut faire aussi l'inverse de cette requête en cherchant les clients n'ayant pas de
commande.

SELECT *
FROM Client Cl
WHERE NOT EXISTS (SELECT *
FROM Commande
WHERE CodeCli = Cl.CodeCli);
Dans les deux requêtes ci-dessous, il est possible de faire autrement car nous ne comparons
qu'avec un seul attribut (CodeCli). On aurait pu donc passer par une joitnure ou un IN (ou
NOT IN) avec une sous-requête.

Par contre, si on cherche à comparer plus d'un attribut, il devient difficile (voire impossible
parfois) d'utiliser l'opérateur = ou IN, ou une jointure. On passe donc par EXISTS.
On cherche ici à trouver des clients qui habitent dans la même ville (et donc le même pays)
qu'un employé.

SELECT Societe, Ville, Pays


FROM Client Cl
WHERE EXISTS (SELECT *
FROM Employe
WHERE Ville LIKE Cl.Ville
AND Pays LIKE Cl.Pays);
Exercices
1. Lister les produits n'ayant jamais été commandés
2. Lister les fournisseurs dont au moins un produit a été livré en France
3. Lister les fournisseurs qui ne proposent que des boissons
4. Lister les produits pour lesquels il existe une remise supérieure à 20%
5. Lister les clients qui n'ont pas commandé le produit "Sasquatch Ale"
6. Lister les clients pour lesquels il existe une commande de plus de 1500 €

Exercices complémentaires
1. Lister les clients qui ont commandé du "Camembert Pierrot" (sans aucune jointure)
2. Lister les fournisseurs dont aucun produit n'a été commandé par un client français
3. Lister les clients qui ont commande tous les produits du fournisseur "Exotic Liquids"
4. Quel est le nombre de fournisseurs n’ayant pas de commandes livrées au Canada ?
5. Lister les produits qui n'ont pas été commandés par le client Piccolo und mehr
6. Lister les employés ayant une clientèle sur tous les pays
7. Lister les produits qui ont été placés dans une commande plus de 50 fois
8. Afficher la somme du montant moyen des commandes
9. Lister les commandes avec le nombre de produits à l'intérieur en utilisant une sous-
requête dans le SELECT

Union
L'union est une opération ensembliste qui consiste à prendre les éléments présents dans A et
dans B, A et B étant deux ensembles obtenus grâce à des requêtes, et réuni grâce à l'opérateur
UNION. L'exemple ci-dessous renvoie

Cela permet d'aggréger les données provenant de deux requêtes SQL indépendantes mais
ayant des clauses SELECT compatibles.
Tous les clients qui sont français (premier SELECT) ou dont le contact est le propriétaire de
l'entreprise (deuxième SELECT).

SELECT Societe, Fonction, Pays


FROM Client
WHERE Pays = "France"
UNION
SELECT Societe, Fonction, Pays
FROM Client
WHERE Fonction = "Propriétaire";
Il faut noter que les deux SELECT doivent absolument renvoyer des champs ayant des types
compatibles. Si besoin il est possible de tricher un peu :

SELECT Societe, Fonction, Pays


FROM Client
WHERE Pays = "France"
UNION
SELECT Societe, Fonction, Pays
FROM Client
WHERE Fonction = "Propriétaire";
Ordre et limite
Si l'on souhaite faire un tri du résultat, et/ou se limiter aux premières lignes, les commandes
ORDER BY et LIMIT doivent se placer tout à la fin, dans la dernière requête SQL. Par
exemple, nous trions ici la réquête précédente sur le nom de la société.

SELECT Societe, Fonction, Pays


FROM Client
WHERE Pays = "France"
UNION
SELECT Societe, Fonction, Pays
FROM Client
WHERE Fonction = "Propriétaire"
ORDER BY 1;
Et si l'on veut se limiter aux 10 premières lignes, la requête devient la suivante.

SELECT Societe, Fonction, Pays


FROM Client
WHERE Pays = "France"
UNION
SELECT Societe, Fonction, Pays
FROM Client
WHERE Fonction = "Propriétaire"
ORDER BY 1
LIMIT 10;
Résultats dupliqués
Il arrive que des lignes soient présentes dans les deux requêtes. Ici, cela est le cas pour les
entreprises françaises dont le contact est le propriétaire de l'entreprise. Dans la version
précédente, ces entreprises n'apparaissent qu'une seule fois. Si l'on veut avec les doublons, on
rajoute ALL à la suite du terme UNION. Ce qui revient à la requête suivante (avec un tri sur
le nom pour mieux voir les doublons).

SELECT Societe, Fonction, Pays


FROM Client
WHERE Pays = "France"
UNION ALL
SELECT Societe, Fonction, Pays
FROM Client
WHERE Fonction = "Propriétaire"
ORDER BY 1;
Exercices
En utilisant la clause UNION :

1. Lister les employés (nom et prénom) étant "Représentant(e)" ou étant basé au


"Royaume-Uni"
2. Réécrivez la même requête mais cette fois sans utiliser d'opérateur ensembliste
3. Lister les clients (société et pays) ayant commandé via un employé situé à Londres
("London" pour rappel) ou ayant été livré par "Speedy Express"
4. Listez le numéro et la date des commandes livrées en "Belgique" ou à la société
"Hanari Carnes"

Intersection
L'intersection, avec l'opérateur INTERSECT, entre deux ensembles A et B, obtenus grâce à
deux requêtes, permet de ne récupérer que les lignes présentes dans les deux ensembles. En
reprenant l'exemple précédent, nous récupérons ici tous les clients français dont le contact est
le propriétaire de l'entreprise.

SELECT Societe, Fonction, Pays


FROM Client
WHERE Pays = "France"
INTERSECT
SELECT Societe, Fonction, Pays
FROM Client
WHERE Fonction = "Propriétaire";
Tout comme l'union, les champs des deux SELECT doivent être identiques.

Les règles pour le tri et la limitation des résultats sont aussi les mêmes.

Exercices
Avec l'opérateur INTERSECT :

1. Lister les employés (nom et prénom) étant "Représentant(e)" et étant basé au


"Royaume-Uni"
2. Lister les clients (société et pays) ayant commandé via un employé basé à "Seattle" et
ayant commandé des "Desserts"
3. Lister les produits (référence et nom) commandés le "2014-08-08" et livrés à "Lyon"
Différence
Enfin, la différence entre deux éléments A et B, renvoie les éléments de A qui ne sont pas
présents dans B. Il faut donc faire attention à l'ordre des SELECT, puisque les résultats ne
seront pas identiques entre A - B et B - A. L'opérateur est EXCEPT en SQL classique (et
MINUS dans certains cas - Oracle par exemple). En reprenant le même exemple, la requête
suivante renvoie les clients français dont le contact n'est pas propriétaire.

SELECT Societe, Fonction, Pays


FROM Client
WHERE Pays = "France"
EXCEPT
SELECT Societe, Fonction, Pays
FROM Client
WHERE Fonction = "Propriétaire";
De même que l'union et l'intersection, les champs des deux SELECT doivent être les mêmes.

Et le tri et la limitation doivent se faire aussi à la fin.

Exercices
Avec l'opérateur EXCEPT :

1. Lister les employés (nom et prénom) étant "Représentant(e)" mais n'étant basé au
"Royaume-Uni"
2. Lister les clients (société et pays) ayant commandés via un employé situé à Londres
("London" pour rappel) et n'ayant jamais été livré par "United Package"

Fonctions de fenêtrage
Ces fonctions vont effectuer des calcules basés sur des lignes liées à une ligne particulière.
Contrairement aux fonctions de regroupement, elles n'aggrègent pas les données.

Ces fonctions nécessitent d'utiliser une fenêtre avec la clause PARTITION BY. Cette clause,
dite de partitionnement ou de fenêtrage, divise les lignes en sous-ensembles auxquels la
fonction va s'appliquer.
Clause de partitionnement
La clause de partitionnement permet de grouper les données selon un critère puis d'appliquer
la fonction sur la fenêtre ainsi créée. La requête suivante partitionne les données par date et
numérote les lignes individuellement dans chaque groupe. Sans cette clause de
partitionnement, toutes les lignes du résultat sont considérées comme une seule partition.

Numérotation des lignes


La fonction ROW_NUMBER() va numéroter les lignes en fonction de leur apparition dans le
résultat de la reqûete. La numérotation recommence pour chaque groupe créé par la clause de
partitionnement.

ROW_NUMBER() sans partitionnement


SELECT
ROW_NUMBER () OVER (
ORDER BY Societe
) RowNum,
Societe,
Contact,
Pays
FROM Fournisseur;
ROW_NUMBER() avec partitionnement
SELECT
ROW_NUMBER () OVER (
PARTITION BY Pays
ORDER BY Societe
) RowNum,
Societe,
Contact,
Pays
FROM Fournisseur;
Calcul du rang d'une ligne
La fonction RANK() calcule le rang de chaque ligne parmi l'ensemble des lignes retournées.
SELECT
Nocom, RANK () OVER (
ORDER BY Nocom
)
FROM Commande;
Cette requête liste les commandes triées par montant croissant et affiche le rang
correspondant.

RANK() sans partitionnement


SELECT DateCom, Nocom, SUM(PrixUnit) "Montant total",
RANK() OVER (
ORDER BY SUM(PrixUnit)
)
FROM Commande NATURAL JOIN DetailCommande
GROUP BY Nocom
ORDER BY SUM(PrixUnit);
RANK() avec partitionnement
SELECT DateCom, Nocom, SUM(PrixUnit) "Montant total",
RANK() OVER (
PARTITION BY DateCom
ORDER BY SUM(PrixUnit)
)
FROM Commande NATURAL JOIN DetailCommande
GROUP BY Nocom
ORDER BY DateCom;
Calcul du pourcentage dans le rang
PERCENT_RANK() affiche le % de rang de la ligne considérée. La valeur étant décimale, il
convient de la multiplier par 100 pour avoir la valeur en pourcentage.

PERCENT_RANK() sans partitionnement


SELECT DateCom, Nocom, SUM(PrixUnit) "Montant total",
PERCENT_RANK() OVER (
ORDER BY SUM(PrixUnit)
) * 100 "% cumulé"
FROM Commande NATURAL JOIN DetailCommande
GROUP BY Nocom
ORDER BY SUM(PrixUnit);
Accès à la ligne précédente
LAG() permet d'accéder, sur une ligne, à la valeur de la ligne précédente. Cela permet de
faire des sous-totaux cumulés ou des calculs de variation. Elle prend trois arguments :

expression : colonne à considérer


offset : le décalage vers les lignes précédentes
default : valeur par défaut si aucune ligne précédente n'existe
LAG() sans partitionnement
SELECT
STRFTIME('%Y', DateCom) "Année",
Nocom,
SUM(PrixUnit) "CA Annuel N",
LAG (SUM(PrixUnit), 1, 0) OVER (
ORDER BY DateCom
) "CA Annuel N-1"
FROM Commande NATURAL JOIN DetailCommande
GROUP BY strftime('%Y', DateCom)
ORDER BY DateCom;
LAG() avec clause de partitionnement
Cette variante de la requête précédente partitionne les données par année puis par client.

SELECT
STRFTIME('%Y', DateCom) "Année",
CodeCLi "Client",
SUM(PrixUnit) "CA Annuel N",
LAG (SUM(PrixUnit), 1, 0) OVER (
PARTITION BY STRFTIME('%Y', DateCom)
ORDER BY DateCom
) "CA Annuel N-1"
FROM Commande NATURAL JOIN DetailCommande
GROUP BY strftime('%Y', DateCom), CodeCLi
ORDER BY DateCom;
La colonne produite peut servir dans des expressions de calcul, par exemple pour connaître la
variation de la valeur entre deux groupes de données.

SELECT
STRFTIME('%Y', DateCom) "Année",
CodeCLi "Client",
SUM(PrixUnit) "CA Annuel N",
LAG (SUM(PrixUnit), 1, 0) OVER (
PARTITION BY CodeCLi
ORDER BY DateCom
) "CA Annuel N-1",
SUM(PrixUnit) - LAG (SUM(PrixUnit), 1, 0) OVER (
PARTITION BY STRFTIME('%Y', DateCom)
ORDER BY DateCom
)
"Différence"
FROM Commande NATURAL JOIN DetailCommande
GROUP BY strftime('%Y', DateCom), CodeCLi
ORDER BY DateCom;
Accès à la ligne suivante
La fonction LEAD() à l'inverse de LAG() permet d'accéder à la valeur de la ligne suivante.

Elle prend trois arguments :


expression : colonne à considérer
offset : le décalage vers les lignes précédentes
default : valeur par défaut si aucune ligne précédente n'existe
LEAD() sans partitionnement
SELECT
STRFTIME('%Y', DateCom) "Année",
SUM(PrixUnit) "CA Annuel N",
LEAD (SUM(PrixUnit), 1, 0) OVER (
ORDER BY DateCom
) "CA Annuel N+1"
FROM Commande NATURAL JOIN DetailCommande
GROUP BY strftime('%Y', DateCom)
ORDER BY DateCom;
LEAD() avec partitionnement
Cette variante de la requête précédente partitionne les données par année puis par client.

SELECT
STRFTIME('%Y', DateCom) "Année",
CodeCLi "Client",
SUM(PrixUnit) "CA Mensuel N",
LEAD (SUM(PrixUnit), 1, 0) OVER (
PARTITION BY strftime('%Y', DateCom)
ORDER BY DateCom
) "CA Mensuel N+1"
FROM Commande NATURAL JOIN DetailCommande
GROUP BY strftime('%Y', DateCom), CodeCLi
ORDER BY DateCom;
Accès à une ligne définie
Si LEAD() et LAG() permettent d'avoir la valeur d'avant et la valeur d'après, la fonction
NTH_VALUE() permet de sélectionner la position relative de la ligne dont on veut la valeur.
NTH_VALUE() sans partitionnement
SELECT NoCom, CodeCli,
NTH_VALUE (CodeCli, 2) OVER (
ORDER BY NoCom DESC
)
FROM Commande;
NTH_VALUE() avec partitionnement
SELECT NoCom, CodeCli,
NTH_VALUE (CodeCli, 2) OVER (
PARTITION BY CodeCli
ORDER BY NoCom DESC
)
FROM Commande;
Accès à la première valeur d'une partition
La fonction FIRST_VALUE() permet de récupérer la première valeur d'une partition sur
chaque ligne de la partition. Lorsque la partition est triée, cela permet d'avoir la valeur
minimum de la partition.

SELECT
STRFTIME('%Y-%m', DateCom) "Année",
SUM(PrixUnit) "CA Annuel N",
FIRST_VALUE(SUM(PrixUnit)) OVER (
PARTITION BY STRFTIME('%Y', DateCom)
ORDER BY SUM(PrixUnit)
RANGE BETWEEN UNBOUNDED PRECEDING AND
CURRENT ROW
) "Decembre"
FROM Commande NATURAL JOIN DetailCommande
GROUP BY strftime('%Y-%m', DateCom)
ORDER BY DateCom;
La clause UNBOUNDED PRECEDING permet de scanner toutes les lignes précédant la
ligne courante dans la partition.

Accès à la dernière valeur d'une partition


La fonction LAST_VALUE() est l'inverse de la fonction FISRT_VALUE(). Lorsque la
partition est triée, cela permet d'avoir la valeur maximum de la partition.

SELECT
STRFTIME('%Y-%m', DateCom) "Année",
SUM(PrixUnit) "CA Annuel N",
LAST_VALUE(SUM(PrixUnit)) OVER (
PARTITION BY STRFTIME('%Y', DateCom)
ORDER BY SUM(PrixUnit)
RANGE BETWEEN CURRENT ROW AND
UNBOUNDED FOLLOWING
) "Decembre"
FROM Commande NATURAL JOIN DetailCommande
GROUP BY strftime('%Y-%m', DateCom)
ORDER BY DateCom;
La clause UNBOUNDED FOLLOWING permet de scanner toutes les lignes suivant la ligne
courante dans la partition.

Classification des valeurs de la partition


La fonction NTILE() permet de classer les valeurs en groupes similaires. L'argument de la
fonction définit le nombre de groupes que l'on veut créer.

La requête ci-dessous liste les produits par catégorie et les classe en trois groupes de prix
croissants.

SELECT CodeCateg, PrixUnit,


NTILE (3) OVER (
PARTITION BY CodeCateg
ORDER BY PrixUnit
) "Bucket"
FROM Produit
ORDER BY CodeCateg;
Distribution cumulée
Cette fonction calcule le pourcentage de distribution cumulé des valeurs de la partition. Voici
un calcul pour illustrer ce pourcentage :

Nombre de lignes avec valeur <= N / Nombre de lignes totales de la partition


Comme pour PERCENT_RANK(), la valeur étant décimale, il convient de la multiplier par
100 pour avoir la valeur en pourcentage.

CUME_DIST() sans partitionnement


Distribution cumulée de chaque catégorie.

SELECT DISTINCT
CodeCateg,
CUME_DIST()
OVER (
ORDER BY CodeCateg
) * 100 CumulativeDistribution
FROM Produit;
CUME_DIST() avec partitionnement
Distribution cumulée du fournisseur dans chaque catégorie.

SELECT DISTINCT
NoFour,
CodeCateg,
CUME_DIST()
OVER (
PARTITION BY CodeCateg
ORDER BY NoFour
) * 100 CumulativeDistribution
FROM Produit;
Exercices
Listez les clients en les numérotant en fonction de leur chiffre d'affaire global
Listez pour chaque mois de l'année le nom des produits vendus en affichant leur chiffre
d'affaires cummulé sur le mois set leur rang dans la liste
Listez les clients en affichant leur chiffre d'affaires cumulé
Listez pour chaque année et chaque client, le CA qu'il a réalisé pour l'année considérée en
affichant également le CA de l'année précédente

Vues
Une vue est un nom donné à une requête SQL qui est stocké dans la base de donneés et peut
être utilisé comme une table. Elles permettent de :

décomposer des requêtes SQL en plusieurs parties réutilisables


de cacher la complexité d'une requête
de masquer certaines lignes ou colonnes des tables
Elles ajoutent un niveau d'abstraction entre les tables et les ordres SELECT. Dans SQLite, les
vues sont en lecture uniquement mais certains SGBD supportent les instructions de DML
dans les vues.

Syntaxe
La création d'une vue revient à créer une requête SQL stockée en base avec un nom :

CREATE [TEMP] VIEW [IF NOT EXISTS] view_name[(column-name-list)]


AS
select-statement;
Le mot clé TEMP permet de créer une vue temporaire qui est détruite à la fin de la session
utilisateur.

Création d'une vue


La création d'une vue se fait à l'aide d'une commande simple :

CREATE VIEW IF NOT EXISTS V_ProduitsCategories AS


SELECT RefProd, NomProd, NomCateg
FROM Produit INNER JOIN Categorie
ON Produit.CodeCateg = Categorie.CodeCateg;
Il est possible de personnaliser les noms de colonne de la vue pour qu'ils donnent plus de sens
à celle-ci :

CREATE VIEW IF NOT EXISTS V_ProduitsCategories (Code, Produit, Categorie) AS


SELECT RefProd, NomProd, NomCateg
FROM Produit INNER JOIN Categorie
ON Produit.CodeCateg = Categorie.CodeCateg;
Les colonnes de la vue prennent le nom des alias de colonnes qui sont placés dans
l'instruction SELECT.

Usage d'une vue


La vue peut être utilisée comme n'importe quelle table dans les instructions SQL :

SELECT * FROM V_ProduitsCategories;


Suppression d'une vue
La clause IF EXISTS est optionelle, un message d'erreur alors affiché si on essaye de
supprimer une vue n'existant pas.

DROP VIEW IF EXISTS V_ProduitsCategories;


Exercices
Dans ces exercices, pensez à bien nommer les colonnes avec des Alias dans le Select, cela
permet de faciliter les requêtes faites sur les vues.

Créer une vue nommée "V_QteCommandes" permettant d'afficher le nom de chaque client et
le nombre de commandes qu'il a passé chaque année. Utilisez cette vue pour lister les 10
clients qui ont passé le plus de commandes.
Créer une vue nommée "V_ProduitsFournisseurVendus" permettant d'afficher pour chaque
année et chaque fournisseur, le nom et la quantité de chaque produit vendu. Utilisez cette vue
pour lister les 5 fournisseurs ayant vendu le plus de produits au global.
Créer une vue nommée "V_ChiffreAffaires" permettant d'afficher le nom de chaque client
avec le chiffre d'affaires qu'il a généré chaque année. Utilisez cette vue pour chercher les trois
meilleurs clients.
Créer une vue nommée "V_ProduitsVendus" affichant pour chaque année et pour chaque
mois le montant des commandes (hors remise). Utilisez cette vue pour trouver les trois
produits les plus vendus
Créer une vue nommée "V_CodeClient" qui affiche pour chaque client sa société et un
identifiant basé sur les trois premiers caractères du champ Societe combinés avec les cinq
premiers chiffres d'un nombre aléatoire produit par la fonction RANDOM(). Attention,
RANDOM() renvoie parfois un code négatif et cela n'est pas souhaitable.

Triggers INSERT, UPDATE ou DELETE


Un trigger est un élément de la base de données qui est exécuté automatiquement lorsqu'une
instruction INSERT, UPDATE ou DELETE a lieu sur une table.

Ils permettent, par exemple :

de rajouter des contrôles avant l'exécution d'une transaction


de faire de l'audit des changements apportés à la base de données
d'implémenter des règles métier
Attention : ajouter de la logique dans la base de données est délicat, les développeurs ont
tendance à oublier qu'elle est là.

Syntaxe
La création d'une vue revient à créer une requête SQL stockée en base avec un nom :

CREATE TRIGGER [IF NOT EXISTS] trigger_name


[BEFORE|AFTER] [INSERT|UPDATE|DELETE]
ON table_name
[WHEN condition]
BEGIN
statements;
END;
Les mots clé BEFORE, AFTER et INSTEAD OF permettent de spécifier si le trigger se
déclenche avant, après ou à la place l'instruction.

Le mot clé WHEN permet de définir des conditions de déclenchement : le trigger n'agit que si
la condition est vraie.

Pour le moment, SQLite ne supporte que les triggers de type FOR EACH ROW, les triggers
se déclenchent une fois pour chaque ligne touchée par l'instruction de déclenchement.

Si on combine les possibilités de déclenchement, cela nous donne :

BEFORE INSERT
AFTER INSERT
BEFORE UPDATE
AFTER UPDATE
BEFORE DELETE
AFTER DELETE
INSTEAD OF INSERT
INSTEAD OF DELETE
INSTEAD OF UPDATE
Accès aux données de la transaction
Nous avons accès aux données qui sont insérées, mises à jour ou supprimées à l'aide des mots
clé OLD et NEW sous la forme : OLD.nom_colonne et NEW.nom_colonne.

Toutes les clauses ne sont pas disponibles, cela dépend de l'instruction :

INSERT : NEW est disponible


UPDATE : OLD et NEW sont disponibles
DELETE : OLD est disponible
Création d'un trigger BEFORE INSERT
Exemple : nous souhaitons valider le format de l'adresse email d'un client avant son insertion
en base de données : si l'adresse n'est pas au bon format, l'insertion est bloquée.

Mise à jour de la table Client


Ajout de l'email dans la table client :

ALTER TABLE Client ADD Email VARCHAR(150);


Le trigger permettant cette vérification est le suivant :

CREATE TRIGGER TRG_ValidateEmailClient


BEFORE INSERT ON Client
BEGIN
SELECT
CASE
WHEN NEW.Email NOT LIKE '%_@__%.__%' THEN
RAISE (ABORT,'Email invalide')
END;
END;
Test du trigger
Insertion d'un client avec un mail invalide :

INSERT INTO Client (CodeCli, Societe, Email)


VALUES('CYBER', 'Cyberdyne', 'emailinvalide.com');
Insertion d'un client avec un mail valide :

INSERT INTO Client (CodeCli, Societe, Email)


VALUES('CYBER', 'Cyberdyne', 'email@valide.com');
Vérification du contenu de la table :

SELECT CodeCli, Societe, Email


FROM Client
WHERE CodeCli = 'CYBER';
Création d'un trigger AFTER UPDATE
Exemple : nous souhaitons conserver l'historique des emails utilisés par le client dans une
table dédiée à leur historisation.

Création de la table d'historique


CREATE TABLE HistoriqueMail (
Id INTEGER PRIMARY KEY,
CodeClient VARCHAR(30),
AncienEmail TEXT,
NouvelEmail TEXT,
Operation TEXT,
Moment TEXT
);
Création du trigger
Le trigger permettant cette historisation est le suivant :

CREATE TRIGGER TRG_HistoriqueEmailClient


AFTER UPDATE ON Client
WHEN OLD.Email <> NEW.Email
BEGIN
INSERT INTO HistoriqueMail (
CodeClient,
AncienEmail,
NouvelEmail,
Operation,
Moment
)
VALUES
(
OLD.CodeCli,
OLD.Email,
NEW.Email,
'UPDATE',
DATETIME('NOW')
);
END;
Test du trigger
Un update de la société ne déclenche pas le trigger :

UPDATE Client
SET Societe = 'Cyberdyne Systems'
WHERE CodeCli = 'CYBER';
Vérification :

SELECT * FROM HistoriqueMail;


Un update de l'email déclenche le trigger :

UPDATE Client
SET Email = 'email@cyberdyne.com'
WHERE CodeCli = 'CYBER';
Vérification :

SELECT * FROM HistoriqueMail;


Suppression d'un trigger
La clause IF EXISTS est optionelle, un message d'erreur alors affiché si on essaye de
supprimer un trigger n'existant pas.

DROP TRIGGER IF EXISTS TRG_ValidateEmailClient;


Si une table est supprimée, les triggers dessus sont supprimés également.

Exercices
Exercice 1
Créer un trigger qui vérifie avant insertion ou mise à jour que le numéro de téléphone d'un
client commence bien par un caractère +. Si ce n'est pas le cas, l'insertion ne doit pas avoir
lieu

INSERT INTO Client (CodeCli, Societe, Tel)


VALUES('CYBER', 'Cyberdyne', '3338877665533');
INSERT INTO Client (CodeCli, Societe, Tel)
VALUES('CYBER', 'Cyberdyne', '+3338877665533');
Exercice 2
Créer un trigger qui vérifie avant insertion ou mise à jour que le pays de livraison prévu
correspond bien à un pays qui existe dans la table des clients. Si ce n'est pas le cas, l'insertion
ne doit pas avoir lieu. Voici deux requêtes d'intertion permettant de tester le trigger :

INSERT INTO Commande(NoCom,CodeCli,NoEmp,DateCom,ALivAvant,Port,PaysLiv)


VALUES (99999,'VINET', 5, '2020-03-24', '2020-03-30', 80, 'France');
INSERT INTO Commande(NoCom,CodeCli,NoEmp,DateCom,ALivAvant,Port,PaysLiv)
VALUES (999999,'VINET', 5, '2020-03-24', '2020-03-30', 80, 'Russie');
Exercice 3
Créer un trigger qui audite les changements apportés à la colonne RendCompteA de la table
des employés. Créer au préalable la date d'audit avec les colonnes nécessaires puis un trigger
qui va enregistrer dedans la date du changement, le nom de l'ancien manager et le nom du
nouveau manager.

CREATE TABLE AuditHierarchieEmploye (DateMaj TEXT, NoEmp INT, AncienManager


INT, NouveauManager INT);
Voici les requêtes pour vérifier que l'audit fonctionne :

UPDATE Employe SET RendCompteA=1 WHERE NoEmp=9;


SELECT * FROM AuditHierarchieEmploye;

Triggers INSTEAD OF
Les triggers INSTEAD OF fonctionnent uniquement sur des vues, il n'est pas possible de les
utiliser avec des tables.

Les vues étant en lecture seule, il est impossible d'exécuter dessus des instructions de DML
telles que INSERT, UPDATE ou DELETE.

Lorsqu'une vue possède un trigger INSTEAD OF, il va réagir à l'instruction de DML et nous
permettre d'exécuter de la logique. Par exemple, il va être possible de remplacer un INSERT
sur la vue par un INSERT et un UPDATE sur deux tables sous-jascentes.

Cela permet de contourner la restriction des vues et permettre des modifications sur les
données qui s'y trouvent.

Syntaxe
La création d'une vue revient à créer une requête SQL stockée en base avec un nom :

CREATE TRIGGER [IF NOT EXISTS] schema_ame.trigger_name


INSTEAD OF [DELETE | INSERT | UPDATE OF column_name]
ON table_name
BEGIN
statements;
END;
Création d'un trigger INSTEAD OF
Exemple : nous souhaitons permettre l'insertion d'un produit et d'une catégorie directement
avec une insertion dans la vue.

Création de la vue
La vue sur laquelle le trigger va être placé est la suivante :

CREATE VIEW IF NOT EXISTS V_ProduitsCategories (Code, Libelle, Categorie) AS


SELECT RefProd, NomProd, NomCateg
FROM Produit INNER JOIN Categorie
ON Produit.CodeCateg = Categorie.CodeCateg;
Test de la vue :

SELECT * FROM V_ProduitsCategories;


Création du trigger
Le trigger permettant l'insertion est le suivant :

CREATE TRIGGER TRG_InsertProduitCategorie


INSTEAD OF INSERT ON V_ProduitsCategories
BEGIN
INSERT INTO Categorie(CodeCateg, NomCateg)
VALUES(SUBSTR(UPPER(NEW.Categorie),0,5), NEW.Categorie);

INSERT INTO Produit(RefProd, NomProd, CodeCateg)


VALUES(New.Code, NEW.Libelle, SUBSTR(UPPER(NEW.Categorie),0,5));
END;
Dans ce trigger, nous pourrions utiliser la fonction last_insert_rowid() qui permettrait de
récupérer le rowid de la ligne ajoutée dans la table Categorie pour l'ajouter dans la colonne
CodeCateg de la table des Produits. Ici ce n'est pas nécessaire car le code n'est pas un ID de
clé auto-incrémenté.

Test du trigger
Insertion d'un produit dans la vue :

INSERT INTO V_ProduitsCategories (Code, Libelle, Categorie)


VALUES(9999, 'Charentaises', 'Pantouffles');
Vérification de l'insertion dans la vue :

SELECT * FROM V_ProduitsCategories WHERE Code=9999;


La catégorie a bien été ajoutée :
SELECT * FROM Categorie WHERE CodeCateg='PANT';
Le produit également :

SELECT * FROM Produit WHERE RefProd=9999;


Exercices
Exercice 1
Créer une vue V_CategoriesProduits affichant chaque catégorie (code et nom) avec le
nombre de produits qu'elle contient. Si la catégorie ne contient aucun produit, afficher le
nombre 0. Ajoutez un trigger permettant d'insérer une nouvelle catégorie directement dans
cette vue à l'aide d'une commande INSERT telle que :

INSERT INTO V_CategoriesProduits(Code, Libelle, NbProduits)


VALUES (9,'Hygienne',0);
Tester l'insertion avec :

SELECT * FROM Categorie WHERE CodeCateg=9;

Indexation
Dans une base de données relationelle, une table est une liste de lignes divisées en
colonnes. Chaque ligne possède un identifiant unique appelé ROWID qui permet de la
localiser et d'y accéder. Une table est donc une association (rowid -> ligne).

Un index est une structure complémentaire qui permet d'accélérer les recherches de
données dans les tables. Il est constitué de valeurs associées à des rowid et peut être vu
comme une série d'associations (ligne -> rowid).

Le type d'index le plus courant est le B-Tree (Balanced Tree et non pas Binary Tree),
qui a une forme d'arbre équilibré. L'équilibrage signifie qu'il y a un même nombre de valeurs
à traverser pour trouver les lignes que l'on cherche : les recherches sont faites en temps
d'accès constant.

Les recherches de valeurs exactes (=) ou les inégalités (>, >=, <,<=) peuvent
grandement bénéficier des indexes.
Dans un index, les données sont toujours triées de façon croissante.

Fonctionnement
Un index repose sur une seule table mais peut inclure plusieurs colonnes. Il stocke
réellement des données et donc consomme de la place sur le serveur. Lorsqu'il est placé sur
une colonne, l'index associe chaque valeur de la colonne aux ROWID qui la contiennent.

Création d'un index


Voici un exemple de syntaxe pour créer un index unique :

CREATE UNIQUE INDEX IDX_PageAccueil


ON Fournisseur (PageAccueil);
Trois informations sont à spécifier :

Le nom de l'index
Le nom de la table sur laquelle repose l'index
La liste des colonnes de l'index
Un index UNIQUE va bloquer l'ajout de doublons dans la table.

Usage de l'index par le serveur


Lors d'une recherche de fournisseur par page d'accueil, le serveur va utiliser l'index
pour trouver les lignes correspondantes :

SELECT *
FROM Fournisseur
WHERE PageAccueil='http://www.capcod.eu';
Index multi-colones
Un index multi-colonnes va contenir les données de plusieurs colonnes, il est donc un
peu plus polyvalent. Les deux colonnes étant prises en compte dans le calcul des valeurs
distinctes, cela permet d'améliorer la recherche de colonnes utilisées ensembles dans les
SELECT En contrepartie, il va prendre plus de place sur le disque.
CREATE UNIQUE INDEX IDX_Client
ON Client (Societe, Ville);
Usage de l'index par le serveur
L'index sera utilisé avec des requêtes telles que :

SELECT *
FROM Client
WHERE Societe='CHOPS' AND Ville='Bern';
SELECT *
FROM Client
WHERE Societe='CHOPS';
Par contre l'index ne sera pas utilisé avec :

SELECT *
FROM Client
WHERE Societe='CHOPS' OR Ville='Bern';
SELECT *
FROM Client
WHERE Ville='Bern';
Consultation des indexes sur une table
Avec la commande PRAGMA
PRAGMA index_list('Fournisseur');
Avec un select dans le dictionnaire
SELECT type, name, tbl_name, sql
FROM sqlite_master
WHERE type= 'index';
Suppression d'un index
La commande DROP permet de supprimer un index.

DROP INDEX IDX_PageAccueil;


Exercices
Créer un index unique permettant d'améliorer la recherche d'un employé par son nom
Créer un index unique permettant d'améliorer la recherche d'un fournisseur par son
numéro de téléphone en évitant l'insertion de doublons dans cette colonne
Créer un index permettant de rechercher un client par Nom et par Code postal
simultanément
Réalisez un script composé de commandes permettant de supprimer les indexes créés
pendant les exercices précédents

Transactions
Les serveurs de base de données relationelles gèrent des transactions qui sont :
atomiques, consistantes, isolées, et durables (ACID).

Atomiques :
Consistantes :
Isolées :
Durables :
Certais SGBD comme SQLite fonctionnent en AUTOCOMMIT, cela signifie que
chaque commande INSERT, UPDATE ou DELETE est validée immédiatement après
exécution.

Démarrer une transaction


Pour démarrer une transaction explicitement, il faut utiliser la commande :

BEGIN TRANSACTION;
Une fois ouverte, la transaction reste active jusqu'a ce qu'elle soit explicitement
finalisée par une validation ou une annulation.

Valider une transaction


L'instruction de validation rend tous les changements permanents et visibles par tous
les utilisateurs du serveur :

COMMIT;
Annuler une transaction
L'instruction d'annulation retire tous les changements effectués et remet les tables
dans leur état d'origine.

ROLLBACK;
Exemple de transaction annulée
Démarrage de la transaction :

BEGIN TRANSACTION;
Insertion d'une donnée :

INSERT INTO Produit ('RefProd', 'NomProd', 'NoFour', 'CodeCateg', 'QteParUnit',


'PrixUnit', 'UnitesStock', 'UnitesCom', 'NiveauReap', 'Indisponible')
VALUES ('PROD1','Produit A1', 1, 1, 1, 18, 0, 0, 1, 1);
Vérification de l'insertion :

SELECT * FROM Produit WHERE RefProd='PROD1';


Annulation :

ROLLBACK;
Vérification de l'annulation :

SELECT * FROM Produit WHERE RefProd='PROD1';


Exemple de transaction validée
Démarrage de la transaction :

BEGIN TRANSACTION;
Insertion d'une donnée :

INSERT INTO Produit ('RefProd', 'NomProd', 'NoFour', 'CodeCateg', 'QteParUnit',


'PrixUnit', 'UnitesStock', 'UnitesCom', 'NiveauReap', 'Indisponible')
VALUES ('PROD1','Produit A1', 1, 1, 1, 18, 0, 0, 1, 1);
Vérification de l'insertion :

SELECT * FROM Produit WHERE RefProd='PROD1';


Annulation :

COMMIT;
Conformation de l'insertion :

SELECT * FROM Produit WHERE RefProd='PROD1';


Qu’est-ce que SQL Developer ?
Oracle SQL Developer est l’IDE d’Oracle Database
Une interface utilisateur graphique gratuite, Oracle SQL Developer, permet aux utilisateurs et
aux administrateurs de base de données d’effectuer leurs tâches de base de données en moins
de clics et de frappes. L’objectif principal de l’outil de productivité SQL Developer est
d’aider l’utilisateur final à gagner du temps et à optimiser le retour sur investissement dans la
pile technologique d’Oracle Database.

SQL Developer prend en charge Oracle Database 10g, 11g et 12c et s’exécute sur tout
système d’exploitation prenant en charge Java.

Pour le développeur
SQL Developer fournit des éditeurs puissants pour travailler avec SQL, PL/SQL, les
procédures Java stockées et XML. Exécutez des requêtes, générez des plans d’exécution,
exportez des données au format souhaité (XML, Excel, HTML, PDF, etc.), exécutez,
déboguez, testez et documentez vos programmes de base de données, et bien plus encore avec
SQL Developer.

Pour l’administrateur de bases de données


SQL Develper n’est pas réservé aux développeurs ! Depuis la version 3.0, le panneau
d’administrateur de bases de données (disponible dans le menu Affichage) propose aux
administrateurs de bases de données un ensemble d’interfaces pour leurs tâches les plus
critiques. SQL Developer continue d’ajouter et d’améliorer les fonctionnalités à destination
des administrateurs de bases de données. Aujourd’hui, le panneau d’administrateur de bases
de données de SQL Developer prend en charge les éléments suivants :

Data Pump
Recovery Manager (RMAN)
Audit Oracle
Gestion des utilisateurs et des rôles
Gestion du stockage, y compris la possibilité d’ajouter de l’espace à vos tablespaces
Resource Manager
Fonctionnalités du pack de diagnostic :
Instantanés
Plans de départ
ADDM
ASH
AWR
Pour l’architecte d’application et Data Modeler
Oracle SQL Developer comprend une solution complète de modélisation de données avec
Oracle SQL Developer Data Modeler (SDDM) fonctionnant à l’intérieur de l’application
(également disponible en tant qu’installation autonome et gratuite.) SDDM prend en charge
les éléments suivants :

Modélisation logique, relationnelle, physique, dimensionnelle


Diagrammes de flux de données
Scripts DDL
Importation à partir de dictionnaires de données, de scripts DDL, de référentiels Oracle
Designer et d’ERwin
un référentiel de reporting
Gestion des versions de vos designs via Subversion
Comparaison de modèles avec génération de scripts ALTER
puissant utilitaire de recherche et de reporting
Pour le développeur et l’administrateur d’applications Web
Oracle SQL Developer permet d’administrer Oracle REST Data Services et de créer et de
modifier les services RESTful.

Oracle SQL Developer s’intègre à Oracle APEX, ce qui permet de parcourir les applications
et d’effectuer d’autres activités Application Express. Grâce à Oracle SQL Developer, vous
pouvez parcourir, exporter et importer, supprimer ou déployer des applications. Une sélection
de rapports Application Express est proposée et vous pouvez créer vos propres rapports
personnalisés.

Migrations de bases de données tierces


Autrefois disponible dans Migration Workbench, Oracle SQL Developer est désormais la
principale plateforme de migration pour déplacer vos bases de données tierces vers Oracle
Database. Les utilisateurs peuvent se connecter à Access, SQL Server, Sybase ASE, DB2 ou
Teradata et parcourir un processus piloté par un assistant pour déplacer leurs objets, données
et applications vers Oracle.
Captures d’écran des principales fonctionnalités
Voici une synthèse de diverses fonctionnalités de SQL Developer. Cliquez sur la vignette
pour afficher une image en taille réelle.

1. Créer des connexions


Créer des connexions
Créer et tester des connexions
pour plusieurs bases de données
pour plusieurs schémas
Stocker les connexions utilisées fréquemment
Importer et exporter des connexions
Stocker le mot de passe ou être invité à la connexion
Importer les détails de connexion à partir de tnsnames.ora
Prise en charge de LDAP, Kerberos, authentification externe et utilisateurs proxy
Connexions de groupe dans des dossiers
Option avancée pour identifier l’URL de connexion à partir de Java
Connexions pour TimesTen, MySQL, Microsoft SQL Server, Microsoft Access, Sybase,
Teradata et IBM DB2
Une liste complète des certifications est disponible
SQL Developer prend entièrement en charge les connexions à Oracle 10g et versions
ultérieures.

Les utilisateurs peuvent créer des connexions à des bases de données pour des bases de
données non Oracle MySQL, Microsoft SQL Server, Microsoft Access, Sybase, Teradata et
IBM DB2 pour la navigation dans les objets et les données. Des capacités de feuille de calcul
limitées sont également disponibles pour ces bases de données.

2. Parcourir les objets


Parcourir les objets
Navigateur d’objets basé sur l’arborescence
Prise en charge des objets communs
Tableaux, vues, index
Packages, procédures, fonctions, déclencheurs
Types
Séquences
Vues et journaux matérialisés
Synonymes (publics et privés)
Liens de base de données
Répertoires
Schémas XML
Tables et files d’attente
Java
Tâches
Corbeille (10g et plus)
Autres utilisateurs
Affichage par onglets des détails spécifiques à chaque type d’objet
Les objets sont regroupés par type. Pour chaque type d’objet, un filtre peut être appliqué pour
restreindre l’affichage.

3. Créer des objets


Créer des objets
Créer des boîtes de dialogue pour chaque type d’objet pris en charge
Prise en charge de :
Tables externes
Tables d’index organisées
Tables temporaires
Tables partitionnées (plage, hachage et liste)
Prend en charge les caractères à casse mixte et multi-octets
Lors de la création d’une table, les utilisateurs peuvent spécifier des séquences et des
déclencheurs avant insertion pour remplir une colonne avec des valeurs.

4. Modifier des objets


Modifier des objets
Boîtes de dialogue spécifiques à l’action pour la modification, par exemple,
Table > Renommer
Colonne > Ajouter
Vue > Compiler
Indice > Recréer
Procédure > Déboguer
Déclencheur > Désactiver
Séquence > Supprimer
Vue matérialisée > Changer le parallélisme
Synonyme > Supprimer
Lien de base de données > Tester
Corbeille > Purger
La plupart des objets ont une boîte de dialogue d’édition générale ainsi que des modifications
spécifiques disponibles en appelant un menu contextuel par un clic droit.

5. Interroger et mettre à jour les données


Interroger et mettre à jour les données
Interroger et mettre à jour les données
Parcourir les données de la grille de table
trier
filtrer
mettre en surbrillance
gérer les colonnes (masquer et afficher)
ligne en double
vue d’enregistrement unique
Insérer, mettre à jour et supprimer des données
Prise en charge des CLOB et des BLOB
Colonnes à ajustement automatique
Suivre les modifications des données via le journal des messages
Visual Query Builder
Utilisez le Générateur de requêtes pour créer rapidement des requêtes SQL par glisser-
déposer, pour sélectionner des tables et cliquer avec la souris pour sélectionner des colonnes.

6. Exporter des données et DDL, importer des données


Exporter des données et DDL, importer des données
Exporter des données et DDL, importer des données
Exporter des données vers
XML
csv
SQL Insert
SQL Loader
texte
HTML
XLS
Restreindre les colonnes
Ajouter une clause where
Exporter DDL
sélectionner plusieurs types d’objet
sélectionner plusieurs objets
EXPORT DDL et assistant de données
Importer des données de
XLS, CVS
Importer des données et créer une table
Exportation de données disponible à partir de n’importe quelle grille de données. Cela
comprend Table > Données, résultats de feuille de calcul SQL et sortie de rapport.

7. Copie et comparaison de schémas


Copie et comparaison de schémas
Copie et comparaison de schémas
Copie de schéma
Copier le contenu d’un schéma dans un autre
Déposer, tronquer ou copier des objets
Créer un rapport de résultats
Comparaison de schémas
Comparer et créer une instruction SQL Difference entre 2 schémas
Sélectionner les objets à comparer
Liste des différences et code SQL à exécuter
Exécuter la sortie du script vers la feuille de calcul SQL à exécuter sur la connexion de votre
choix
8. Commandes de processus
Commandes de processus
Commandes de processus
Mise en évidence de la syntaxe
Aperçu du code
Aperçu des alias de table
Exécution d’instructions uniques
Exécution de script
Formatage du code
Historique de commande
Expliquer le plan
Sortie HTP et OWA
Prise en charge des variables de liaison
Glisser-déposer dans votre SQL
Extraits
Tables/Vues matérialisées (transfert de l’instruction select complète)
Fonctions procédures (transfert de la liste complète des paramètres)
Tous les autres objets de Connexions
SQL des rapports
Tous les fichiers sont ouverts dans une feuille de calcul SQL. L’utilisateur sélectionne
simplement la connexion adaptée dans la liste en haut à droite et peut modifier, exécuter et
enregistrer.
9. Modifier le code PL/SQL
Modifier le code PL/SQL
Éditeur de fonctions complètes
Visionneuse d’extraits de code
Formateur de code
Mise en évidence de la syntaxe
Code Insight (saisie semi-automatique)
Pliage de code
Reporting d’erreurs en ligne
Signets de code
Touches rapides personnalisables
Visionneuse d’extraits de code personnalisable
Edition PL/SQL basée sur des fichiers
Tous les fichiers .pks, .pkb et .pls sont ouverts dans un éditeur de code PL/SQL. Les
utilisateurs peuvent modifier, utiliser des extraits, compiler et enregistrer ces fichiers.

10. Exécuter et déboguer PL/SQL


Exécuter et déboguer PL/SQL
Exécuter des procédures, des fonctions et des packages
DBMS_OUTPUT
Valeurs retournées par la fonction
Paramètres OUT
Boîte de dialogue Exécuter PL/SQL
Spécifier la cible d’exécution
Affiche les informations sur les paramètres
Génère un bloc PL/SQL modifiable pour renseigner les paramètres
Débogueur pleinement fonctionnel
Contrôler l’exécution d’un programme (Step into, over, ...)
Inspecter et modifier les variables
Configurer les conditions de point d’arrêt
11. Exécuter et créer des rapports
Exécuter et créer des rapports
Exécuter et créer des rapports
Suite de rapports
Dictionnaire de données
DBA
Surveillance
Rechercher PL/SQL
Sécurité
Application Express
Data Modeler
Migration
Défini par l’utilisateur
Tableau de base
Master-detail
Graphiques
Jauges
HTML
Explorer
Script
La plupart des rapports fournis incluent le clic.. Si un utilisateur clique sur une ligne
résultante, la définition de l’objet dans cette ligne s’affiche. Le code SQL de tout rapport peut
également être extrait dans une feuille de calcul, modifié puis stocké dans votre propre
rapport personnalisé.

12. Tests unitaires PL/SQL


Tests unitaires PL/SQL
Créer un test
Ajouter une suite de tests
Créer une implémentation de test
Accès multi-utilisateurs basé sur le référentiel
Exécuter des rapports de test
Composants de bibliothèque réutilisables
Les objets sont regroupés par type. Pour chaque type d’objet, un filtre peut être appliqué pour
restreindre l’affichage.

13. Visionneuse Data Modeler


Visionneuse Data Modeler
Visionneuse Data Modeler
Data Modeler en lecture seule
Ouvrir et parcourir des modèles de données logiques et relationnels
Glisser-déposer de tables pour créer des diagrammes
Parcourir des domaines, des informations commerciales et des données de modèle de
processus
Accès en lecture seule pour
domaines
informations commerciales
données de modèle de processus
14. Migrer à partir de bases de données tierces
Migrer à partir de bases de données tierces
Créer des connexions à la base de données pour
MySQL
Microsoft SQL Server
Microsoft Access
Sybase
IBM DB2
Teradata
Parcourir les bases de données tierces
Migration rapide
Migration des moindres privilèges
Migration pas à pas
Capturer un modèle tiers
Convertir en modèle Oracle
Générer un script DDL
Exécuter DDL
Migrer les données vers Oracle
Migration avancée
La migration d’objets complexes prend en charge les procédures stockées, les déclencheurs et
les vues
Translation Scratch Editor
Translation Difference Viewer
15. Contrôle de version
Contrôle de version
Intégré avec SQL Developer
Prise en charge de CVS, Subversion, Serena Dimensions et Perforce
Extraire des fichiers
Valider la copie de travail
Comparer et fusionner des fichiers
Importer des fichiers
Historique des versions
16. Examiner l’IDE robuste
Examiner l’IDE robuste
Composants multiples
Connexions
Rapports
Feuille de calcul SQL
Extraits (glisser-déposer dans le code)
Arrêt sur vue
Fenêtres ancrables
Écran divisé
Préférences
Validation automatique avec la feuille de calcul SQL
Définir les touches accélératrices
Afficher les numéros de ligne
Taille de l’onglet
Vérifier la disponibilité de mises à jour
Pilotes tiers
pour ajouter des extensions CVS, Serena Dimensions et Perforce
Extensible
SQL Developer exploite l’IDE JDeveloper. Cela donne aux développeurs la possibilité
d’écrire des extensions.
Présentation du PL/SQL
Description
Langage de programmation d'Oracle.

Permet une manipulation procédurale de requêtes SQL.

Avantages
Intégration

Il est possible d'écrire des fonctions complexes de manipulation de données sans recourir à un
langage externe.

Amélioration des performances

Le code PL/SQL est très proche du moteur Oracle. De plus pour le code stocké, les requêtes
qu'il manipule sont pré-compilées, et donc son exécution est optimisée.

Structure d'un bloc PL/SQL


Syntaxe
CTRL+C pour copier, CTRL+V pour coller
1 DECLARE ou IS
2 Variables, curseurs, etc.
3 BEGIN
4 Instructions SQL et PL/SQL
5 EXCEPTION
6 Gestion d'erreur.
7 END;
8/
Attention/
Un bloc PL/SQL est terminé par un ; comme une instruction SQL.
Par ailleurs, dans les environnements d'exécution Oracle (comme SQL Developer), il est
nécessaire de séparer les blocs par un "/" (sur une nouvelle ligne).

Une bonne habitude est donc de terminer les blocs PL/SQL par des "/".

Complément
http://stackoverflow.com/questions/3024418/two-plsql-statements-with-begin-and-end-run-
fine-seperately-but-not-together

Types de variables
Scalaires

VARCHAR, DATE, CHAR, LONG, BOOLEAN, INTEGER

RECORD

Déclaration d'un type RECORD : TYPE nom_type IS RECORD (déclaration de propriétés);

Déclaration d'une variable enregistrement de ce type : nom_variable nom_type;

Curseurs

Permettent de manipuler des résultats de requête.

SyntaxeDéclaration des variables


CTRL+C pour copier, CTRL+V pour coller
1 identifiant [CONSTANT] type [NOT NULL] [:= valeur];
ExempleDéclaration des variables
CTRL+C pour copier, CTRL+V pour coller
1 DECLARE
2 v_deptno NUMBER(2) NOT NULL := 10;
3 v_hiredate DATE;
4 v_location VARCHAR2(13) := 'Atlanta';
5 c_comm CONSTANT NUMBER := 1400;
6 BEGIN
7
...
SyntaxeAffectation des variables
CTRL+C pour copier, CTRL+V pour coller
1 variable := valeur | variable
ExempleAffectation des variables
CTRL+C pour copier, CTRL+V pour coller
1 x:=10;
2 x:=y;
ConseilRéférence à un type de colonne existant
On peut faire référence au type d'une colonne d'une table par la syntaxe suivante en
remplacement du type de données : nom_table.nom_colonne%TYPE.

ExempleRéférence à un type de colonne existant


CTRL+C pour copier, CTRL+V pour coller

Structures de contrôle
SyntaxeStructure conditionnelle
CTRL+C pour copier, CTRL+V pour coller
1 IF THEN
2 instructions
3 ELSIF THEN
4 instructions
5 ELSE
6 instructions
7 END IF;
SyntaxeBoucle FOR
CTRL+C pour copier, CTRL+V pour coller
1 FOR compteur IN [REVERSE] inf...sup LOOP
2 instructions
3 END LOOP;
SyntaxeBoucle WHILE
CTRL+C pour copier, CTRL+V pour coller
1 WHILE condition LOOP
2 instructions
3 END LOOP;
SyntaxeBoucle REPEAT
CTRL+C pour copier, CTRL+V pour coller
1 LOOP
2 instructions
3 EXIT [WHEN condition];
4 END;

Affichage à l'écran
Syntaxe
CTRL+C pour copier, CTRL+V pour coller
1
SET SERVEROUTPUT ON
CTRL+C pour copier, CTRL+V pour coller
1
BEGIN
2
 DBMS_OUTPUT.PUT_LINE ('Hello World');
3
END;

Blocs PL/SQL : Procédure, fonction, bloc anonyme


SyntaxeProcédure
CTRL+C pour copier, CTRL+V pour coller
1
CREATE OR REPLACE PROCEDURE nom_proc
2
IS
3
 ...
4
BEGIN
5
 ...
6
[EXCEPTION]
7
 ...
8
END ;
ExempleProcédure
CTRL+C pour copier, CTRL+V pour coller
1
CREATE OR REPLACE PROCEDURE pHello (who VARCHAR2)
2
IS
3
BEGIN
4
DBMS_OUTPUT.PUT_LINE('Hello ' || who);
5
END;
6
/
SyntaxeFonction
CTRL+C pour copier, CTRL+V pour coller
1
CREATE OR REPLACE FUNCTION nom_func
2
 RETURN type_retourné
3
IS
4
 ...
5
BEGIN
6
 ...
7
 RETURN valeur;
8
[EXCEPTION]
9
 ...
10
END ;
ExempleFonction
CTRL+C pour copier, CTRL+V pour coller
1
CREATE OR REPLACE FUNCTION fDateDuJour RETURN date
2
IS
3
vDate date;
4
BEGIN
5
SELECT SYSDATE INTO vDate FROM DUAL;
6
RETURN vDate;
7
END;
8
/
Attention
Le type de retourné par une fonction ne doit pas spécifier de taille :

RETURN varchar

et non RETURN varchar(10)

http://docs.oracle.com/cd/B13789_01/server.101/b10759/statements_5009.htm

SyntaxeAnonyme
CTRL+C pour copier, CTRL+V pour coller
1
[DECLARE]
2
 ...
3
BEGIN
4
 ...
5 [EXCEPTION]
6...
7 END ;
ExempleScript anonyme
CTRL+C pour copier, CTRL+V pour coller
1 SET SERVEROUTPUT ON;
2 BEGIN
3 pHello('World');
4 DBMS_OUTPUT.PUT_LINE(fDateDuJour);
5 END;
6/

Curseurs PL/SQL
DéfinitionCurseur
Un curseur est un pointeur sur un résultat de requête.

SyntaxeSyntaxe OPEN/FETCH
CTRL+C pour copier, CTRL+V pour coller
1
DECLARE
2
 CURSOR c_moncurseur IS
3
   SELECT prop1, prop2, ... FROM relations;
4
 vProp1 relation1.prop1%TYPE;
5
 vProp2 ...
6
BEGIN
7
 OPEN c_moncurseur;
8
 LOOP
9
   FETCH c_moncurseur INTO vProp1, vProp2, ...;
10
   EXIT WHEN c_moncurseur%NOTFOUND;
11
   instructions
12
 END LOOP;
13
END;
RappelBD "Gestion des intervenants" : Schéma relationnel
CTRL+C pour copier, CTRL+V pour coller
1
tIntervenant (#pknom:varchar, prenom:varchar, poste:integer)
2
tCours (#pkannee:2000..2100, #pknum:integer, titre:varchar, type:C|TD|TP,
fkintervenant=>tIntervenant, debut:date)
ExempleBD "Gestion des intervenants" : Traitement de données et curseur
CTRL+C pour copier, CTRL+V pour coller
1
CREATE OR REPLACE PROCEDURE pAfficheIntervenants
2
IS
3
CURSOR cIntervenants IS
4
SELECT pknom, prenom FROM tIntervenant;
5
vNom tIntervenant.pknom%TYPE;
6
vPrenom tIntervenant.prenom%TYPE;
7
BEGIN
8
DBMS_OUTPUT.PUT_LINE('** Liste des intervenants **');
9
OPEN cIntervenants;
10
LOOP
11
FETCH cIntervenants INTO vNom, vPrenom;
12
EXIT WHEN cIntervenants%NOTFOUND;
13
DBMS_OUTPUT.PUT_LINE('-' || INITCAP(TRIM(vPrenom)) || ' ' ||
INITCAP(TRIM(vNom)));
14
END LOOP;
15
END;
16
/
17
18
SET SERVEROUTPUT ON;
19
BEGIN
20
pAfficheIntervenants1;
21
END;
22
/
CTRL+C pour copier, CTRL+V pour coller
1
** Liste des intervenants **
2
-Stéphane Crozat
3
-Antoine Jouglet
ComplémentSyntaxe FOR/IN
CTRL+C pour copier, CTRL+V pour coller
1
DECLARE
2
 CURSOR c_moncurseur IS
3
  SELECT prop1, prop2, ... FROM relations;
4
BEGIN
5
 FOR c_montuple IN c_moncurseur LOOP
6
  instructions
7
 END LOOP;
8
END;
CTRL+C pour copier, CTRL+V pour coller
1
CREATE OR REPLACE PROCEDURE pAfficheIntervenants2
2
IS
3
CURSOR cIntervenants IS
4
SELECT pknom, prenom FROM tIntervenant;
5
BEGIN
6
DBMS_OUTPUT.PUT_LINE('** Liste des intervenants **');
7
FOR i IN cIntervenants LOOP
8
DBMS_OUTPUT.PUT_LINE('-' || INITCAP(TRIM(i.prenom)) || ' ' ||
INITCAP(TRIM(i.pknom)));
9
END LOOP;
10
END;
11
/
Test simulé PL / SQL I

Q 1 - Lequel des énoncés suivants n'est pas vrai pour le langage PL / SQL?

A - Il prend en charge les instructions Embedded SQL.

B - Il a toutes les caractéristiques d'un langage de programmation structuré moderne.

C - Ce n'est pas un langage structuré par blocs.

D - Les applications développées avec PL / SQL ne sont pas portables.

Q 2 - Lequel des énoncés suivants n'est pas vrai pour le langage PL / SQL?

A - La syntaxe générale de PL / SQL est basée sur celle du langage de programmation ADA et Pascal.

B - Outre Oracle, PL / SQL est disponible dans la base de données en mémoire TimesTen et IBM DB2.

C - PL / SQL est étroitement intégré à SQL.

D - Il n'offre pas de vérification des erreurs.

Q 3 - Laquelle des affirmations suivantes est vraie pour le langage PL / SQL ?

A - *PL / SQL permet d'accéder à des packages SQL prédéfinis.

B - PL / SQL prend en charge la programmation orientée objet.

C -PL / SQL prend en charge le développement d'applications Web et de pages serveur.

D - Tout ce qui précède.

Q 4 - Lequel des énoncés suivants n'est pas vrai à propos de la section de déclaration d'un bloc PL /
SQL?

A - Cette section commence par le mot clé DECLARE.

B - C'est une section obligatoire.

C - Il définit toutes les variables, curseurs, sous-programmes et autres éléments à utiliser dans le
programme.

D - Aucune de ces réponses.

Q 5 - Laquelle des affirmations suivantes est vraie à propos de la section d'exécution d'un bloc PL /
SQL?
A - Il est placé entre les mots-clés BEGIN et END.

B - C'est une section obligatoire.

C - Il se compose des instructions PL / SQL exécutables.

D - Tout ce qui précède.

Q 6 - Lequel des énoncés suivants n'est pas vrai à propos de la section d'exécution d'un bloc PL /
SQL?

A - Il doit avoir plus d'une ligne de code exécutable.

B - Il peut avoir juste une commande NULL pour indiquer que rien ne doit être exécuté.

C - Les instructions doivent toujours se terminer par un point-virgule.

D - La section peut contenir des commandes SQL, des commandes de contrôle logique, des
commandes d'affectation ainsi que d'autres commandes.

Q 7 - Lequel des énoncés suivants n'est pas vrai à propos de la section de gestion des exceptions d'un
bloc PL / SQL?

A - Cette section commence par le mot clé EXCEPTION.

B - C'est une section obligatoire.

C - Il contient des exceptions qui gèrent les erreurs dans le programme.

D - Aucune de ces réponses.

Q 8 - Laquelle des affirmations suivantes est vraie pour les commentaires en PL / SQL?

A - Les commentaires sont des déclarations explicatives.

B - PL / SQL prend en charge les commentaires sur une seule ligne et sur plusieurs lignes.

C - Les commentaires sur une seule ligne PL / SQL commencent par le délimiteuA - (double tiret) et
les commentaires sur plusieurs lignes sont encadrés par / * et * /.

D - Tout ce qui précède.

Q 9 - Lequel des éléments suivants n'est pas une unité PL / SQL?


A - Tableau

B - Type

C - Déclencheur

D - Paquet

Q 10 - Laquelle des affirmations suivantes est vraie pour les types de données en PL / SQL?

A - Les types de données Grand objet ou LOB sont des pointeurs vers des objets volumineux qui sont
stockés séparément des autres éléments de données, tels que le texte, les images graphiques, les
clips vidéo et les formes d'onde sonore.

B - Les types de données composites ont des éléments de données qui ont des composants internes
accessibles individuellement. Par exemple, les collections et les enregistrements.

C - Les références sont des pointeurs vers d'autres éléments de données.

D - Tout ce qui précède.

Q 11 - Lequel des énoncés suivants est vrai pour les types de données scalaires dans PL / SQL?

A - Ils contiennent des valeurs uniques sans composants internes.

B - Des exemples de types de données scalaires sont NUMBER, DATE ou BOOLEAN.

C - PL / SQL fournit des sous-types de types de données.

D - Tout est vrai.

Q 12 - Laquelle des affirmations suivantes est vraie pour les types et sous-types de données de
caractères en PL / SQL?

A - LONG est une chaîne de caractères de longueur variable d'une taille maximale de 32 760 octets.

B - ROWID est un identifiant de colonne physique, l'adresse d'une colonne dans une table ordinaire.

C - CHAR est une chaîne de caractères de longueur variable d'une taille maximale de 32 767 octets.

D - NCHAR est une chaîne de caractères nationaux de longueur variable d'une taille maximale de 32
767 octets.
Q 13 - Lequel des énoncés suivants n'est pas vrai pour les types de données d'objets volumineux et
en PL / SQL?

A - BFILE est utilisé pour stocker des objets binaires volumineux dans des fichiers du système
d'exploitation en dehors de la base de données.

B - BLOB est utilisé pour stocker les données de caractères dans la base de données.

C - CLOB est utilisé pour stocker de gros blocs de données de caractères dans la base de données.

D - NCLOB est utilisé pour stocker de gros blocs de données NCHAR dans la base de données.

Q 14 - Quelle valeur sera attribuée à la variable déclarée comme ci-dessous -

compteur binary_integer;

A-0

B-1

C - NULL

D - Aucune de ces réponses.

Q 15 - Considérez le code suivant -

DECLARE

-- Global variables

num number := 95;

BEGIN

dbms_output.put_line('num: ' || num1);

DECLARE

-- Local variables

num number := 195;

BEGIN

dbms_output.put_line('num: ' || num1);

END;

END;

Que se passera-t-il lorsque le code sera exécuté?

A - Il ne s'exécute pas, il a une erreur de syntaxe


B - Il imprimera

num: 95

num: 195

C - Il imprimera

num: 95

num: 95

D - Il imprimera

num: 195

num: 195

Q 16 - Quel est le problème dans le code suivant?

DECLARE

c_id := 1;

c_name customers.name%type;

c_addr customers.address%type;

BEGIN

SELECT name, address INTO c_name, c_addr

FROM customers

WHERE id = c_id;

END;

A - Vous ne pouvez pas utiliser l'instruction SELECT INTO de SQL pour affecter des valeurs aux
variables PL / SQL.

B - L'instruction SELECT INTO est ici erronée. Ce devrait être: SELECT c_name, c_address INTO name,
addr

C - L'instruction WHERE est erronée. Il doit être: WHERE id: = c_id;

D - La variable c_id doit être déclarée comme une variable compatible avec le type comme -

c_id customers.id% type: = 1;


Q 17 - Lequel des énoncés suivants n'est pas vrai pour les constantes et littéraux PL / SQL?

A - Une constante contient une valeur qui, une fois déclarée, ne change pas dans le programme.

B - La déclaration CONSTANT ne peut pas imposer la contrainte NOT NULL.

C - Une constante est déclarée à l'aide du mot clé CONSTANT.

D - Une déclaration CONSTANT nécessite une valeur initiale.

Q 18 - Quelle sera la sortie de l'extrait de code suivant?

DECLARE

a number (2) := 21;

b number (2) := 10;

BEGIN

IF ( a <= b ) THEN

dbms_output.put_line(a);

END IF;

IF ( b >= a ) THEN

dbms_output.put_line(a);

END IF;

IF ( a <> b ) THEN

dbms_output.put_line(b);

END IF;

END;

A-2

B - 21

C - 10

D - 21, 10
Q 19 - Que serait imprimé lorsque le code suivant est exécuté?

DECLARE

x NUMBER;

BEGIN

x := 5;

x := 10;

dbms_output.put_line(-x);

dbms_output.put_line(+x);

x := -10;

dbms_output.put_line(-x);

dbms_output.put_line(+x);

END;

A - -10

dix

dix

-dix

B - 10

-dix

dix

-dix

C - -10

+10

+10

-dix

D - 10

-dix

-dix

dix
Q 20 - Pour obtenir le résultat de sortie du serveur et l'afficher à l'écran, vous devez écrire -

A - activer la sortie du serveur

B - activer la sortie du serveur

C - activer dbmsoutput

D - activer la sortie dbms

Q 21 - Lequel des énoncés suivants n'est pas vrai à propos des structures de prise de décision PL /
SQL?

A - L'instruction IF associe une condition à une séquence d'instructions entourées des mots-clés
THEN et END IF.

B - L'instruction IF ajoute également le mot-clé ELSE suivi d'une autre séquence d'instructions.

C - L'instruction IF-THEN-ELSIF vous permet de choisir entre plusieurs alternatives.

D - PL / SQL n'a pas d'instruction CASE.

Q 22 - Lequel des énoncés suivants est vrai pour l'extrait de code suivant?

DECLARE

a number(3) := 100;

BEGIN

IF (a = 50 ) THEN

dbms_output.put_line('Value of a is 10' );

ELSEIF ( a = 75 ) THEN

dbms_output.put_line('Value of a is 20' );

ELSE

dbms_output.put_line('None of the values is matching');

END IF;

dbms_output.put_line('Exact value of a is: '|| a );

END;

A - Il y a une erreur de syntaxe.


B - Il affichera «Aucune des valeurs ne correspond».

C - Il imprimera

Aucune des valeurs ne correspond

La valeur exacte de a est: 100

D - Aucune de ces réponses.

Explication

l'instruction ELSIF est mal écrite comme ELSEIF

Q 23 - Lequel des énoncés suivants est vrai à propos de l'extrait de code suivant?

DECLARE

a number(3) := 100;

BEGIN

IF (a = 50 ) THEN

dbms_output.put_line('Value of a is 10' );

ELSIF ( a = 75 )

dbms_output.put_line('Value of a is 20' );

ELSE

dbms_output.put_line('None of the values is matching');

END IF;

dbms_output.put_line('Exact value of a is: '|| a );

END;

A - Il y a une erreur de syntaxe.

B - Il affichera «Aucune des valeurs ne correspond».

C - Il imprimera

Aucune des valeurs ne correspond

La valeur exacte de a est: 100

D - Aucune de ces réponses.

Explication

il a le mot clé THEN manquant dans l'instruction ELSIF


Q 24 - Lequel des énoncés suivants est vrai à propos de la syntaxe de l'instruction PL / SQL CASE
suivante?

CASE selector

WHEN 'value1' THEN S1;

WHEN 'value2' THEN S2;

WHEN 'value3' THEN S3;

...

ELSE Sn; -- default case

END CASE;

A - Il est mal écrit.

B - C'est parfaitement écrit.

C - C'est vous pouvez spécifier le littéral NULL pour toutes les expressions S et la valeur par défaut S n
.

D - Toutes les expressions telles que le sélecteur, la valeur et les valeurs renvoyées n'ont pas besoin
d'être du même type de données.

Q 25 - Quelle est la sortie du code suivant?

DECLARE

grade char(1) := 'B';

BEGIN

case

when grade = 'A' then dbms_output.put_line('Excellent');

when grade = 'B' then dbms_output.put_line('Very good');

when grade = 'C' then dbms_output.put_line('Well done');

when grade = 'D' then dbms_output.put_line('You passed');

when grade = 'F' then dbms_output.put_line('Better try again');

else dbms_output.put_line('No such grade');

end case;

END;

A - Il y a une erreur de syntaxe, il n'y aura donc aucune sortie.


B-B

C - Très bien

D - Pas de telle note


Qcm PL/SQL
BISSO DOUMINGOU Johanne IR4 – Groupe 1

Question 1
Lors d'une transaction, j’exécute la commande:

DELETE * from Client; where id=1;

Réponse du serveur: 100 000 lignes détruites.

Que faire?

a. J’interromps la connexion à la base

b . J’effectue un commit

c . J’éteins tout et je pars en courant

d . J’effectue un rollback
Question 2
Qu’est-ce qu’une vue ?

Veuillez choisir une réponse :

a. Une vue est une table permettant de gérer des privilèges SQL

b . Une vue est une série d’instructions SQL qui permet d’automatiser des actions

c . Une vue est un bloc qui permet de regrouper plusieurs instructions

d . Une vue est une table logique basée sur une ou plusieurs tables

Question 3

Voici une vue sur les logements corses

create view LogementCorse as

select *

from Logement

where lieu='Corse'

Est-il possible à votre avis d’effectuer l’insertion suivante dans la vue ?

insert into LogementCorse (code, nom, capacité, type, lieu)

values ('ta', 'Tabriz', 34, 'Hôtel', 'Bretagne') ;

a. Oui, car aucune restriction ne s’applique

b . Oui, car cette ligne va être stocké dans la vue, pas dans la table Logement, et il sera donc
possible de le retrouver en interrogeant la vue .

c . Non, pour des raisons de cohérence : en interrogeant la vue, on ne peut pas retrouver un
logement en Bretagne !
Question 4
Dans quel cas peut-on se passer des mots-clés BEGIN … END pour entourer le corps d’une procédure
stockée ?

Veuillez choisir une réponse :

a. Si le corps de la procédure n’est composé que d’une requête

b . Si le corps de la procédure comporte plusieurs requêtes.

c . Toujours, ils sont facultatifs et permettent juste de mieux organiser le code de la procédure .
d . Jamais, ces mots-clés sont obligatoires.

Question 5
Les propriétés ACID des transactions sont

a. Une situation idéale qui est souvent mise en échec en pratique

b . Garanties par le SGBD

c . Programmées par le développeur d’application

Question 6

Qu’est-ce qu’une procédure stockée?

a. Une procédure dont le code est sauvegardé dans la base de données avant d’être chargée et
exécutée dans une application cliente

b . Une procédure stockée et exécutée par le serveur de données

c . Une procédure dont le code est placé sur le disque

Question 7

Combien de temps peut-on utiliser un curseur?

a. jusqu’à ce qu’il soit fermé

b . Tant que personne ne modifie les lignes sélectionnées

c . Le résultat d’un curseur est figé et toujours disponible


Question 8

Qu’est-ce qu’une transaction?

a. Une séquence d’opérations, lecture ou écriture, terminée par commit ou rollback

b . La séquence des opérations effectuées par un programme

c . Une opération suivie d’une opération de lecture

d . Une opération d’écriture dans la base


Question 9

Dans une transaction, À quel moment doit-on effectuer un commit

a. À intervalles périodiques (par exemple toutes les 5 mns)

b . Dès que la base est arrivée à un état cohérent

c . Après chaque mise à jour


Question 10

Quand se déclenche un trigger?

a. À la demande d’un utilisateur

b . Périodiquement, en fonction d’une configuration

c . En fonction d’événements affectant la base de données

Question 11

Parmi les affirmations suivantes, qui concernent les index, laquelle est fausse ?

a. Il peut y avoir plusieurs index sur une même table

b . Une colonne peut être utilisée par plusieurs index

c . Lorsque l’on met une clé sur une colonne (primaire ou étrangère), automatiquement, un index
existe sur cette colonne

d . Un index permet d'accélérer les requêtes d’insertion


Question 12

Quelles restrictions s’appliquent à l’interrogation d’une vue?

a. Aucune

b . On ne peut pas créer de vue sur une autre vue

c . On ne peut pas effectuer de jointure entre une vue et une table


Question 13

Parmi les actions suivantes, laquelle ne peut pas être effectuée par un trigger?

a. Effectuer une insertion ou mise à jour dans une table autre que celle affectée par l’événement
déclencheur

b . Comparer une ligne avant et après une mise à jour

c . Annuler l’effet d’une mise à jour ou d’une destruction

d . Corriger une erreur de syntaxe dans la requête


Question 14

Comment définiriez-vous la notion de vue?

a. C’est un moyen de créer une deuxième base dérivée de la première par exécution de requêtes
SQL

b . C’est un moyen de pré-définir des requêtes

c . C’est un moyen de pré-calculer un résultat

Question 15

Un trigger AFTER INSERT est attaché à une table . Si l'on insère cinq lignes, les instructions du trigger
seront exécutées combien de fois ?

Veuillez choisir une réponse :

a. les instructions du trigger seront exécutées une fois, à la fin du bloc d’instruction d’insertion

b . les instructions du trigger seront exécutées aucune fois car AFTER INSERT est une instruction
qui n’existe pas

c . les instructions du trigger seront exécutées cinq fois, chaque itération permettant de traiter
les données d'une des lignes insérées

d . les instructions du trigger seront exécutées dix fois car chaque table peut avoir au maximum 2
triggers par événement déclencheur

Question 16

Quelle affirmation sur les procédures stockées vous semble exacte ?

a. Les procédures stockées étendent et remplacent SQL car elles offrent toute la puissance d’un
langage de programmation

b . Les procédures stockées complètent SQL avec les structures d’un langage de programmation
(boucles, tests)

c . Les procédures stockées sont utilisées par SQL pour évaluer les requêtes

Question 17

Parmi les types d’éléments suivants, lequel n’est pas conservé si l’on se déconnecte, puis se
reconnecte ?

Veuillez choisir une réponse :


a. Une procédure stockée

b . Un trigger

c . Une vue

d . Aucun des trois


Question 18

Quelle instruction permet d’appeler une procédure stockée ?

Veuillez choisir une réponse :

a. BEGIN nom_procedure([paramètre1[,paramètre2,…]])

b . nom_procedure([paramètre1[,paramètre2,…]])

c . SELECT nom_procedure([paramètre1[,paramètre2,…]])

d . CALL nom_procedure([paramètre1[,paramètre2,…]])
Question 19

Quelle instruction permet de donner des privilèges systèmes ?

Veuillez choisir une réponse :

a. EXECUTE privilege

b . GRANT privilege

c . ALTER privilege

d . CREATE privilege
Question 20

Quelle commande permet de valider directement une requête ?

Veuillez choisir une réponse :

a. commit=1;

b . SET autocommit=0;

c . SET autocommit=1;

d . START TRANSACTION;
Question 21

Quel type de requête ne peut-on pas faire sur une vue ?

Veuillez choisir une réponse :

a. Une requête SELECT qui contient une sous-requête

b . Une requête SELECT avec jointure

c . Ces trois types de requêtes sont possibles.

d . Une requête SELECT qui contient une fonction d’agrégation et un GROUP BY

Question 22

Lors d'une transaction, dire que deux programmes sont concurrents, c’est dire que :

a. Ils peuvent échanger des messages

b . Ils s’exécutent sur la même machine

c . Ils communiquent avec le même serveur de données


Question 23

Comment créer une procédure stockée ?

Veuillez choisir une réponse :

a. BEGIN PROCEDURE nom_procedure([paramètre1[,paramètre2,…]])

b . CREATE PROCEDURE nom_procedure([paramètre1[,paramètre2,…]])

c . START PROCEDURE nom_procedure([paramètre1[,paramètre2,…]])

d . PROCEDURE nom_procedure([paramètre1[,paramètre2,…]])
Question 24

Que signifie « Atomicité »

a. Le serveur sait annuler ou valider solidairement toutes les opérations d’une même transaction

b . Le serveur effectue et valide les opérations de lecture ou d’écriture une par une

c . Le serveur effectue toutes les opérations en même temps


Question 25

Dans une vue, quelle est l’utilité de la clause WITH CHECK OPTION

a. On vérifie que la syntaxe de la requête SQL définissant la vue est correcte

b . On vérifie que tout ce qui est inséré dans la vue peut être lu dans la vue

c . On vérifie les droits d’accès de l’utilisateur avant toute mise à jour


Question 26

Comment ajouter un index appelé Ind2 à la colonne Lib3 ?

a.

b.

c .

d.
Question 27

Qu’est-ce qu’un curseur ?

a. Un tableau contenant le résultat d’une requête

b . Une variable indiquant combien de lignes on souhaite récupérer après exécution d’une
requête

c . Un mécanisme permettant de parcourir le résultat d’une requête

Question 28

Comment clôturer une transaction ?

Veuillez choisir une réponse :

a. Avec l’instruction COMMIT et ROLLBACK

b . Avec l’instruction ROLLBACK seulement

c . Avec l’instruction COMMIT seulement

d . Soit avec l’instruction COMMIT, soit avec l’instruction ROLLBACK

Vous aimerez peut-être aussi