Sunteți pe pagina 1din 17

Caractéristiques Objet-Relationnel de Oracle

3e année Informatique
Najib Tounsi

Référence utile: https://docs.oracle.com/cd/B28359_01/appdev.111/b28371/toc.htm


ou https://docs.oracle.com/cd/B12037_01/appdev.101/b10807/10_objs.htm

Prérequis: les notions de base de l'approche objets.

Sommaire

1. Un type pour les objets


1. Création de type d'objets
1. Exemple-1. Déclaration simple d'un objet
2. Exemple-1-bis. Ajout de méthodes
3. Exemple-2. Instanciation et utilisation simple de cet objet.
4. Exemple-2-bis. Utilisation de fonction membre.
5. Exemple-3. Implémentations des fonctions membres
2. Fonctions membres MAP et ORDER
3. Constructeurs.
2. L'Utilisation des objets
1. Comme composant d'un autre objet
2. Objet comme attribut dans une relation
3. Objet comme schéma de relation (ou comme ligne dans une table)
4. L'objet Relation
1. Relations imbriquées
2. Utilisation
5. Relation Non First Normal Form
1. Type Vecteur VARRAY
6. Référence à un objet
1. Autres requêtes
2. Les références en PL/SQL
7. Vues Objet/Relation
3. En Résumé
4. Correction de certains exercices

En Oracle les objets définis par l'utilisateur peuvent être utilisés principalement pour définir des colonnes de table avec des valeurs non classiques
comme une image, ou des éléments (lignes) de table. Oracle parle de Type au lieu de Classe.

1. Un Type pour les Objets


1.1. Création de type d'objets

Un type OBJECT est un type de donnée défini par l'utilisateur et qu'on peut utiliser comme les autres types Oracle. Il est défini par la commande
CREATE TYPE.

1.1.1. Exemple-1. Déclaration simple d'un objet

CREATE TYPE Adresse AS OBJECT (


no NUMBER,
rue VARCHAR2(25),
ville VARCHAR2(20)
);
/

Une adresse est composée d'un n° dans une rue dans une ville.

forme générale de déclaration de type d'objets :

CREATE TYPE nom de type AS OBJECT {liste de caractéristiques}


1.1.2. Exemple-1-bis. Ajout de méthodes

On peut munir cet objet de méthodes spécifiques appelées member. On rajoute leur déclaration à la liste des champs.

CREATE TYPE Adresse AS OBJECT (


no NUMBER,
rue VARCHAR2(25),
ville VARCHAR2(20),

MAP MEMBER FUNCTION getNo RETURN NUMBER,


MEMBER PROCEDURE display
);
/

forme générale des déclarations de méthodes:

MEMBER FUNCTION nom de fonction RETURN TYPE


MEMBER PROCEDURE nom de procedure

NB. RETURN et non pas RETURNS

Ici, on a muni les objets d'une fonction d'accès getNo qui retourne le n° dans la rue et une méthode display pour imprimer une adresse (MAP sera
expliqué plus bas).

1.1.3. Exemple-2. Instanciation et utilisation simple de cet objet.

Le programme PL/SQL suivant crée un objet Adresse et imprime ses attributs :

SET SERVEROUTPUT ON
DECLARE
maison Adresse;
BEGIN
maison := Adresse(2, 'Atlas', 'Rabat');
dbms_output.put_line('Maison No: '|| TO_CHAR(maison.no));
dbms_output.put_line('Rue: '|| maison.rue);
dbms_output.put_line('Ville: '|| maison.ville);
END;

où on peut noter l'instanciation Adresse(2, 'Atlas', 'Rabat'); avec un appel à une fonction implicite adresse () du même nom que le
type d'objet (c'est un constructeur en fait), et des valeurs énumérées comme dans un insert into de SQL.

Résultat de ce programme :

SQL> /
Maison No: 2
Rue: Atlas
Ville: Rabat

Procédure PL/SQL terminé avec succès.

1.1.4. Exemple-2-bis. Utilisation de fonction membre.

DECLARE
maison Adresse;
BEGIN
maison := NEW Adresse(2, 'Atlas', 'Rabat');
maison.display(); /* Invocation de méthode sur objet */
END;

NB. Pour que ce programme marche, il faut, bien sûr, implémenter la méthode display (voir § ci-après).

Le résultat de ce programme est le même que précédemment :

SQL> /
Maison No: 2
Rue: Atlas
Ville: Rabat

Procédure PL/SQL terminé avec succès.


Remarque: on peut souhaiter utiliser la forme

CREATE OR REPLACE TYPE ...

qui crée le type ou le remplace s'il existe (car sinon, il y a erreur). La commande

DROP TYPE nom;

permet de supprimer un type déjà créé.

1.1.5. Exemple-3. Implémentations des fonctions membres

On définit le corps des méthodes de façon séparée avec la commande

