Académique Documents
Professionnel Documents
Culture Documents
É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
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 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.
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
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
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 packages
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.
Déclaration et initialisation
variable_name [CONSTANT] datatype [NOT NULL] [:=|DEFAULT expr];
Affectation
variable := expression;
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.
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
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
À toute requête SQL, Oracle alloue une zone mémoire SQL privée
appelée curseur
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 :
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
La syntaxe de la fermeture :
CLOSE cursor_name;
Les curseurs explicites
Exemple 1
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
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
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
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
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
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
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
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.
Je suis un peu rouillé sur mon jargon du curseur en PL / SQL. Quelqu'un sait-il cela?
CURSOR my_cursor IS
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
BEGIN
OPEN cur;
CLOSE cur;
END;
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
C1_REC C1%ROWTYPE;
BEGIN
FOR C1_REC IN C1
LOOP
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.
BEGIN
DECLARE
CURSOR C1
IS
C1_REC C1%ROWTYPE;
BEGIN
OPEN c1;
LOOP
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.
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
end loop;
close emp_cursor;
end;
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.
SQL> DECLARE
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
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: ' ||
21
22 l_start := DBMS_UTILITY.get_time;
23
25 SELECT dummy
26 INTO l_dummy
27 FROM dual;
28 END LOOP;
29
30 DBMS_OUTPUT.put_line('Implicit: ' ||
32 END;
33 /
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.
Curseur Implicite
Explicite Curseur
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.
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:
le curseur est créé pour L’instruction ‘SELECT’ qui est donnée dans la déclaration du curseur.
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
%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
Explication du Code:
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 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 » 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:
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
Explication du Code:
ligne de Code 4: Construction de la boucle ‘ FOR ‘ pour le curseur avec la variable de boucle
lv_emp_name.
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)
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')
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-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)
create or replace
trigger deconnexion before LOGOFF on DATABASE begin
insert into trace values (user,sysdate);
end;
Les déclencheurs Oracle (Triggers)
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
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
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
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 -
DATE
TIMESTAMP
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.
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
TIMEZONE_HOUR
TIMEZONE_MINUTE
00 à 59
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.
C'est une autre variante de TIMESTAMP qui inclut un décalage de fuseau horaire dans sa valeur.
ADD_MONTHS(x, y);
Ajoute y mois à x.
LAST_DAY(x);
MONTHS_BETWEEN(x, y);
NEXT_DAY(x, day);
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();
TRUNC(x [, unit]);
Tronque x.
CURRENT_TIMESTAMP();
Renvoie un TIMESTAMP WITH TIME ZONE contenant l'heure actuelle de la session ainsi que le fuseau
horaire de la session.
FROM_TZ(x, time_zone);
Convertit TIMESTAMP x et le fuseau horaire spécifié par time_zone en TIMESTAMP WITH TIMEZONE.
LOCALTIMESTAMP();
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);
sept
TO_TIMESTAMP(x, [format]);
TO_TIMESTAMP_TZ(x, [format]);
Convertit la chaîne x en TIMESTAMP WITH TIMEZONE.
Exemples
Example 1
Output -
08/31/2012 5:25:34 PM
Example 2
Output -
31-08-2012 05:26:14
Example 3
Output -
01/31/2013 5:26:31 PM
Example 4
Output -
8/31/2012 5:26:55.347000 PM
INTERVALLE JOUR À SECONDE - Il stocke une période de temps en termes de jours, heures, minutes
et secondes.
Fonctions d'intervalle
NUMTODSINTERVAL(x, interval_unit);
NUMTOYMINTERVAL(x, interval_unit);
TO_DSINTERVAL(x);
TO_YMINTERVAL(x);
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é.
Les curseurs explicites sont déclarés dans la partie de déclaration du Bloc PL/SQL :
DECLARE
CURSOR <nom_du_curseur> IS
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
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.
DECLARE
CURSOR curseur_EMPLOYE IS
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
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
BEGIN
OPEN curseur_EMPLOYE ;
LOOP
...
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
BEGIN
OPEN curseur_EMPLOYE ;
LOOP
...
*/
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
BEGIN
OPEN curseur_EMPLOYE ;
LOOP
END LOOP;
CLOSE curseur_EMPLOYE ;
END;
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
BEGIN
OPEN curseur_EMPLOYE ;
LOOP
END LOOP;
CLOSE curseur_EMPLOYE ;
END;
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 :
LOOP
...
END LOOP;
DECLARE
BEGIN
LOOP
END LOOP;
END;
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:
?
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:
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.
+----+----------+-----+-----------+----------+
+----+----------+-----+-----------+----------+
| 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
IF sql%notfound 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
Si vous vérifiez les enregistrements dans la table des clients, vous constaterez que les lignes ont été
mises à jour -
+----+----------+-----+-----------+----------+
+----+----------+-----+-----------+----------+
| 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.
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
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 -
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
BEGIN
OPEN c_customers;
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
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.
Table-based
DECLARE
customer_rec customers%rowtype;
BEGIN
FROM customers
WHERE id = 5;
END;
Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -
Customer ID: 5
DECLARE
CURSOR customer_cur is
FROM customers;
customer_rec customer_cur%rowtype;
BEGIN
OPEN customer_cur;
LOOP
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 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
TYPE
type_name IS RECORD
...
record-name type_name;
DECLARE
(title varchar(50),
author varchar(50),
subject varchar(100),
book_id number);
book1 books;
book2 books;
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
(title varchar(50),
author varchar(50),
subject varchar(100),
book_id number);
book1 books;
book2 books;
BEGIN
-- Book 1 specification
book1.book_id := 6495407;
-- Book 2 specification
book2.book_id := 6495700;
END;
Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -
Book 1 title : C Programming
DECLARE
(title varchar(50),
author varchar(50),
subject varchar(100),
book_id number);
book1 books;
book2 books;
BEGIN
END;
BEGIN
-- Book 1 specification
book1.title := 'C Programming';
book1.book_id := 6495407;
-- Book 2 specification
book2.book_id := 6495700;
printbook(book1);
printbook(book2);
END;
Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -
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
exception1-handling-statements
exception2-handling-statements
exception3-handling-statements
........
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
FROM customers
WHERE id = c_id;
EXCEPTION
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!
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.
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
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.
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.
DECLARE
my-exception EXCEPTION;
Exemple
DECLARE
c_name customerS.Name%type;
c_addr customers.address%type;
ex_invalid_id EXCEPTION;
BEGIN
RAISE ex_invalid_id;
ELSE
FROM customers
WHERE id = c_id;
EXCEPTION
dbms_output.put_line('Error!');
END;
Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -
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 -
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.
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.
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
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 -
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é.
Journalisation des événements et stockage des informations sur l'accès aux tables
Auditing
[OF col_name]
ON table_name
WHEN (condition)
DECLARE
Declaration-statements
BEGIN
Executable-statements
EXCEPTION
Exception-handling-statements
END;
Où,
{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.
[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 -
+----+----------+-----+-----------+----------+
+----+----------+-----+-----------+----------+
| 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 -
DECLARE
sal_diff number;
BEGIN
END;
Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -
Trigger created.
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 -
Old salary:
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
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 -
Spécification du paquet
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.
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 .
c_sal customers.salary%TYPE;
BEGIN
SELECT salary INTO c_sal
FROM customers
WHERE id = c_id;
END find_sal;
END cust_sal;
Lorsque le code ci-dessus est exécuté à l'invite SQL, il produit le résultat suivant -
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
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 -
Salary: 3000
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 -
+----+----------+-----+-----------+----------+
| 6 | Louis | 22 | MP | 5500.00 |
+----+----------+-----+-----------+----------+
La spécification du paquet
-- Adds a customer
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 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.
c_name customerS.No.ame%type,
c_age customers.age%type,
c_addr customers.address%type,
c_sal customers.salary%type)
IS
BEGIN
END addCustomer;
BEGIN
WHERE id = c_id;
END delCustomer;
PROCEDURE listCustomer IS
CURSOR c_customers is
BEGIN
name_list.extend;
name_list(counter) := n.name;
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 -
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.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
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é.
- Commande SELECT
- Commande INSERT, UPDATE, DELETE
- Commande de gestion de transaction COMMIT, ROLLBACK, SAVEPOINT
- Utilisation de fonctions SQL : TO_CHAR, TO_NUMBER, SUBSTR, ...
2.1 DECLARE
COMPTEUR Number;
TOTAL Number(8,2);
1
QUOTA Number Default 10;
V_NOM Varchar2(50);
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';
V_Date T_Date;
2
(NOM VARCHAR2(30),
PRENOM VARCHAR2(30),
DATE_NAIS T_Date);
TABLE_ADRESSE ADRESSE;
INDICE BINARY_INTEGER;
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 :
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
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
5
Revision PL-SQL Guide SQL*Plus
Déconnexion
- La commande de déconnexion est la commande exit.
Activer un utilisateur
- Par défaut, cet utilisateur est désactivé
- ALTER USER HR ACCOUNT UNLOCK;
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).
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, /).
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…
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.
1
SELECT * FROM REGIONS;
Visualiser ensuite le contenu du buffer.
Solution
C:\Users\Matthieu>sqlplus
Enter user-name: hr
Enter password:
Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - Production
SQL> list
1* SELECT * FROM REGIONS
SQL> c/REGIONS/DEPARTMENTS
1* SELECT * FROM DEPARTMENTS
SQL> /
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.
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.
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:
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 :
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 :
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.
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.
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.
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.
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.
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 :
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.
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.
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 :
Ci-dessous, nous cherchons la présence du terme "Ave." dans l'adresse des employés.
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
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, ...
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.
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 :
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.
Nous disposons de deux autres informations très utiles pour les différences de dates :
Dans l'exemple ci-dessous, nous calculons le nombre de jours qu'ont duré les Jeux
Olympiques 2016 (du 5 au 21 août).
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().
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(*)
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.
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 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.
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.
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 :
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).
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 *
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.
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.
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.
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.
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.
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
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
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.
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.
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 !
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é.
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).
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.
Les règles pour le tri et la limitation des résultats sont aussi les mêmes.
Exercices
Avec l'opérateur INTERSECT :
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.
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.
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.
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.
La requête ci-dessous liste les produits par catégorie et les classe en trois groupes de prix
croissants.
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 :
Syntaxe
La création d'une vue revient à créer une requête SQL stockée en base avec un nom :
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.
Syntaxe
La création d'une vue revient à créer une requête SQL stockée en base avec un nom :
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.
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.
UPDATE Client
SET Societe = 'Cyberdyne Systems'
WHERE CodeCli = 'CYBER';
Vérification :
UPDATE Client
SET Email = 'email@cyberdyne.com'
WHERE CodeCli = 'CYBER';
Vérification :
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
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 :
Création de la vue
La vue sur laquelle le trigger va être placé est la suivante :
Test du trigger
Insertion d'un produit dans la vue :
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.
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.
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.
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.
BEGIN TRANSACTION;
Une fois ouverte, la transaction reste active jusqu'a ce qu'elle soit explicitement
finalisée par une validation ou une annulation.
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 :
ROLLBACK;
Vérification de l'annulation :
BEGIN TRANSACTION;
Insertion d'une donnée :
COMMIT;
Conformation de l'insertion :
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.
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 :
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.
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.
Avantages
Intégration
Il est possible d'écrire des fonctions complexes de manipulation de données sans recourir à un
langage externe.
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.
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
RECORD
Curseurs
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;
RETURN varchar
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?
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.
Q 4 - Lequel des énoncés suivants n'est pas vrai à propos de la section de déclaration d'un bloc PL /
SQL?
C - Il définit toutes les variables, curseurs, sous-programmes et autres éléments à utiliser dans le
programme.
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.
Q 6 - Lequel des énoncés suivants n'est pas vrai à propos de la section d'exécution d'un bloc PL /
SQL?
B - Il peut avoir juste une commande NULL pour indiquer que rien ne doit être exécuté.
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?
Q 8 - Laquelle des affirmations suivantes est vraie pour les commentaires en PL / SQL?
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 * /.
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.
Q 11 - Lequel des énoncés suivants est vrai pour les types de données scalaires dans PL / SQL?
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.
compteur binary_integer;
A-0
B-1
C - NULL
DECLARE
-- Global variables
BEGIN
DECLARE
-- Local variables
BEGIN
END;
END;
num: 95
num: 195
C - Il imprimera
num: 95
num: 95
D - Il imprimera
num: 195
num: 195
DECLARE
c_id := 1;
c_name customers.name%type;
c_addr customers.address%type;
BEGIN
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
D - La variable c_id doit être déclarée comme une variable compatible avec le type comme -
A - Une constante contient une valeur qui, une fois déclarée, ne change pas dans le programme.
DECLARE
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 -
C - activer dbmsoutput
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.
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
END IF;
END;
C - Il imprimera
Explication
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
END IF;
END;
C - Il imprimera
Explication
CASE selector
...
END CASE;
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.
DECLARE
BEGIN
case
end case;
END;
C - Très bien
Question 1
Lors d'une transaction, j’exécute la commande:
Que faire?
b . J’effectue un commit
d . J’effectue un rollback
Question 2
Qu’est-ce qu’une vue ?
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
d . Une vue est une table logique basée sur une ou plusieurs tables
Question 3
select *
from Logement
where lieu='Corse'
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 ?
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
Question 6
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
Question 7
Question 11
Parmi les affirmations suivantes, qui concernent les index, laquelle est fausse ?
c . Lorsque l’on met une clé sur une colonne (primaire ou étrangère), automatiquement, un index
existe sur cette colonne
a. Aucune
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
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
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 ?
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
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 ?
b . Un trigger
c . Une vue
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
a. EXECUTE privilege
b . GRANT privilege
c . ALTER privilege
d . CREATE privilege
Question 20
a. commit=1;
b . SET autocommit=0;
c . SET autocommit=1;
d . START TRANSACTION;
Question 21
Question 22
Lors d'une transaction, dire que deux programmes sont concurrents, c’est dire que :
d . PROCEDURE nom_procedure([paramètre1[,paramètre2,…]])
Question 24
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
Dans une vue, quelle est l’utilité de la clause WITH CHECK OPTION
b . On vérifie que tout ce qui est inséré dans la vue peut être lu dans la vue
a.
b.
c .
d.
Question 27
b . Une variable indiquant combien de lignes on souhaite récupérer après exécution d’une
requête
Question 28