Feuille de TD 2 de BDA, M1 2008 2009 11-03-2011 Les fichiers en format pdf des projections du cours sont disponibles sur la page : http://www.ibisc.univevry.fr/~serena mais ne pas les imprimer au bât. C, svp. SGBDRO avec Oracle Tout au long de ce TD, nous travaillerons sur un exemple de gestion de comptes bancaires avec deux tables : Clients et Comptes. Les clients pourront avoir plusieurs comptes mais chaque comptes ne peut appartenir quʼà un seul client (relation MANY-ONE). Lʼenvironnement de travail sous Windows est le logiciel Tora de Oracle. => Lancez Tora et connectez-vous à la base de donnée Oracle en fournissant un nom dʼutilisateur identique à votre login (sans le caractère «-» sʼil est présent) ET un mot de passe égal à ce même nom dʼutilisateur. => Votre base de donnée est initialement vierge et vous avez les droits de lecture/écriture pour les types, tables, vues, etc. => A la fermeture de la session vous aurez le choix de conserver vos modifications sur la base ou non (COMMIT). 1 Première version simpliste Dans cette version simpliste aucune clé primaire ni aucune relation nʼont été définies. Elle est simplement fournie pour donner un prototype de code SQL. A partir du fichier ComptesClientsV1.sql fourni, exécutez chaque instruction SQL après en avoir analyser le sens et avoir noté au fur et à mesure les éventuels problèmes (erreur dʼexécution ou non intégrité de la base). REM ******************************************************************** REM ************** EXERCICE COMPTES / CLIENTS - VERSION 1 ************** (ORACLE 10g SQL) REM ******************************************************************** REM Voir la documentation "Oracle Database SQL Reference 10g Release 2 (10.2) - Part Number B14200-02" REM à l adresse : http://download.oracle.com/docs/cd/b19306_01/server.102/b14200/toc.htm REM ou l index general "Oracle Database Master Index 10g Release 2 (10.2)" REM à l adresse : http://www.oracle.com/pls/db102/show_mindex REM **** CREATION DES TYPES **** CREATE TYPE Compte AS OBJECT ( lenum NUMBER(6),, letype VARCHAR(10),, lesolde NUMBER(10,2),, lepossesseur NUMBER(6) ) ; CREATE TYPE SetComptes AS TABLE OF Compte ; CREATE TYPE Client AS OBJECT ( lenumss NUMBER(6),, lenom VARCHAR(25), 1
) ;, ladresse VARCHAR(50),, letelephone VARCHAR(20),, lescomptes SetComptes REM **** CREATION TABLE COMPTES **** CREATE TABLE Comptes OF Compte ; REM Ajout de commentaires COMMENT ON COLUMN Comptes.leNum IS 'Numéro du compte (6 digits : précision sur l unité)' ; COMMENT ON COLUMN Comptes.leType IS 'Type de compte : CHEQUE, CODEVI, EPARGNE...' ; COMMENT ON COLUMN Comptes.leSolde IS 'Solde du compte (10 digits : précision au centième)' ; COMMENT ON COLUMN Comptes.lePossesseur IS ' Possesseur du compte' ; REM **** CREATION TABLE CLIENTS **** CREATE TABLE Clients OF Client NESTED TABLE lescomptes STORE AS LesComptesTable ; REM Ajout de commentaires COMMENT ON COLUMN Clients.leNumSS IS 'Numéro du client (6 digits : précision sur l unité)' ; COMMENT ON COLUMN Clients.leNom IS 'Le nom du client' ; COMMENT ON COLUMN Clients.lAdresse IS 'L adresse du client' ; COMMENT ON COLUMN Clients.leTelephone IS 'Le téléphone du client' ; COMMENT ON COLUMN Clients.lesComptes IS 'Les comptes du client (NESTED TABLE)' ; REM *** PREMIERES REQUETES SIMPLES *** SELECT lenum, letype, lesolde, lepossesseur FROM Comptes ; SELECT lenom FROM Clients ; SELECT lenumss, lenom, ladresse, letelephone FROM Clients ; SELECT lc.*, c.lenom FROM Clients c, table(c.lescomptes) lc ; REM CREATION D'UNE VUE pour VOIR LES CLIENTS REM car SELECT * FROM CLIENTS ne fonctionne pas REM du fait que la table contient une table imbriquée CREATE VIEW LESCLIENTS AS SELECT lenumss, lenom, ladresse, letelephone FROM Clients ; REM CREATION D'UNE VUE pour VOIR LES COMPTES DES CLIENTS REM qui permet d'observer la table imbriquée pour tous les clients CREATE VIEW LESCOMPTESCLIENTS AS SELECT lc.*, c.lenom FROM Clients c, table(c.lescomptes) lc ; SELECT * FROM LESCLIENTS ; SELECT * FROM LESCOMPTESCLIENTS ; REM INSERTION DE TUPLES CLIENTS (sans compte) Clients VALUES ( 100, 'Pierre', '10 rue de la Gare', '01...', SetComptes()) ; Clients VALUES ( 200, 'Paul', '3 rue du Parc ', '06...', SetComptes()) ; Clients VALUES ( 300, 'Alain', '2 rue du Lac', '09...', Null) ; Clients VALUES ( 400, 'Alain OK', '2 rue du Lac', '09...', SetComptes()) ; REM INSERTION DE TUPLES COMPTES ASSOCIES AUX CLIENTS PAR LEURS NUMEROS Comptes VALUES ( 1, 'CHEQUE', 1000, 100) ; Comptes VALUES ( 2, 'CODEVI', 2000, 100) ; Comptes 2
VALUES ( 3, 'CHEQUE', 10000, 200) ; Comptes VALUES ( 4, 'EPARGNE', 9000, 300) ; Comptes VALUES ( 5, 'LIVRET', 4000, 300) ; Comptes VALUES ( 6, 'EPARGNE', 9000, 400) ; Comptes VALUES( 7, 'LIVRET', 4000, 400) ; SELECT * FROM LESCLIENTS ; SELECT * FROM LESCOMPTESCLIENTS ; REM REMPLISSAGE DES COMPTES DANS LA TABLE DES COMPTES DES CLIENTS REM Il existe deux syntaxes pour l insertion dans une table imbriquée : REM THE (...) (...) ; REM TABLE (...) VALUES (...) ; THE (SELECT c.lescomptes FROM Clients c WHERE c.lenumss = 100) (SELECT cp.* FROM Comptes cp WHERE cp.lepossesseur = 100) ; THE (SELECT c.lescomptes FROM Clients c WHERE c.lenumss = 200) (SELECT cp.* FROM Comptes cp WHERE cp.lepossesseur = 200) ; THE (SELECT c.lescomptes FROM Clients c WHERE c.lenumss = 300) (SELECT cp.* FROM Comptes cp WHERE cp.lepossesseur = 300) ; THE (SELECT c.lescomptes FROM Clients c WHERE c.lenumss = 400) (SELECT cp.* FROM Comptes cp WHERE cp.lepossesseur = 400) ; SELECT * FROM LESCLIENTS ; SELECT * FROM LESCOMPTESCLIENTS ; REM Requete pour voir les comptes de Pierre seulement SELECT * FROM LESCOMPTESCLIENTS lc WHERE lc.lenom = 'Pierre' ; ELEMENTS de CORRECTION : Lors de lʼinsertion du tuple client pour Alain, on a utilisé Null au lieu du constructeur de table SetComptes : Clients VALUES ( 300, 'Alain', '2 rue du Lac', '09...', Null) ; En lʼabsence de contrainte dʼintégrité, la requête dʼinsertion est acceptée mais il ne sera pas possible dʼajouter les tuples des comptes dʼalain dans la table. Ce qui est tenté plus loin dans : THE (SELECT c.lescomptes FROM Clients c WHERE c.lenumss = 300) (SELECT cp.* FROM Comptes cp WHERE cp.lepossesseur = 300) ; Cette requête provoque donc lʼerreur : table inexistante. 2 Deuxième version avec contraintes d intégrité simples Dans cette seconde version, lʼobjectif est de définir les premières contraintes dʼintégrité sous la forme : - de clés primaires, - de non nullité dʼattributs. Il est possible de déclarer les contraintes soit à la création de la table soit après coup (la déclaration peut être alors plus précise). Exemple pour les clés primaires : - à la création : CREATE TABLE Comptes OF Compte (PRIMARY KEY (lenum)) ; - après coup : ALTER TABLE Comptes 3
ADD CONSTRAINT comptes_pk PRIMARY KEY ( lenum ) 1) Transformez la version 1 pour définir les clés primaires des tables ainsi que les attributs devant raisonnablement être NOT NULL. ELEMENTS de CORRECTION : REM ******************************************************************** REM ************** EXERCICE COMPTES / CLIENTS - VERSION 2 ************** (ORACLE 10g SQL) REM ******************************************************************** REM Voir la documentation "Oracle Database SQL Reference 10g Release 2 (10.2) - Part Number B14200-02" REM à l adresse : http://download.oracle.com/docs/cd/b19306_01/server.102/b14200/toc.htm REM ou l index general "Oracle Database Master Index 10g Release 2 (10.2)" REM à l adresse : http://www.oracle.com/pls/db102/show_mindex REM RESET DES TYPES ET TABLES REM DROP TABLE Clients2 force ; REM DROP TYPE Client2 force ; REM DROP TABLE Comptes2 force ; REM DROP TYPE SetComptes2 ; REM DROP TYPE Compte2 force ; REM **** CREATION DES TYPES **** CREATE TYPE Compte2 AS OBJECT ( lenum NUMBER(6), letype VARCHAR(10), lesolde NUMBER(10,2), lepossesseur NUMBER(6) ) ; CREATE TYPE SetComptes2 AS TABLE OF Compte2 ; CREATE TYPE Client2 AS OBJECT ( lenumss NUMBER(6), lenom VARCHAR(25), ladresse VARCHAR(50), letelephone VARCHAR(20), lescomptes SetComptes2 ) ; REM **** CREATION TABLE COMPTES **** CREATE TABLE Comptes2 OF Compte2 ; REM Ajout des contraintes d'intégrité (en vérifications immédiates: ) ALTER TABLE Comptes2 ADD CONSTRAINT comptes2_pk PRIMARY KEY ( lenum ) ALTER TABLE Comptes2 ADD CONSTRAINT comptes2_type_nn CHECK (letype IS NOT NULL) ALTER TABLE Comptes2 ADD CONSTRAINT comptes2_solde_nn CHECK (lesolde IS NOT NULL) ALTER TABLE Comptes2 ADD CONSTRAINT comptes2_possesseur_nn CHECK (lepossesseur IS NOT NULL) 4
REM Ajout de commentaires COMMENT ON COLUMN Comptes2.leNum IS 'Clé primaire de la table Comptes2 (NOT NULL, UNIQUE)' ; COMMENT ON COLUMN Comptes2.leType IS 'Type de compte : CHEQUE, CODEVI, EPARGNE...(NOT NULL)' ; COMMENT ON COLUMN Comptes2.leSolde IS 'Solde du compte : précision au centième (NOT NULL)' ; COMMENT ON COLUMN Comptes2.lePossesseur IS ' Possesseur du compte (NOT NULL)' ; REM **** CREATION TABLE CLIENTS **** CREATE TABLE Clients2 OF Client2 NESTED TABLE lescomptes STORE AS LesComptesTable2 ; REM Ajout des contraintes d'intégrité (en vérifications immédiates: ) ALTER TABLE Clients2 ADD CONSTRAINT cliens2_pk PRIMARY KEY ( lenumss ) ALTER TABLE Clients2 ADD CONSTRAINT clients2_lenom_nn CHECK (lenom IS NOT NULL) ALTER TABLE Clients2 ADD CONSTRAINT clients2_lescomptes_nn CHECK (lescomptes IS NOT NULL) REM Ajout de commentaires COMMENT ON COLUMN Clients2.leNumSS IS 'Clé primaire de la table Clients2 (NOT NULL, UNIQUE)' ; COMMENT ON COLUMN Clients2.leNom IS 'Le nom du client (NOT NULL)' ; COMMENT ON COLUMN Clients2.lAdresse IS 'L''adresse du client' ; COMMENT ON COLUMN Clients2.leTelephone IS 'Le téléphone du client' ; COMMENT ON COLUMN Clients2.lesComptes IS 'Les comptes du client (NESTED TABLE, NOT NULL)' ; REM CREATION D'UNE VUE pour VOIR LES CLIENTS REM car SELECT * FROM CLIENTS2 ne fonctionne pas REM du fait que la table contient une table imbriquée CREATE VIEW LESCLIENTS2 AS SELECT lenumss, lenom, ladresse, letelephone FROM Clients2 ; REM CREATION D'UNE VUE pour VOIR LES COMPTES DES CLIENTS REM qui permet d'observer la table imbriquée pour tous les clients CREATE VIEW LESCOMPTESCLIENTS2 AS SELECT lc.*, c.lenom FROM Clients2 c, table(c.lescomptes) lc ; REM PREMIERES REQUETES SIMPLES SELECT * FROM LESCLIENTS2 ; SELECT * FROM LESCOMPTESCLIENTS2 ; REM INSERTION DE TUPLES CLIENTS (sans compte) Clients2 VALUES ( 100, 'Pierre', '10 rue de la Gare', '01...', SetComptes2()) ; Clients2 VALUES ( 200, 'Paul', '3 rue du Parc ', '06...', SetComptes2()) ; Clients2 VALUES ( 300, 'Alain', '2 rue du Lac', '09...', Null) ; REM ERREUR Clients2 VALUES ( 400, 'Alain OK', '2 rue du Lac', '09...', SetComptes2()) ; REM INSERTION DE TUPLES COMPTES (non associés aux clients) Comptes2 5
VALUES ( 1, 'CHEQUE', 1000, 100) ; Comptes2 VALUES ( 2, 'CODEVI', 2000, 100) ; Comptes2 VALUES ( 3, 'CHEQUE', 10000, 200) ; Comptes2 VALUES ( 4, 'EPARGNE', 9000, 300) ; Comptes2 VALUES ( 5, 'LIVRET', 4000, 300) ; Comptes2 VALUES ( 6, 'EPARGNE', 9000, 400) ; Comptes2 VALUES( 7, 'LIVRET', 4000, 400) ; SELECT * FROM LESCLIENTS2 ; SELECT * FROM LESCOMPTESCLIENTS2 ; REM REMPLISSAGE DES COMPTES DANS LA TABLE DES COMPTES DES CLIENTS THE (SELECT c.lescomptes FROM Clients2 c WHERE c.lenumss = 100) (SELECT cp.* FROM Comptes2 cp WHERE cp.lepossesseur = 100) ; THE (SELECT c.lescomptes FROM Clients2 c WHERE c.lenumss = 200) (SELECT cp.* FROM Comptes2 cp WHERE cp.lepossesseur = 200) ; THE (SELECT c.lescomptes FROM Clients2 c WHERE c.lenumss = 300) (SELECT cp.* FROM Comptes2 cp WHERE cp.lepossesseur = 300) ; REM ERREUR THE (SELECT c.lescomptes FROM Clients2 c WHERE c.lenumss = 400) (SELECT cp.* FROM Comptes2 cp WHERE cp.lepossesseur = 400) ; SELECT * FROM LESCOMPTESCLIENTS2 ; REM Essai de réinsertion des mêmes comptes pour le client 400 (Alain OK) THE (SELECT c.lescomptes FROM Clients2 c WHERE c.lenumss = 400) (SELECT cp.* FROM Comptes2 cp WHERE cp.lepossesseur = 400) ; REM Les comptes ont été réinsérés! SELECT * FROM LESCOMPTESCLIENTS2 ; REM Destruction des comptes dans la table imbriquée pour le client 400 (Alain OK) DELETE FROM THE (SELECT c.lescomptes FROM Clients2 c WHERE c.lenumss = 400) lc WHERE lc.lepossesseur = 400 ; SELECT * FROM LESCOMPTESCLIENTS2 ; REM On recommence l'insertion pour le client 400 (Alain OK) THE (SELECT c.lescomptes FROM Clients2 c WHERE c.lenumss = 400) (SELECT cp.* FROM Comptes2 cp WHERE cp.lepossesseur = 400) ; SELECT * FROM LESCOMPTESCLIENTS2 ; REM Requete pour voir les comptes de Pierre seulement SELECT * FROM LESCOMPTESCLIENTS2 lc WHERE lc.lenom = 'Pierre' ; 2) Vous avez dû insérer les comptes du client #400 («Alain OK») dans sa table imbriquée. Exécutez à nouveau lʼordre dʼinsertion. Est-ce toujours possible? Quʼen est-il de lʼintégrité de la base? THE (SELECT c.lescomptes FROM Clients c WHERE c.lenumss = 400) (SELECT cp.* FROM Comptes cp WHERE cp.lepossesseur = 400) ; ELEMENTS de CORRECTION : L insertion multiple de mêmes comptes dans la table imbriquée sera toujours possible. La relation MANY-ONE n est pas assurée par le SGBD mais dans le cas présent la relation est 6
encore correcte car même si un client voit plusieurs fois le même compte, un compte n a jamais qu un seul possesseur. Aussi, le problème n est pas tant un défaut d intégrité au niveau relationnel qu un défaut d intérité portant sur le nombre de référence à un même compte. 2 Troisième version avec contraintes d intégrité avancées Dans cette version, lʼobjectif est de définir des contraintes dʼintégrité avancées en fonction de ce quʼil est possible de faire avec Oracle. 1) Transformez le code de telle sorte à déclarer une clé étrangère au niveau des références de possesseurs des comptes. Exemple pour les clés étrangères : - à la création : CREATE TABLE Employees OF Employee (FOREIGN KEY (emplofsociety) REFERENCES Society(society_id) ON DELETE NO ACTION) ; - après coup : ALTER TABLE Employees ADD CONSTRAINT empl_soc_fk FOREIGN KEY (emplofsociety) REFERENCES Society(society_id) ON DELETE CASCADE) See Referential Integrity Constraints (http://download.oracle.com/docs/cd/b19306_01/server.102/b14220/data_int.htm#sthref3029) Actions Defined by Referential Integrity Constraints Referential integrity constraints can specify particular actions to be performed on the dependent rows in a child table if a referenced parent key value is modified. The referential actions supported by the FOREIGN KEY integrity constraints of Oracle are UPDATE and DELETE NO ACTION, and DELETE CASCADE. Note: Other referential actions not supported by FOREIGN KEY integrity constraints of Oracle can be enforced using database triggers. See Chapter 22, "Triggers" for more information. Delete No Action The No Action (default) option specifies that referenced key values cannot be updated or deleted if the resulting data would violate a referential integrity constraint. For example, if a primary key value is referenced by a value in the foreign key, then the referenced primary key value cannot be deleted because of the dependent data. Delete Cascade A delete cascades when rows containing referenced key values are deleted, causing all rows in child tables with dependent foreign key values to also be deleted. For example, if a row in a parent table is deleted, and this row's primary key value is referenced by one or more foreign key values in a child table, then the rows in the child table that reference the primary key value are also deleted from the child table. Delete Set Null A delete sets null when rows containing referenced key values are deleted, causing all rows in child tables with dependent foreign key values to set those values to null. For example, if employee_id references manager_id in the TMP table, then deleting a manager causes the rows for all employees working for that manager to have their manager_id value set to null. Constraint Attributes You can define constraints as either deferrable or not deferrable, and either initially deferred or initially immediate. These attributes can be different for each constraint. You specify them with keywords in the CONSTRAINT clause: 7
* DEFERRABLE or * INITIALLY DEFERRED or Constraints can be added, dropped, enabled, disabled, or validated. You can also modify a constraint's attributes. Constraint States * ENABLE ensures that all incoming data conforms to the constraint * DISABLE allows incoming data, regardless of whether it conforms to the constraint * VALIDATE ensures that existing data conforms to the constraint * NOVALIDATE means that some existing data may not conform to the constraint ELEMENTS de CORRECTION : REM Ajout des contraintes d'intégrité pour la relation COMPTES/CLIENTS REM ATTENTION, la clé primaire doit avoir préalablement été déclarée dans la table Clients. ADD CONSTRAINT comptes3_possesseur_fk FOREIGN KEY (lepossesseur) REFERENCES Clients3(leNumSS) ON DELETE CASCADE ADD CONSTRAINT comptes3_possesseur_nn CHECK (lepossesseur IS NOT NULL) Dès lors : - toute insertion de tuples dans la table Comptes ne pourra être effective que si la clé étrangère réfère à un tuple de clé primaire identique dans la table Clients. Si la table Client ne possède pas de tuple avec la même clé, le SGBD annulera l exécution de l instruction SQL (rollback). - avec ON DELETE CASCADE, toute destruction de tuples dans la table Clients entrainera automatiquement la destruction en cascade des tuples correspondants dans la table Comptes. 2) Discutez de la contrainte d'intégrité référentielle apportée par la présence de la clé étrangère et en fonction de lʼaction définie (No action, Cascade, Set null). Effectuez des tests. ELEMENTS de CORRECTION : Contrainte d intégrité apportée par la clé étrangère : - toute insertion de tuples dans la table Comptes ne pourra être effective que si la clé étrangère réfère à un tuple de clé primaire identique dans la table Clients. Si la table Client ne possède pas de tuple avec la même clé, le SGBD annulera l exécution de l instruction SQL (rollback). Contrainte d intégrité apportée par ON DELETE CASCADE : - toute destruction de tuples dans la table Clients entrainera automatiquement la destruction en cascade des tuples correspondants dans la table Comptes. 3) On vient de réaliser une partie de la relation recherchée dans le schéma ODL : class Compte { attribute integer lenum; attribute enum TypeCompte {CHEQUE, LIVRET, CODEVI} letype; attribute real lesolde; } relationship Client lepossesseur // autre côté de la relation inverse Client::lesComptes // cible de la relation inverse class Client { attribute string attribute string lenom; ladresse; 8
attribute string attribute integer letéléphone; lenumss; } relationship Set<Compte> lescomptes // un côté de la relation inverse Compte::lePossesseur; // cible de la relation inverse La question restante est «Comment réaliser lʼautre partie de la relation?» Pour cela, il faudrait être en mesure dʼautomatiser lʼajout et la destruction de comptes dans la liste des comptes dʼun client (lescomptes). Cʼest justement ce quʼoffrent les Triggers. See Oracle Database Application Developer's Guide Fundamentals - 10g Release 2 (10.2) - Part Number B14251-01 (http://download.oracle.com/docs/cd/b19306_01/appdev.102/b14251/adfns_triggers.htm#sthref1333) 9) Coding Triggers Triggers are procedures that are stored in the database and are implicitly run, or fired, when something happens. Traditionally, triggers supported the execution of a PL/SQL block when an INSERT, UPDATE, or DELETE occurred on a table or view. Triggers support system and other data events on DATABASE and SCHEMA. Oracle Database also supports the execution of PL/SQL or Java procedures. ELEMENTS de CORRECTION : REM ******************************************************************** REM ************** EXERCICE COMPTES / CLIENTS - VERSION 3 ************** (ORACLE 10g SQL)REM ******************************************************************** REM Voir la documentation "Oracle Database SQL Reference 10g Release 2 (10.2) - Part Number B14200-02" REM à l adresse : http://download.oracle.com/docs/cd/b19306_01/server.102/b14200/toc.htm REM ou l index general "Oracle Database Master Index 10g Release 2 (10.2)" REM à l adresse : http://www.oracle.com/pls/db102/show_mindex REM RESET DES TYPES ET TABLES REM DROP TABLE Clients3 force ; REM DROP TYPE Client3 force ; REM DROP TABLE Comptes3 force ; REM DROP TYPE SetComptes3 ; REM DROP TYPE Compte3 force ; REM **** CREATION DES TYPES **** CREATE TYPE Compte3 AS OBJECT ( lenum NUMBER(6), letype VARCHAR(10), lesolde NUMBER(10,2), lepossesseur NUMBER(6) ) ; CREATE TYPE SetComptes3 AS TABLE OF Compte3 ; CREATE TYPE Client3 AS OBJECT ( lenumss NUMBER(6), lenom VARCHAR(25), ladresse VARCHAR(50), letelephone VARCHAR(20), lescomptes SetComptes3 ) ; REM **** CREATION TABLE COMPTES **** CREATE TABLE Comptes3 OF Compte3 ; REM **** CREATION TABLE CLIENTS **** 9
CREATE TABLE Clients3 OF Client3 NESTED TABLE lescomptes STORE AS LesComptesTable3 ; REM Définition des clés primaires pour les tables COMPTES et CLIENTS ADD CONSTRAINT comptes3_pk PRIMARY KEY ( lenum ) ALTER TABLE Clients3 ADD CONSTRAINT cliens3_pk PRIMARY KEY ( lenumss ) REM Ajout des contraintes d'intégrité pour la relation COMPTES/CLIENTS REM ATTENTION, la clé primaire doit avoir préalablement été déclarée dans la table Clients. ADD CONSTRAINT comptes3_possesseur_fk FOREIGN KEY (lepossesseur) REFERENCES Clients3(leNumSS) ON DELETE CASCADE ADD CONSTRAINT comptes3_possesseur_nn CHECK (lepossesseur IS NOT NULL) REM Autres contraintes d'intégrité ADD CONSTRAINT comptes3_type_nn CHECK (letype IS NOT NULL) ADD CONSTRAINT comptes3_solde_nn CHECK (lesolde IS NOT NULL) ADD CONSTRAINT comptes3_solde_positive CHECK (lesolde > 0) ALTER TABLE Clients3 ADD CONSTRAINT clients3_lenom_nn CHECK (lenom IS NOT NULL) ALTER TABLE Clients3 ADD CONSTRAINT clients3_lescomptes_nn CHECK (lescomptes IS NOT NULL) REM Ajout de commentaires COMMENT ON COLUMN Comptes3.leNum IS 'Clé primaire de la table Comptes3 (NOT NULL)' ; COMMENT ON COLUMN Comptes3.leType IS 'Type de compte : CHEQUE, CODEVI, EPARGNE...(NOT NULL)' ; COMMENT ON COLUMN Comptes3.leSolde IS 'Solde du compte : précision au centième (NOT NULL, >0)' ; COMMENT ON COLUMN Comptes3.lePossesseur IS 'Clé étrangère : Clients3.leNumSS / Possesseur du compte (NOT NULL)' ; COMMENT ON COLUMN Clients3.leNumSS IS 'Clé primaire de la table Clients3 (NOT NULL, UNIQUE)' ; COMMENT ON COLUMN Clients3.leNom IS 'Le nom du client (NOT NULL)' ; COMMENT ON COLUMN Clients3.lAdresse IS 'L''adresse du client' ; 10
COMMENT ON COLUMN Clients3.leTelephone IS 'Le téléphone du client' ; COMMENT ON COLUMN Clients3.lesComptes IS 'Les comptes du client (NESTED TABLE, NOT NULL)' ; REM CREATION D'UNE VUE pour VOIR LES CLIENTS REM car SELECT * FROM CLIENTS3 ne fonctionne pas REM du fait que la table contient une table imbriquée CREATE VIEW LESCLIENTS3 AS SELECT lenumss, lenom, ladresse, letelephone FROM Clients3 ; REM CREATION D'UNE VUE pour VOIR LES COMPTES DES CLIENTS REM qui permet d'observer la table imbriquée pour tous les clients CREATE VIEW LESCOMPTESCLIENTS3 AS SELECT lc.*, c.lenom FROM Clients3 c, table(c.lescomptes) lc ; REM PREMIERES REQUETES SIMPLES SELECT * FROM LESCLIENTS3 ; SELECT * FROM LESCOMPTESCLIENTS3 ; REM INSERTION DE TUPLES CLIENTS (sans compte) Clients3 VALUES ( 100, 'Pierre', '10 rue de la Gare', '01...', SetComptes3()) ; Clients3 VALUES ( 200, 'Paul', '3 rue du Parc ', '06...', SetComptes3()) ; Clients3 VALUES ( 300, 'Alain', '2 rue du Lac', '09...', Null) ; REM ERREUR Clients3 VALUES ( 400, 'Alain OK', '2 rue du Lac', '09...', SetComptes3()) ; REM INSERTION DE TUPLES COMPTES (non associés aux clients) Comptes3 VALUES ( 1, 'CHEQUE', 1000, 100) ; Comptes3 VALUES ( 2, 'CODEVI', 2000, 100) ; Comptes3 VALUES ( 3, 'CHEQUE', 10000, 200) ; Comptes3 VALUES ( 4, 'EPARGNE', 9000, 300) ; REM ERREUR Comptes3 VALUES ( 5, 'LIVRET', 4000, 300) ; REM ERREUR Comptes3 VALUES ( 6, 'EPARGNE', 9000, 400) ; Comptes3 VALUES( 7, 'LIVRET', 4000, 400) ; SELECT * FROM LESCLIENTS3 ; SELECT * FROM LESCOMPTESCLIENTS3 ; REM REMPLISSAGE DES COMPTES DANS LA TABLE DES COMPTES DES CLIENTS REM Il existe deux syntaxes pour l'insertion dans une table imbriquée : REM THE (...) (...) ; REM TABLE (...) VALUES (...) ; THE (SELECT c.lescomptes FROM Clients3 c WHERE c.lenumss = 100) (SELECT cp.* FROM Comptes3 cp WHERE cp.lepossesseur = 100) ; THE (SELECT c.lescomptes FROM Clients3 c WHERE c.lenumss = 200) (SELECT cp.* FROM Comptes3 cp WHERE cp.lepossesseur = 200) ; THE (SELECT c.lescomptes FROM Clients3 c WHERE c.lenumss = 300) (SELECT cp.* FROM Comptes3 cp WHERE cp.lepossesseur = 300) ; REM ERREUR THE (SELECT c.lescomptes FROM Clients3 c WHERE c.lenumss = 400) (SELECT cp.* FROM Comptes3 cp WHERE cp.lepossesseur = 400) ; 11
SELECT * FROM LESCOMPTESCLIENTS3 ; REM Destruction du client 400 (Alian OK) SELECT * FROM Comptes3 c WHERE c.lepossesseur = 400 ; DELETE FROM Clients3 c WHERE c.lenumss = 400 ; REM => tous les comptes qui réfèrent à ce client ont été détruits automatiquement REM du fait de la clé étrangère dans les comptes SELECT * FROM LESCLIENTS3 ; SELECT * FROM Comptes3 c WHERE c.lepossesseur = 400 ; SELECT * FROM LESCOMPTESCLIENTS3 ; REM Creation d'une procedure TRIGGER lors de la creation d'un nouveau compte CREATE OR REPLACE TRIGGER MajLesComptes BEFORE INSERT ON Comptes3 /* Before inserting a compte : le possesseur du compte est automatiquement ajouté dans la table des comptes du côté client Clients3(lesComptes) */ FOR EACH ROW DECLARE n INTEGER; BEGIN SELECT lc.lenum INTO n FROM Clients3 c, table(c.lescomptes) lc WHERE lc.lenumss = :new.lepossesseur ; THE (SELECT c.lescomptes FROM Clients3 c WHERE c.lenumss = :new.lepossesseur) (:new.lenum, :new.letype, :new.lesolde, :new.lepossesseur) ; EXCEPTION WHEN NO_DATA_FOUND THEN Raise_application_error(-20322, 'Impossible de créer le compte pour le client ' :new.lepossesseur ' car il ne peut être insérer dans sa table de comptes!') ; END; SELECT * FROM LESCOMPTESCLIENTS3 ; Comptes3 VALUES( 10, 'LIVRET', 4000, 400) ; 12