CREATE TYPE BODY

Forme simple :

CREATE TYPE BODY type-d-objet AS


déclaration méthode IS
bloc PLSQL
...
END;

Ce qui donne pour notre exemple :

CREATE TYPE BODY Adresse AS

/* Implémentation fonction d'accès */


MAP MEMBER FUNCTION getNo RETURN NUMBER IS
BEGIN
RETURN no;
END;

/* Implémentation méthode d'affichage display */


MEMBER PROCEDURE display IS
BEGIN
dbms_output.put_line('Maison No: '|| TO_CHAR(no));
dbms_output.put_line('Rue: '|| rue);
dbms_output.put_line('Ville: '|| ville);

END;
END;
/

Noter l'usage direct des noms de champs de l'objet, appelé SELF en Oracle, sur qui la méthode est appelée. L'écriture no, rue, ville est
équivalente à self.no, self.rue, self.ville .

Une autre déclaration du profile de la méthode display peut -être :

MEMBER PROCEDURE display ( SELF IN OUT NOCOPY Adresse )

cette syntaxe permet au paramètre SELF de passer par référence au lieu de passer par valeur (qui est le mode IN OUT par défaut). Ce qui sied à un
objet large utilisé en consultation. Exercice : le vérifier (modifier l'objet dans la procédure). NB. Utiliser la même déclaration dans le BODY (sans
variable paramètre).

Exercices :

1. Rajouter une méthode qui change la rue d'une adresse.


2. Rajouter une méthode qui change toute l'adresse. La nouvelle adresse est passée en paramètre.

1.2. Fonctions membres MAP et ORDER

Une méthode déclarée MAP sert pour classer et déterminer si deux objets sont les mêmes; en utilisant les valeurs d'attribut des objets par exemple,.
Si on écrit Obj1 >= Obj2 cela revient, dans notre cas, à utiliser la méthode getNo déclarée MAP pour tester si Obj1.getNo() >=
Obj2.getNo() . La valeur de cette fonction dépend des attributs d'un objet, par exemple le n° dans la rue dans notre exemple.

Le programme suivant :

DECLARE
maison Adresse;
maison2 Adresse;
BEGIN
maison := Adresse(2, 'Atlas', 'Rabat');
maison2 := Adresse(3, 'Atlas', 'Casa');
IF (maison > maison2) THEN maison.display();
ELSE maison2.display();
END IF;
END;
/

imprime les données de maison2.

Une méthode MAP détermine un ordre sur les objets. Elle est appelée automatiquement dans les requêtes qui nécessitent un ordre, comme par
exemple avec DISTINCT, ORDER BY ou GROUP BY de SQL. (cf. exemple plus bas)

Une fonction membre déclarée ORDER permet, quant à elle, de dire si un objet est plus grand, égale ou plus petit qu'un autre, selon un critère
définie par la méthode elle même. Par exemple :

ORDER MEMBER FUNCTION compare (a Adresse) RETURN NUMBER IS


BEGIN
IF ville > a.ville THEN
RETURN 1;
ELSIF ville < a.ville THEN
RETURN -1;
ELSE
RETURN 0;
END IF;
END;

Cette méthode doit retourner -1, 0 ou 1 avec la signification <, = ou >.

Une fonction ORDER ne peut exister avec une fonction MAP.

1.3. Constructeurs.
Un constructeur est introduit par le mot clé CONSTRUCTOR. Un constructeur pour le type Adresse serait :

CONSTRUCTOR FUNCTION Adresse (a IN OUT NOCOPY Adresse) RETURN SELF AS RESULT

Noter le résultat SELF qui est donc du type de l'objet à construire. Ici, on a choisi une adresse a (déjà créée) comme paramètre pour
l'instanciation du nouvel objet. Voici le corps pour cette méthode :

CONSTRUCTOR FUNCTION Adresse (a IN OUT NOCOPY Adresse)


RETURN SELF AS RESULT IS
BEGIN
no := a.no;
rue := a.rue;
ville := a.ville;
return;
END;

Un nouvel objet est créé par copie de a.

Un usage de ce constructeur est :

m := Adresse(3, 'Rif', 'Casa');


maison := Adresse(m);

Exercice : définir un autre constructeur à partir des trois données élémentaires, numéro, rue et ville. Tester.

Voir ici pour plus de détails.

2. L'Utilisation des Objets


On peut utiliser un objet de différentes manières. Comme composant d'un autre objet (programmation objet classique), comme valeur d'attribut
dans une relation (étendre la possibilité des valeurs de champ), ou comme tuple dans une relation (notion de collection d'objets), ou comme type
d'une relation (sert pour les relations imbriquées), ou comme une vue "objet" sur une relation SQL classique.

2.1. Comme composant d'un autre objet


On reprend le type Adresse de l'exemple 1-bis.

Exemple-3. Un objet personne ayant une adresse


CREATE or replace TYPE Personne AS OBJECT (
code number(5),
nom varchar2(30),
adr Adresse,
member procedure display
);
/

CREATE or replace TYPE BODY Personne AS

MEMBER PROCEDURE display IS


BEGIN
dbms_output.put_line('Code: '|| TO_CHAR(code));
dbms_output.put_line('nom: '|| nom);
dbms_output.put_line('Ville: '|| adr.ville); /* ou bien adr.display();*/
END;
END;
/

Instanciation et utilisation :

DECLARE
moi Personne;
BEGIN
moi := Personne (124, 'Untel', Adresse(2, 'Atlas', 'Rabat')); (1)
dbms_output.put_line('Mon nom: ' || moi.nom);
dbms_output.put_line('Ma ville: '|| moi.adr.ville); (3)
moi.display;
END;
/

A ce niveau, on utilise des objets en mémoire comme en programmation Java par exemple. Il n'y a pas encore de conservation de données dans une
BD quelconque. Noter l'instanciation par constructeurs imbriqués (1) et l'accès en cascade (3).

Résultat du programme :

Mon nom: Untel


Ma ville: Rabat
Code: 124
nom: Untel
Ville: Rabat

PL/SQL procedure successfully completed.

Exercice : Reprendre ce programme un imprimant l'adresse complète (utiliser la méthode display de Adresse.)

En générale les objets Oracle sont à utiliser avec des relations. Soit comme type d'attribut, c'est à dire un objet est une valeur dans une colonne, soit
comme relation-objet, c'est à dire une "ligne" dans une table. La table est alors un ensemble d'objets.

2.2. Objet comme attribut dans une relation


Exemple-4. Création d'une table de personnes (avec code, nom et adresse) faisant usage de la classe Adresse de l'exemple précédent.

CREATE TABLE Personnes (


code number(5) primary key,
nom varchar2 (20),
adr Adresse
);

Noter le CREATE TABLE au lieu de CREATE OBJECT de l'exemple précédent.

Ici, c'est une table SQL normale. Seul diffère un type de champ qui est non primitif.

Instanciation :

INSERT INTO Personnes VALUES (123, 'Untel', Adresse(56, 'IbnSina', 'Rabat'));


INSERT INTO Personnes VALUES (124, 'Untel2', Adresse(67, 'Avicene', 'Rabat'));

où on constate donc que pour un champ non primitif on fait appel au constructeur.
Consultation :

a) Accès à un champ de type objet (adr ici)

SELECT p.nom , p.adr


FROM personnes p;

Résultat:

NOM ADR(NO, RUE, VILLE)


------------------------ ------------------------------------
Untel ADRESSE(56, 'IbnSina', 'Rabat')
Untel2 ADRESSE(67, 'Avicene', 'Rabat')

où la notation SQL est la même (p.adr) mais la valeur adresse apparaît sous forme d'expression constructeur.

(NB. Pour avoir un format de colonnes lisible aisément, utiliser la directive column de SqlPlus :
SQL> column code format 99999
SQL> column nom format a20
SQL> column adr format a50
par exemple)

b) Application de méthode à un champ objet

SELECT p.nom , p.adr.getNo()


FROM personnes p
WHERE p.code = 123;

NOM P.ADR.GETNO()
-------------------- -------------
Untel 56

c) Accès à un attribut de l'objet (ville ici)

SELECT p.nom
FROM Personnes p
WHERE p.adr.ville='Rabat';

NOM
--------------------
Untel
Untel2

d) ORDER BY sur l'attribut objet (se rappeler qu'il y a une méthode MAP définie dessus)

SELECT p.nom , p.adr


FROM personnes p
ORDER BY p.adr DESC;

NOM ADR(NO, RUE, VILLE)


------------------------ ------------------------------------
Untel2 ADRESSE(67, 'Avicene', 'Rabat')
Untel ADRESSE(56, 'IbnSina', 'Rabat')

où on voit que la fonction MAP getNo() a joué le rôle d'ordre sur les adresses selon le champ NO de l'adresse.

Exercices:

a. Vérifier que ORDER BY p.adr.getNo() donne le même résultat. Même sans la clause MAP (qui, en l'occurrence, permet la syntaxe
SQL normale ORDER BY p.adr ).
b. Même requête, mais en ordre décroissant des noms de rue.

2.3. Objet comme schéma de relation (ou comme ligne dans une table)
On reprend le type d'objets personne précédent (exemple-4 ci-dessus), mais avec une adresse de type simple pour le moment.

Exemple-5. Création d'une table d'objets de type personne,

CREATE TABLE Personnes OF Personne;

où Personne est défini par :


CREATE TYPE Personne AS OBJECT (
code number(5),
nom varchar2(30),
ville varchar (20), /* L'adresse est un champ simple ici */

MEMBER FUNCTION getNom RETURN varchar2,


MEMBER PROCEDURE display
);
/

Exercice: programmer le corps de ce type.

Instanciation

INSERT INTO Personnes VALUES (Personne (123, 'Untel' , 'Rabat'));

Noter le paramètre de VALUES : le constructeur Personne.

Consultation

a) Usage des champs de l'objet comme attributs de la table

SELECT p.nom
FROM Personnes p
WHERE p.ville = 'Rabat';

NOM
------------------------------
Untel

Ici, le SELECT est "surchargé" pour s'appliquer à un champ d'objet au lieu d'attribut de table. L'objet p est considéré comme un tuple.
Exercices : Rajouter d'autres tuples (Oups! objets) à cette table. Essayer d'autres requêtes de votre choix.

b) Usage de VALUE() qui convertit un alias SQL de tuple vers un objet

SELECT VALUE(p)
FROM Personnes p;

VALUE(P)(CODE, NOM, VILLE)


----------------------------------------------
PERSONNE(123, 'Untel', 'Rabat')

Noter le schéma d'affichage du résultat comme type d'objet. Noter aussi la notation constructeur utilisée pour l'affichage des valeurs. (Voir
requête b').

Même requête en plus précis

SELECT VALUE(p).getNom()
FROM Personnes p
WHERE p.ville = 'Rabat';

VALUE(P).GETNOM()
----------------------------------
Untel

Ici, on appelle une méthode sur « l'objet-tuple » résultat. Noter encore le schéma du résultat et le type de valeurs afichées.

Exercice: Quel est le schéma du résultat avec SELECT VALUE(p).nom ?

Noter encore l'abus de notation p.ville au lieu de VALUE(p).ville

b') Usage de la table comme « liste d'attributs »

SELECT *
FROM Personnes p;

CODE NOM VILLE


------- ---------------------- ----------------
123 Untel Rabat

à comparer par rapport à la précédente requête (au niveau du schéma du résultat!).


c) Usage en programmation PL/SQL

DECLARE
moi Personne;
xnom varchar2(20);
BEGIN -- affiche les détails d'une Personne et
SELECT VALUE(p) INTO moi
FROM Personnes p
WHERE VALUE(p).code = 123;
moi.display();
SELECT VALUE(p).nom INTO xnom
FROM Personnes p
WHERE VALUE(p).code = 123;
dbms_output.put_line('Nom = '|| xnom);
END;
/

Code: 123
nom: Untel
Ville: Rabat
Nom = Untel

Procédure PL/SQL terminée avec succès.

On peut voir cette table personnes

1. comme une table à colonne unique où chaque ligne est donc un objet personne auquel on peut appliquer les méthodes définies
(requête b), on utilise VALUE() pour cela, ou bien

2. comme une table où chaque colonne est un des composants (code, nom, ville) de l'objet personne, et sur laquelle on applique les
opérations SQL (requête b').

La différence entre (1) la table Personnes définie sur l'objet Personne, et (2) une table définie directement par un schéma (code, nom,
ville), c'est que dans le premier cas on peut appliquer des méthodes utilisateurs display, getNom, aux "tuples" de la première table. Cela
est utile quand on a des applications PL/SQL avec des objets comme ceux de Java, Python ou C++.

La fonction VALUE() convertit une ligne vers une instance d'objet pour accéder aux membres d'un objet : VALUE(ligne).membre.

Exercices : Insérer d'autres lignes. Exécuter les requêtes suivantes et les comparer. Quel est leur résultat?
select *
from personnes p
order by 1 desc;

SELECT VALUE(p)
FROM Personnes p
order by 1 desc;

SELECT VALUE(p).getNom()
FROM Personnes p
order by 1 desc;

SELECT VALUE(p)
FROM Personnes p
order by nom desc;

La deuxième requête donne l'erreur : "ORA-22950: cannot ORDER objects without MAP or ORDER method". Pourquoi?
Comment réparer pour que cette même requête marche?

Exemple-5-bis. Personne comme objet composé

Même exemple avec adresse comme objet cette fois-ci

CREATE TYPE Personne AS OBJECT (


code number(5),
nom varchar2(30),
adr Adresse,

MEMBER FUNCTION getNom RETURN varchar2,


MEMBER PROCEDURE display
);

CREATE TABLE Personnes OF Personne;


Instanciation

INSERT INTO Personnes VALUES (Personne (123, 'Untel', Adresse(2, 'Atlas', 'Rabat')));

Consultation

On utilisera la notation simplifiée p.nom au lieu de VALUE(p).nom (voir la remarque précédente)

a)

SELECT * FROM personnes;

CODE NOM ADR(NO, RUE, VILLE)


---------- ------------------------------ -----------------------------------------------
123 Untel ADRESSE(2, 'Atlas', 'Rabat')

b)

SELECT p.nom
FROM Personnes p
WHERE p.adr.ville = 'Rabat';

NOM
------------------------------
Untel

2.4. L'Objet Relation


Une relation (au sens ensemble de tuples), peut être considérée comme objet elle-même. Le concept de table Oracle peut servir pour définir ce
nouveau type d'objets:

CREATE TYPE collection AS TABLE OF type

C'est en quelque sorte le type collection de quelque chose.

Exemple-6. Soit le type :

CREATE TYPE Point AS OBJECT (


x NUMBER,
y NUMBER
);
/

Un polygone est une collections de points

CREATE TYPE Polygone AS TABLE OF Point;


/

Ainsi, l'objet polygone est une collection de points.

Un instance de ce type, par exemple un carré qui est une liste de quatre coins, a la forme:

Polygone (Point(0,0), Point(0,1), Point(1,1), Point(1,0) )


// Noter la forme « collection » d'objets « tuples ».

NB. La table de Point n'est pas accessible tel que. D'ailleurs elle n'a pas de nom pour être interrogée. Polygone est un nom de type.

L'intérêt d'un tel objet relation est dans la création de relations imbriquées.

2.4.1. Relations imbriquées

On peut maintenant créer une relation Figures (nom, coins) pour des figures géométriques du type polygonal, avec le nom de figure, carré, ligne,
etc. et la liste des coins; laquelle liste est définie comme relation imbriquée Polygone. Voir figure.

Exemple-7:

nom coins
Carré x y
0 0
0 1
1 1
1 0

x y
ligne 0 0
2 1

... ...

Cette relation est déclarée comme suit :

CREATE TABLE Figures (


nom VARCHAR2(20),
coins Polygone
)

Dans chaque ligne, la valeur de colonne coins est une relation polygone à part.

Une clause d'optimisation est à rajouter à cette déclaration :

NESTED TABLE coins STORE AS TableDePoints;

Cette clause optionnelle NESTED TABLE indique que les différentes (petites) relations imbriquées ne sont pas à stocker individuellement comme
valeur de l'attribut coins, mais en totalité dans une seule relation, appelée à l'occasion TableDePoints. Relation qui ne peut être accessible
telle qu'elle (voir aussi la requête (d) ci-après) . Cette relation stocke donc des objets de type Polygone.

2.4.2. Utilisation

a) Insertion

insert into Figures values( 'ligne',


polygone ( Point(0,0), Point(2,1) ) );

insert into Figures values( 'carre',


polygone ( Point(0,0), Point(0,1), Point(1,1),Point(1,0) ) );

b) Affichage de la table

SELECT *
FROM Figures;

NOM COINS(X, Y)
--------------- -----------------------------------
ligne POLYGONE(POINT(0, 0), POINT(2, 1))
carre POLYGONE(POINT(0, 0), POINT(0, 1),
POINT(1, 1), POINT(1, 0))

c) On obtient les coins d'une ligne par une requête comme:

SELECT coins
FROM figures
WHERE nom='ligne';

COINS(X, Y)
-----------------------------------------------------
POLYGONE(POINT(0, 0), POINT(2, 1))

Noter ici que les valeurs du champ coins retournés sont des objets polygones (et non points).

d) Pour accéder aux valeurs x et y donc, il faut préciser la relation imbriquée désirée dans la clause FROM. Le mot clé THE permet cela en
désignant la relation (imbriquée) comme résultat d'une sous-requête SELECT.

1. « Chercher les cordonnées x et y de carré »

SELECT x, y
FROM THE ( SELECT coins
FROM figures
WHERE nom='carre')

Le SELECT interne cherche "la relations ensemble des coins" d'un carré, pour ensuite en afficher les valeurs x et y par le SELECT externe.

X Y
---------- ----------
0 0
0 1
1 1
1 0

Il faut noter ici que le résultat du SELECT interne doit être unique (mono-tuple), c'est à dire une seule "valeur" relation imbriquée. Sans clause
WHERE ici ou si il y avaient plus d'un carré, cela provoquerait une erreur.

On peut aussi nommer cette relation par un alias.

2. « Chercher les cordonnées x et y d'un une ligne telle que x > y »

SELECT lg.x, lg.y


FROM THE ( SELECT coins
FROM figures
WHERE nom='ligne') lg
WHERE lg.x > lg.y

La relation imbriquée (coins d'une ligne) est nommée lg pour ensuite être interrogée normalement par le SELECT externe. Résultat :

X Y
---------- ----------
2 1

3. « Changer les coordonnées x de carré pour y = 0 »

UPDATE THE(SELECT coins FROM figures


WHERE nom= 'carre')
SET x=x+1
WHERE y=0;

Résultat :

SELECT x, y
FROM THE ( SELECT coins
FROM figures
WHERE nom='carre');

X Y
---------- ----------
1 0
0 1
1 1
2 0

e) Usage de TABLE()...

A TABLE expression enables you to query a collection in the FROM clause like a table. In effect, you join the nested table with the row
that contains the nested table.

La requête suivante donne les coordonnées de chaque figure :

SELECT nom, x, y
FROM figures, TABLE ( figures.coins);

NOM X Y
--------------- ---------- ----------
ligne 0 0
ligne 2 1
carre 0 0
carre 0 1
carre 1 1
carre 1 0

6 rows selected.
La clause FROM contient la relation figures pour chercher le nom, et TABLE(figures.coins) pour avoir la relation imbriquée dans le
même tuple.

Comparer par rapport à la requête b)

TABLE sert à "aplatir" l'attribut coins dans la table figures pour permettre ensuite d'interroger ses données. La table (ou l'alias) figures
doit apparaître à gauche (i.e. avant) de l'expression TABLE. "Left correlation".

e') Les coordonnées de la figure 'ligne' :

SELECT nom, x, y
FROM figures, TABLE ( figures.coins)
WHERE nom='ligne';

NOM X Y
--------------- ---------- ----------
ligne 0 0
ligne 2 1

TABLE sert à "aplatir" l'attribut coins dans la table figures pour permettre ensuite d'interroger ses données. La table (ou l'alias) figures
doit apparaître à gauche (i.e. avant) de l'expression TABLE. "Left correlation".

f) "Chercher le nom des figurent qui passent par l'origine

SELECT nom
FROM figures, TABLE ( figures.coins)
where x = 0 and y = 0;

NOM
---------------
ligne
carre

Devoir à faire: Imaginer un schéma de BD avec des clients qui ont fait des commandes (voir cours 1ère année, relation non normalisée).

1 M
Clients <-------- Fait ----------> Commandes

Imaginer les attributs nécessaires.

Considérer deux cas : (1) une table clients avec pour chacun la liste des commandes (sous forme de table imbriquée), ou (2) deux tables
commandes et clients où chaque commande fait référence au client (voir aussi §2.6 ci-après).

Comparer les deux cas en faisant quelques requêtes, telles que : « le nom du client ayant commandé un produit donné », ou « le total des prix de la
commande d'un client donné ».

2.5. Relation Non First Normal Form


En fait, les objets donnent droit à des relations qui ne sont pas en « première forme normale » dans le sens valeur simple. Dans le cas 2.2, un
domaine de relation est un tuple (Adresse), et dans le cas 2.4, un domaine est de type relation (Polygone, ensemble de tuple).

2.5.1. Type Vecteur VARRAY

Il y a aussi la possibilité plus simple que le cas 2.4 où un domaine est de type vecteurs de valeurs (pas forcément scalaires d'ailleurs). Le type
Oracle prédéfini VARRAY permet cela. Par exemple :

CREATE TYPE ListeNotes AS VARRAY(10) OF NUMBER;


CREATE TABLE Eleve (Nom VARCHAR2(20), Notes ListeNotes);

2.6. Référence à un objet


Une référence à un objet est une valeur qui désigne un objet. Son intérêt réside dans la fait de pouvoir utiliser un objet dans plusieurs endroits sans
le dupliquer (on partage l'objet). Par exemple des commandes faites par un même client, font toutes référence à un même objet client. Un type REF
est prédéfini pour cela. Une valeur REF identifie un objet.

Un identifiant d'objet est appelé OID. Chaque objet ligne dans une table d'objets (§ 2.3) comporte un identifiant d'objet (OID) associé. Oracle
attribue par défaut un identifiant unique généré par le système en tant qu'OID, pour chaque objet stocké comme "ligne" dans une table d'objets.

Exemple-8: on reprend l'exemple-5-bis (type Personne et table Personnes OF Personne) et on rajoute:


CREATE TYPE Departement AS OBJECT (
nom VARCHAR2(20),
chef REF Personne);
/

CREATE TABLE Departements OF Departement;

Une valeur de l'attribut chef dans cette table est une référence (pointeur) vers un objet Personne.

Un objet référencé ainsi doit être un tuple dans une relation comme personnes ci-dessus. Et c'est le seul usage possible d'une référence.

Une référence ne peut donc pas pointer vers une colonne objet d'une table. Pas plus que vers un objet indépendant créé à cet effet.

Exemple-9: Pour ajouter une ligne dans la table Departements, on peut écrire:

INSERT INTO Departements


SELECT 'Informatique', REF (p)
FROM Personnes p
WHERE p.nom='Untel';

qui insère le département Informatique avec comme chef une référence à "Untel", calculée par REF(p) où p est la ligne désirée de la table
Personnes.

Après le SELECT imbriqué, le système génère un OID pour la tuple p trouvé, et le met comme valeur dans chef. On peut le constater avec la
requête:

SELECT * FROM Departements; (a)

NOM CHEF
---------------- ---------------------------------------------------------------------------
Informatique 00002202080FADD5A1C0614C8B9CD2773D8A7EF04865CA185E26644911AA00793E3AAFE722

L'OID est bien sûr non significatif pour l'utilisateur.

Noter enfin, que créer une instance quelconque pour l'affecter à un objet REF , comme dans:

INSERT INTO Departements VALUES ('Informatique',


Personne (123, 'Untel', Adresse(2, 'Atlas', 'Rabat' )));

aurait été une erreur, car la référence se fait vers un objet qui n'est pas un tuple dans une table (ORA-00932: types de données
incohérents).

Exercice: faire la même insertion en utilisant UPDATE. (indication: Insérer au préalable le tuple ('informatique', NULL) et ensuite
remplacer la valeur NULL par l'objet qu'il faut.)

2.6.1. Autres requêtes

Exemple-9-bis: Pour suivre une référence et accéder à un de ses composants (ex. chef.nom), on utilise aussi la notation classique comme si
l'attribut n'était pas une référence (la déréférence est automatique)

SELECT nom, d.chef.nom


FROM Departements d;

NOM CHEF.NOM
-------------------- ------------------------------
Informatique Untel

Par contre, pour l'attribut chef en tant qu'objet, il faut utiliser l'opérateur DEREF pour déréférencer le pointeur (sinon, on obtient l'OID comme
exemple-9 ci-dessus)

SELECT nom, DEREF(d.chef)


FROM Departements d;

NOM DEREF(D.CHEF) (CODE, NOM, ADR(NO, RUE, VILLE))


------------------ -------------------------------------------------------------
Informatique PERSONNE(123, 'Untel', ADRESSE(56, 'IbnSina', 'Rabat'))

Exemple-10: Solution exercice précédent:

INSERT INTO Departements VALUES ('Mecanique', NULL);


UPDATE Departements
SET chef = (SELECT REF(p) FROM personnes p WHERE nom='Untel2')
WHERE nom = 'Mecanique'

Noter le REF(p)!

La requête suivante montre la table après cette insertion/mise-à-jour.

SELECT * FROM Departements;

NOM CHEF
------------------- ---------------------------------------------------------------------------
Informatique 00002202080FADD5A1C0614C8B9CD2773D8A7EF04865CA185E26644911AA00793E3AAFE722
Mecanique 00002202089E09671DCA464C7B902A03146C283AC165CA185E26644911AA00793E3AAFE722

2.6.2. Les références en PL/SQL

Il y a des variables REF en PL/SQL

Exemple-11:

SQL> DECLARE
2 moi Personne;
3 ref_moi ref Personne;
4 xnom varchar2(20);
5
6 BEGIN
7 SELECT d.chef.nom INTO xnom
8 FROM Departements d
9 WHERE d.nom ='Mecanique';
10 dbms_output.put_line('Nom = '|| xnom);
11
12 SELECT d.chef INTO ref_moi
13 FROM Departements d
14 WHERE d.nom ='Mecanique';
15
16 SELECT deref(ref_moi) into moi FROM dual;
17 moi.display;
18
19 END;
20 /
Nom = Untel2
Code: 124
nom: Untel2
Ville: Rabat

Procédure PL/SQL terminée avec succès.

SQL>

A la ligne 3, on déclare une variable ref_moi de type REF Personne.

A la ligne 7, on a un SELECT mono-tuple qui suit l'attribut chef sans déréférence. Comme dans l'exemple-9.

A la ligne 12, on a un SELECT mono-tuple sur l'attribut chef pour l'affecter à la variable ref_moi.

Pour appliquer la méthode display à ce chef (ligne 17), il faut déréférencer la variable ref_moi. C'est l'objet du SELECT de la ligne 16, qui
affecte le résultat à la variable moi.

2.7. Vues Objet/Relation

Les vues objets permettent l'utilisation de données relationnelles dans les applications orientées objets. Elles permettent de voir des données
relationnelles existantes dans un modèle objet-relationnel.

Tout comme une vue est une table virtuelle, une vue objet est une table d'objets virtuels.

Exemple-12: On reprend la table Employee ( enum, ename, salary, address, dept) de BD Store (cf
http://www.emi.ac.ma/~ntounsi/COURS/DB/Polys/SQL/SQL-LMD.html ).

On va d'abord créer un type d'objets Employee_type , et ensuite on va définir une table d'objets virtuels de ce type.
a) Création du type et de son implémentation
CREATE OR REPLACE TYPE Employee_type AS OBJECT (
enum varchar (5) ,
ename varchar (20),
salary decimal (9,2),
address varchar(10),
dept varchar (5),
MEMBER PROCEDURE display
);
/

CREATE OR REPLACE TYPE BODY Employee_type AS


MEMBER PROCEDURE display IS
BEGIN
dbms_output.put_line('Num Employee: '|| TO_CHAR(enum));
dbms_output.put_line('Nom: '|| ename);
dbms_output.put_line('Salaire: '|| salary);
dbms_output.put_line('Adresse: '|| address);
END;
END;
/

b) Création de la vue-objet correspondante (pour des employés du département 'D1'):

CREATE VIEW EmpView OF employee_type WITH OBJECT IDENTIFIER (enum) AS


SELECT e.enum, e.ename, e.salary, e.address, e.dept
FROM employee e
WHERE dept = 'D1';

Noter la construction de l'identificateur d'objet sur la base de la clé spécifiée (enum), qui est la clé primaire de la table de base.

c) Interrogation en SQL
SQL> select * from empview;

ENUM ENAME SALARY ADDRESS DEPT


----- -------------------- ---------- ---------- -----
E6 Aziz 8500 Casa D1
E2 Ahmed 6000 Casa D1
E1 Ali 8000 Rabat D1

SQL>

d) Utilisation en PL/SQL
SQL> DECLARE
2 emp employee_type;
3 BEGIN
4 SELECT value(e) INTO emp
5 FROM empview e
6 WHERE e.enum='E6';
7 emp.display;
8 end;
9 /
Num Employee: E6
Nom: Aziz
Salaire: 8500
Adresse: Casa

Procédure PL/SQL terminée avec succès.

SQL>

Les objets virtuels ne sont pas forcément composés des mêmes champs que la table de base d'origine. On aurait pu définir le type
Employee_type ci-dessus uniquement sur les champs nom et adresse d'un employé.

3. En Résumé
Résumé de cette approche Objet/Relationnel.

4. Correction de certains exercices


Exercices du § 1.1

SQL> CREATE OR REPLACE TYPE Adresse AS OBJECT (


2 no NUMBER,
3 rue VARCHAR2(25),
4 ville VARCHAR2(20),
5 MAP MEMBER FUNCTION getNo RETURN NUMBER,
6 MEMBER PROCEDURE display,
7 MEMBER PROCEDURE changeRueAdresse ( r IN VARCHAR2 ),
8 MEMBER PROCEDURE changeAdresse ( a IN OUT NOCOPY Adresse ),
9 CONSTRUCTOR FUNCTION Adresse (a IN OUT NOCOPY Adresse)
10 RETURN SELF AS RESULT
11 );
12 /

Type created.

SQL> CREATE OR REPLACE TYPE BODY Adresse AS


2
3 /* Implémentation fonction d'accès */
4 MAP MEMBER FUNCTION getNo RETURN NUMBER IS
5 BEGIN
6 RETURN no;
7 END;
8
9 /* Implémentation méthode d'affichage display */
10 MEMBER PROCEDURE display IS
11 BEGIN
12 dbms_output.put_line('Maison No: '|| TO_CHAR(no));
13 dbms_output.put_line('Rue: '|| rue);
14 dbms_output.put_line('Ville: '|| ville);
15 END;
16
17 /* Méthode changeRueAdresse */
18 MEMBER PROCEDURE changeRueAdresse ( r IN VARCHAR2 ) IS
19 BEGIN
20 rue := r;
21 END;
22
23 /* Méthode changeAdresse */
24 MEMBER PROCEDURE changeAdresse ( a IN OUT NOCOPY Adresse ) IS
25 BEGIN
26 no := a.no;
27 rue := a.rue;
28 ville := a.ville;
29 END;
30
31 /* Constructeur */
32 CONSTRUCTOR FUNCTION Adresse (a IN OUT NOCOPY Adresse)
33 RETURN SELF AS RESULT IS
34 BEGIN
35 no := a.no;
36 rue := a.rue;
37 ville := a.ville;
38 return;
39 END;
40
41 END;
42 /

Type body created.

SQL>
SQL>
SQL>
SQL> DECLARE
2 maison Adresse; m Adresse;
3 BEGIN
4 maison := Adresse(2, 'Atlas', 'Rabat');
5 maison.display();
6
7 maison.changeRueAdresse ('Rif');
8 maison.display();
9
10 m := Adresse(3, 'Rif', 'Casa');
11 maison.changeAdresse (m);
12 maison.display();
13
14 m.changeRueAdresse('Alpes');
15 maison := Adresse(m); /* vs. maison.Adresse(m) */
16 maison.display();
17 END;
18 /
Maison No: 2
Rue: Atlas
Ville: Rabat
Maison No: 2
Rue: Rif
Ville: Rabat
Maison No: 3
Rue: Rif
Ville: Casa
Maison No: 3
Rue: Alpes
Ville: Casa

PL/SQL procedure successfully completed.

SQL>

Corrigé contrôle Janvier 2019.

S-ar putea să vă placă și