13 Conception dans un
contexte relationnel
Réalisation de la persistance
Constantes du programme d ’application
Fichiers (API du SGF)
Librairies spécialisées (API de haut- niveau)
SGBD
– souplesse, robustesse, intégrité,…
– approche privilégiée dans ce chapitre
SGBD relationnel SQL centralisé
Forces du relationnel
Accessibilité large
Maturité de la technologie
Fondements théoriques solides
Norme répandue (SQL)
Support solide de l'intégrité
Outils de développement
Processus de conception de BD
M o d è l e c o n c e p t u e l d e d o n n é e s
C o n c e p t i o n l o g i q u e
C o n c e p t i o n p h y s i q u e ( s c h é m a i n t e r n e e t r é v i s i o n d u
n i v e a u c o n c e p t u e l ) S c h é m a B D ( n i v e a u c o n c e p t u e l )
M o d è l e p h y s i q u e d e d o n n é e s ( s c h é m a c o n c e p t u e l e t
R é v i s i o n
C o n c e p t i o n
13.1 Mise en œuvre des contraintes d'intégrité en SQL
Enjeux client ou serveur
– Robustesse
– Interactivité
– Performance
Représentation UML : Data Modeling Profile
TABLE
–
classes stéréotypées « TABLE »
PRIMARY KEY
–
étiquette PK devant les colonnes
–
une opération stéréotypée « PK »
UNIQUE
–
opération stéréotypée « UNIQUE »
FOREIGN KEY :
–
une étiquette FK devant les colonnes
–
opération stéréotypée « FK »
–
association de un à plusieurs
association stéréotypée « Identifying » pour association identifiante
CHECK
–
opération stéréotypée « CHECK »
TRIGGER
–
opération stéréotypée « TRIGGER »
13.2 Représentation des
contraintes d'intégrité en SQL
Voir chap. 6 pour syntaxe SQL
Deux catégories
– Contrainte d'intégrité statique
respectée pour chacun des états de la BD
– Contrainte d'intégrité dynamique
contrainte sur changements d'états
Shéma relationnel pour SyLeRat
Membre {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) téléphoneRésidence : VARCHAR(15)
<<table>>
PrêtEnCours {Clé primaire : idExemplaire}
idExemplaire : VARCHAR(10) datePrêt : DATE
idUtilisateur : VARCHAR(10)
<<table>>
PrêtArchivé {Clé primaire : noSequence}
noSequence : INTEGER {dateRetour >= datePrêt}
dateRetour : DATE datePrêt : DATE
idUtilisateur : VARCHAR(10) idExemplaire : VARCHAR(10)
<<table>>
Employé {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) {Clé candidate : codeMatricule}
codeMatricule : CHAR(6)
catégorieEmployé : DomaineCatégorieEmployé
<<table>>
Catégorie {Clé primaire : code}
code : VARCHAR(10) descripteur : VARCHAR(20) codeParent : VARCHAR(10)
<<table>>
Auteur {Clé primaire : noSequence}
noSequence : INTEGER nom : VARCHAR2(20) prénom : VARCHAR2(20)
<<table>>
Editeur {Clé primaire : nomEditeur}
nomEditeur : VARCHAR(20) ville : VARCHAR(20)
<<table>>
Livre {Clé primaire : ISBN}
ISBN : CHAR(13) titre : VARCHAR(50)
annéeParution : DomaineAnnée nomEditeur : VARCHAR(20) code : VARCHAR(10)
<<table>>
Exemplaire {Clé primaire : idExemplaire}
idExemplaire : VARCHAR(10) dateAchat : Date
statut : DomaineStatut ISBN : CHAR(13)
<<table>>
Utilisateur {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) motPasse : VARCHAR(10) nom : VARCHAR2(20) prénom : VARCHAR2(20)
/ catégorieUtilisateur : DomaineCatégorieUtilisateur
<<table>>
DomaineAnnée {INTEGER CHECK value > 0 }
<<domain>>
MembreGénéral {Clé primaire : noSequence}
noSequence
nbMaxPrêts : INTEGER = 5 duréeMaxPrêts : INTEGER = 7
<<table>>
DomaineCatégorieEmployé {VARCHAR(15) CHECK value IN ('bibliothécaire','commis')}
<<domain>>
DomaineStatut
{VARCHAR(15) CHECK value IN ('prêté','disponible','retiré')}
<<domain>>
AuteurLivre {Clé primaire : noSequence, ISBN}
noSequence : INTEGER
{Clé candidate : ISBN, ordreAuteur}
ISBN : CHAR(13) ordreAuteur : INTEGER
<<table>>
{Un Auteur ne peut exister sans AuteurLivre}
{Un Livre ne peut exister sans AuteurLivre}
{Un Editeur ne peut exister
{Un Livre ne peut exister sans Exemplaire}
{Exclusives et une des deux est nécessaire}
{CatégorieUtilisateur doit être cohérente avec la table d'appartenance}
{Le nombre de PrêtEnCours d'un Membre <= nbMaxPrêts}
{statut = 'prêté' si et seulement si un PrêtEnCours fait référence à l'Exemplaire}
13.1.1 Contraintes statiques
Mécanismes déclaratifs (à privilégier)
–
PRIMARY KEY, UNIQUE, NOT NULL, DOMAIN, FOREIGN KEY, CHECK, ASSERTION
Procédural
–
TRIGGER (SQL3)
plus complexe à coder
– plus sujet à erreur
parfois plus performant
parfois inévitables (limite des mécanismes déclaratifs)
13.1.1.1 Contrainte sur le domaine d'une colonne
CREATE DOMAIN DomaineCatégorieUtilisateur AS
VARCHAR(14) CHECK(VALUE IN ('employé', 'membre')) CREATE TABLE Utilisateur
(idUtilisateur VARCHAR(10) NOT NULL,
motPasse VARCHAR(10) NOT NULL,
nom VARCHAR(20) NOT NULL,
prénom VARCHAR(20) NOT NULL,
catégorieUtilisateur DomaineCatégorieUtilisateur NOT NULL, PRIMARY KEY (idUtilisateur)
)
DomaineCatégorieUtilisateur
{VARCHAR(14) CHECK value IN ('employé, 'membre')}
<<domain>>
Utilisateur {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) motPasse : VARCHAR(10) nom : VARCHAR2(20) prénom : VARCHAR2(20)
/ catégorieUtilisateur : DomaineCatégorieUtilisateur
<<table>>
13.1.1.2 Contrainte de clé primaire
CREATE TABLE Utilisateur
(idUtilisateur VARCHAR(10) NOT NULL,
…
PRIMARY KEY (idUtilisateur) )
Utilisateur {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) motPasse : VARCHAR(10) nom : VARCHAR2(20) prénom : VARCHAR2(20)
/ catégorieUtilisateur : DomaineCatégorieUtilisateur
<<table>>
Contrainte de clé candidate
CREATE TABLE Employé
(idUtilisateur VARCHAR(10) NOT NULL, codeMatricule CHAR(6) NOT NULL,
catégorieEmployé DomaineCatégorieEmployé NOT NULL, PRIMARY KEY (idUtilisateur),
UNIQUE (codeMatricule),
FOREIGN KEY (idUtilisateur) REFERENCES Utilisateur )
Employé {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) {Clé candidate : codeMatricule}
codeMatricule : CHAR(6)
catégorieEmployé : DomaineCatégorieEmployé
<<table>>
Contrainte d'intégrité référentielle
Employé {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) {Clé candidate : codeMatricule}
codeMatricule : CHAR(6)
catégorieEmployé : DomaineCatégorieEmployé
<<table>>
Utilisateur {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) motPasse : VARCHAR(10) nom : VARCHAR2(20) prénom : VARCHAR2(20)
/ catégorieUtilisateur : DomaineCatégorieUtilisateur
<<table>>
CREATE TABLE Employé
(idUtilisateur VARCHAR(10) NOT NULL, codeMatricule CHAR(6) NOT NULL,
catégorieEmployé DomaineCatégorieEmployé NOT NULL, PRIMARY KEY (idUtilisateur),
UNIQUE (codeMatricule),
FOREIGN KEY (idUtilisateur) REFERENCES Utilisateur )
Politique NO ACTION par défaut
Contrainte intra-ligne
CREATE TABLE PrêtArchivé
(noSequence INTEGER NOT NULL,
datePrêt DATE NOT NULL,
dateRetour DATE NOT NULL,
idUtilisateur VARCHAR(10) NOT NULL, idExemplaire VARCHAR(10) NOT NULL, PRIMARY KEY (noSequence),
FOREIGN KEY (idUtilisateur) REFERENCES Utilisateur, FOREIGN KEY (idExemplaire) REFERENCES Exemplaire, CHECK (dateRetour >= datePrêt)
)
PrêtArchivé
{Clé primaire : noSequence}
noSequence : INTEGER {dateRetour >= datePrêt}
dateRetour : DATE datePrêt : DATE
idUtilisateur : VARCHAR(10) idExemplaire : VARCHAR(10)
<<table>>
Contraintes statiques complexes
CHECK avec SELECT enchâssé
– non supporté par Oracle
ASSERTION
– non supporté par Oracle
TRIGGER
{Un Editeur ne peut exister sans
qu'un Livre y fasse référence}
{Un Editeur ne peut exister sans qu'un Livre y fasse référence}
CREATE TABLE Editeur
(nomEditeur VARCHAR(20) NOT NULL,
ville VARCHAR(20) NOT NULL,
PRIMARY KEY (nomEditeur),
CHECK nomEditeur IN (SELECT nomEditeur FROM Livre) DEFERRABLE INITIALLY DEFERRED
)
{Un Editeur ne peut exister sans
qu'un Livre y fasse référence}
{Le nombre de PrêtsEnCours d'un Membre
<=nbMaxPrêts (de la table MembreGénéral)}
Membre {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10)
téléphoneRésidence : VARCHAR(15)
<<table>>
{Le nombre de PrêtEnCours d'un Membre <= nbMaxPrêts}
MembreGénéral {Clé primaire : noSequence}
noSequence
nbMaxPrêts : INTEGER = 5 duréeMaxPrêts : INTEGER = 7
<<table>>
{Le nombre de PrêtsEnCours d'un Membre
<=nbMaxPrêts (de la table MembreGénéral)}
Membre {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10)
téléphoneRésidence : VARCHAR(15)
<<table>>
{Le nombre de PrêtEnCours d'un Membre <= nbMaxPrêts}
MembreGénéral {Clé primaire : noSequence}
noSequence
nbMaxPrêts : INTEGER = 5 duréeMaxPrêts : INTEGER = 7
<<table>>
Réalisation avec TRIGGER
PrêtEnCours
{Clé primaire : idExemplaire}
idExemplaire : VARCHAR(10) datePrêt : DATE
idUtilisateur : VARCHAR(10)
<<table>>
{statut = 'prêté' si et seulement si un Prêt EnCours fait référence à l'Exemplaire}
Exemplaire
{Clé primaire : idExemplaire}
idExemplaire : VARCHAR(10) dateAchat : Date
statut : DomaineStatut ISBN : CHAR(13)
<<table>>
CREATE ASSERTION ContrainteStatutPrêté CHECK
((NOT EXISTS
((SELECT idExemplaire FROM Exemplaire
WHERE statut = 'prêté') EXCEPT (SELECT idExemplaire
FROM PrêtEnCours))) AND (NOT EXISTS
((SELECT idExemplaire
FROM PrêtEnCours) EXCEPT (SELECT idExemplaire
FROM Exemplaire
WHERE statut = 'prêté'))))
DEFERRABLE INITIALLY DEFERRED
Par TRIGGER : forcer le changement de statut
CREATE TRIGGER AIPrêtEnCoursModifierStatutExemplaire AFTER INSERT ON PrêtEnCours
REFERENCING
NEW ROW AS ligneAprès FOR EACH ROW
BEGIN
UPDATE Exemplaire
SET statut = 'prêté'
WHERE idExemplaire = ligneAprès.idExemplaire;
END
CREATE TRIGGER ADPrêtEnCoursModifierStatutExemplaire AFTER DELETE ON PrêtEnCours
REFERENCING
OLD ROW AS ligneAvant FOR EACH ROW
BEGIN
UPDATE Exemplaire
SET statut = 'disponible'
WHERE idExemplaire = ligneAvant.idExemplaire;
Membre {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10)
téléphoneRésidence : VARCHAR(15)
<<table>>
Employé {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) {Clé candidate : codeMatricule}
codeMatricule : CHAR(6)
catégorieEmployé : DomaineCatégorieEmployé
<<table>>
Utilisateur {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) motPasse : VARCHAR(10) nom : VARCHAR2(20) prénom : VARCHAR2(20)
/ catégorieUtilisateur : DomaineCatégorieUtilisateur
<<table>>
{Exclusives et une des deux est nécessaire}
{CatégorieUtilisateur doit être cohérente avec la table d'appartenance}
CREATE ASSERTION ContrainteGénéralisationUtilisateur CHECK
((NOT EXISTS (SELECT * FROM Utilisateur, Employé
WHERE catégorieUtilisateur = ‘membre’
AND Utilisateur.idUtilisateur = Employé.idUtilisateur)) AND
(NOT EXISTS (SELECT *
FROM Utilisateur, Membre
WHERE catégorieUtilisateur = ‘employé’
AND Utilisateur.idUtilisateur = Employé.idUtilisateur)) AND
(NOT EXISTS
((SELECT idUtilisateur FROM Utilisateur) EXCEPT
((SELECT idUtilisateur FROM Employé)
UNION (SELECT idUtilisateur FROM MEMBRE)))))
{CatégorieUtilisateur doit être cohérente avec la table d'appartenance} par TRIGGER
CREATE TRIGGER AIEmployéModifierCatégorie AFTER INSERT ON Employé
REFERENCING
NEW ROW AS ligneAprès FOR EACH ROW
BEGIN
UPDATE Utilisateur
SET catégorieUtilisateur = 'employé'
WHERE idUtilisateur = ligneAprès.idUtilisateur;
END
CREATE TRIGGER AIMembreModifierCatégorie AFTER INSERT ON Membre
REFERENCING
NEW ROW AS ligneAprès FOR EACH ROW
BEGIN
UPDATE Utilisateur
SET catégorieUtilisateur = 'membre'
WHERE idUtilisateur = ligneAprès.idUtilisateur;
END
{Exclusives et une des deux est nécessaire} par CHECK
CREATE TABLE Utilisateur
(idUtilisateur VARCHAR(10) NOT NULL, motPasse VARCHAR(10) NOT NULL,
nom VARCHAR(10) NOT NULL,
prénom VARCHAR(10) NOT NULL,
catégorieUtilisateur DomaineCatégorieUtilisateur NOT NULL, PRIMARY KEY (idUtilisateur),
CHECK
((idUtilisateur IN (SELECT idUtilisateur FROM Employé) OR idUtilisateur IN (SELECT idUtilisateur FROM Membre)) AND
NOT (idUtilisateur IN (SELECT idUtilisateur FROM Employé) AND idUtilisateur IN (SELECT idUtilisateur FROM Membre)))
DEFERRABLE INITIALLY DEFERRED )
Impossible avec TRIGGER non DEFERRABLE
13.1.2 Contraintes dynamiques
Interdit de faire un prêt à un Membre qui possède un retard
CREATE TRIGGER BIPrêtEnCoursVérifierRetard BEFORE INSERT ON PrêtEnCours
REFERENCING
NEW ROW AS ligneAprès FOR EACH ROW
WHEN
((EXISTS
(SELECT * FROM MEMBRES
WHERE idUtilisateur = lignesAprès.idUtilisateur))AND (EXISTS
(SELECT * FROM PrêtEnCours, MembreGénéral
WHERE ligneAprès.idUtilisateur = idUtilisateur AND (CURRENT_DATE - datePrêt) > duréeMaxPrêts)))
BEGIN
souleverUneException;
END
{le statut de l'Exemplaire doit être disponible avant qu'un prêt ne soit effectué}
CREATE TRIGGER BIPrêtEnCoursVérifierDisponibilité BEFORE INSERT ON PrêtEnCours
REFERENCING
NEW ROW AS ligneAprès FOR EACH ROW
WHEN (EXISTS
(SELECT * FROM Exemplaire
WHERE ligneAprès.idExemplaire = idExemplaire AND statut <> 'disponible'))
BEGIN
souleverUneException;
END
Archivage des prêts lors d'un retour
CREATE TRIGGER ADPrêtEnCoursArchiverPrêt AFTER DELETE ON PrêtEnCours
REFERENCING
OLD ROW AS ligneAvant FOR EACH ROW
BEGIN
INSERT INTO PrêtArchivé VALUES
SELECT MAX(noSequence)+1, ligneAvant.datePrêt, CURRENT_DATE, ligneAvant.idUtilisateur,
ligneAvant.idExemplaire FROM PrêtArchivé;
END
7.1.9 Exemple de schéma SQL pour SyLeRat
Eviter la circularité des définitions
CREATE TABLE Utilisateur (…,
CHECK
((idUtilisateur IN (SELECT idUtilisateur FROM Employé) OR idUtilisateur IN (SELECT idUtilisateur FROM Membre)) AND
NOT (idUtilisateur IN (SELECT idUtilisateur FROM Employé) AND idUtilisateur IN (SELECT idUtilisateur FROM Membre)))
DEFERRABLE INITIALLY DEFERRED )
CREATE TABLE Employé (…,
FOREIGN KEY (idUtilisateur) REFERENCES Utilisateur )
CREATE TABLE Membre (…,
FOREIGN KEY (idUtilisateur) REFERENCES Utilisateur
)
Utilisation du ALTER TABLE
CREATE TABLE Utilisateur (…
)
CREATE TABLE Employé (…,
FOREIGN KEY (idUtilisateur) REFERENCES Utilisateur )
CREATE TABLE Membre (…,
FOREIGN KEY (idUtilisateur) REFERENCES Utilisateur )
ALTER TABLE Utilisateur
ADD CONSTRAINT contrainteGénéralisationComplèteDisjointe CHECK
((idUtilisateur IN (SELECT idUtilisateur FROM Employé) OR idUtilisateur IN (SELECT idUtilisateur FROM Membre)) AND
NOT (idUtilisateur IN (SELECT idUtilisateur FROM Employé) AND idUtilisateur IN (SELECT idUtilisateur FROM Membre)))
DEFERRABLE INITIALLY DEFERRED
Combinaison de TRIGGER
CREATE TRIGGER BIPrêtEnCoursVérifierNbMaxPrêts BEFORE INSERT ON PrêtEnCours
REFERENCING
NEW ROW AS ligneAprès FOR EACH ROW
WHEN ((EXISTS
(SELECT * FROM MEMBRES
WHERE idUtilisateur = lignesAprès.idUtilisateur)) AND
… END
CREATE TRIGGER BIPrêtEnCoursVérifierRetard BEFORE INSERT ON PrêtEnCours
REFERENCING
NEW ROW AS ligneAprès FOR EACH ROW
WHEN ((EXISTS
(SELECT * FROM MEMBRES
WHERE idUtilisateur = lignesAprès.idUtilisateur))AND
… END
CREATE TRIGGER BIPrêtEnCoursVérifierDisponibilité BEFORE INSERT ON PrêtEnCours
…
Contrôler l'ordre d'exécution
Améliorer la performance
CREATE TRIGGER BIPrêtEnCoursVérifierNbMaxRetardDisponibilité BEFORE INSERT ON PrêtEnCours
REFERENCING
NEW ROW AS ligneAprès FOR EACH ROW
WHEN
(((EXISTS
(SELECT * FROM MEMBRES
WHERE idUtilisateur = lignesAprès.idUtilisateur)) AND
((EXISTS
(SELECT * FROM PrêtEnCours, MembreGénéral
WHERE ligneAprès.idUtilisateur = idUtilisateur AND (CURRENT_DATE - datePrêt) > duréeMaxPrêts))
OR
((SELECT COUNT(*) FROM PrêtEnCours
WHERE ligneAprès.idUtilisateur=PrêtEnCours.idUtilisateur) >=
(SELECT nbMaxPrêts FROM MembreGénéral)) )
) OR
(EXISTS
(SELECT * FROM Exemplaire
WHERE ligneAprès.idExemplaire = idExemplaire AND statut <> 'disponible'))
) BEGIN
souleverUneException;
END
Une seule fois !
N.B. Contrôle plus fin avec PL/SQL
13.2 Conception du schéma relationnel au niveau externe
Ce que l ’utilisateur voit
Sécurité
– utilisateurs, rôles, privilèges
– partir des cas d ’utilisation
Tables virtuelles
13.2.1 Tables virtuelles
Mécanisme d ’encapsulation
– isoler l ’utilisateur des détails du schéma
– indépendance logique des données
Mécanisme de sécurité
– privilège d ’accès à la table
virtuelle seulement
Exemple d ’encapsulation
CREATE VIEW VueMembre AS
SELECT Utilisateur.idUtilisateur, motPasse, nom, prénom, téléphoneRésidence
FROM Utilisateur, Membre
WHERE Membre.idUtilisateur = Utilisateur.idUtilisateur
CREATE VIEW VueEmployé AS
SELECT Utilisateur.idUtilisateur, motPasse, nom, prénom, codeMatricule, catégorieEmployé
FROM Utilisateur, Employé
WHERE Employé.idUtilisateur = Utilisateur.idUtilisateur
CREATE VIEW Prêt AS
(SELECT NULL, datePrêt, NULL, idUtilisateur, idExemplaire FROM PrêtEnCours)
UNION
(SELECT * FROM PrêtArchivé)
Exemple de mécanisme de sécurité
CREATE VIEW MesPrêts AS SELECT *
FROM Prêts
WHERE idUtilisateur = CURRENT_USER
13.2.2 Réalisation des contraintes de sécurité
Sécurité par les données
–
acteur associé à un authorizationID SQL
GRANT les privilèges nécessaires
sécurité appliquée de manière globale (indépendante de l’application)
– traçabilité (audit au niveau SGBD)
–
pooling de connexion impossible avec les connexions JDBC normales
Sécurité par les traitements
–
identificateur d'utilisateur et mot de passe gérés par le programme d'application
–
indépendamment des mécanismes SQL
–
connexion avec authorizationID SQL omnipotent
–
danger accrue en cas de sécurité brisée
–
pas de traçabilité directe à l’acteur
–
Oracle : session légère et authentification proxy pour pooling
Sécurité avancée
Option Oracle Advanced Secutity
– Cryptage bout en bout (réseau)
– Authentification externe
Kerberos, SSL, certificats, biométrique,…
Single Sign-On (SSO)
–
par Oracle Internet Directory (LDAP v3)
Virtual private database
– Plusieurs BD dans une DATABASE
Oracle Label Security
– Étiquetage des lignes par niveaux de sécurité
– e.g. public, secret, top-secret
Exemple par les données
Acteur (cas d ’utilisation) : ROLE SQL
–
ROLE RôleAdministrateurSystème
accès à tout
–
ROLE RôleCommisAuPrêt
tout en lecture (SELECT)
Membre, Utilisateur, PrêtEnCours, PrêtArchivé, Exemplaire en écriture (INSERT, DELETE, UPDATE)
–ROLE RôleMembre
–
...
GRANT ROLE xxx to authorizationID
NB Pas de motDePasse dans table Utilisateur !
Utilisation de TRIGGER
Le prêt est interdit 1h00 et 7h00
CREATE TRIGGER BIPrêtEnCoursVérifierHeure BEFORE INSERT ON PrêtEnCours
FOR EACH ROW
WHEN (CURRENT_TIME > TIME '01:00:00') AND (CURRENT_TIME < TIME '07:00:00')
BEGIN
souleverUneException;
END
13.3 Niveau interne du schéma relationnel : conception physique
Input
–
schéma (niveau conceptuel) initial
–
estimations des volumes de données
–
architecture
contraintes SGBD
contraintes plate-forme
–
charge de travail
exigences de performance
Output
–
le « meilleur » schéma interne
solution approchée
13.3.1 Conception physique dans un contexte centralisé
Organisations primaires
Organisations
secondaires
Organisation primaire
Fichiers et paramètres d'allocation d'espace
– quel disque/site
architectures RAID
– taille de bloc
– taille de fichier
fixe
allocation dynamique
–
taille des granules
Organisation primaire (suite)
Paramètres d'allocation d'espace aux tables
– quels fichiers ?
– taille des granules
– homogène / hétérogène
– colonne externe ? (multimédia)
chemin vers fichier externe
URL
Organisation primaire (suite)
Paramètres du mécanisme d'allocation d'espace aux lignes
–
position dans le fichier
allocation sérielle, grappe, index primaire, hachage
réservation d ’espace
paramètres spécifiques à l ’organisation
–
enregistrements
taille fixe/variable
chevauchant ou non
compression
cryptage
–
gestion des identifiants d ’enregistrement (IDE)
Organisations secondaires
Chemins d ’accès supplémentaires
– référence par IDE
Index secondaire
Organisation multidimensionnelle
Listes
– SGBD réseau
Collections de références
– SGBD objet
13.3.2 Le processus de conception
Intrants
– schéma relationnel au niveau conceptuel
– volume des tables
estimation difficile
– charge de travail
partir des cas d ’utilisation
Sortie
– schéma interne « optimal »
Problème d ’optimisation complexe
Enjeux conflictuels
– lecture/écriture, espace/temps
Grande quantité de paramètres
Mesures approximatives
Evolution des paramètres
Solution approchée
– heuristiques
Surveillance et mise au point
13.3.3 Heuristiques de conception
Sous-ensemble des opérations
– en ordre d ’ importance
Négliger le coût des mises à jour
Négliger le coût en espace mémoire
Méthode gloutonne
Méthode gloutonne
Initialiser schémaInterne
–
e.g. organisation sérielle pour chacune des tables
Pour chacune des opération
i(en ordre de priorité)
–
trouver meilleur plan P pour opération
iselon schémaInterne
–
chercher amélioration de schémaInterne
qui permet de produire un meilleur plan P ’
sans pénaliser opération
1… opération
i-1–
si P ’ meilleur que P
schémaInterne := schémaInterne amélioré
Comment améliorer ?
–
heuristiques d ’amélioration
13.3.4 Sélection par égalité
SELECT ...
FROM T
WHERE X = uneValeurDeX
Heuristique
–
Si
bonne sélectivité de X
volume de données suffisant
–
1. Hachage sur X
–
2. Index primaire ou index groupant sur X
–
3. Index secondaire sur X
–
N.B. Conditions particulières
Exemple glouton
>>> sélectivité de ISBN
>>> volume de données
TempsES (S=H) = 11ms
–
volume assez stable
TempsES (S=IP) = 44ms
–
sélection par intervalle non pertinente
TempsES (S=IS) = 55ms
Opération de priorité 1:
SELECT *
FROM Livre
WHERE ISBN = unISBN
Hachage<{Livre},{ISBN}>
Livre
<<table>>
Exemple
Meilleur plan P avec schéma interne précédent
– TempsES (BAL) > 50 010ms (meilleur cas)
sélectivité de code = 1/4000
volume de données suffisant
TempsES (S=H) = 143ms
– conflit avec hachage sur ISBN
TempsES (S=IP) = 242ms
– conflit ...
TempsES (S=IS) = 2827ms
– seule possibilité pour éviter de pénaliser 1
Opération de priorité 2:
SELECT *
FROM Livre
WHERE code = unCode
Hachage<{Livre},{ISBN}>
Livre
<<table>>
IndexSecondaire<Livre,{code}>
Glouton : temps moyen sous-optimal
Hypothèse
– sélection par ISBN : 50 fois par jour
– sélection par code : 30 fois par jour
– moyenne : (50*11 + 30*2827)/80 = 1067ms
Pénaliser un peu opération 1…
– moyenne : (50*55 + 30*143)/80 = 88ms
Hachage<{Livre},{code}>
Livre
<<table>>
IndexSecondaire<Livre,{ISBN}>
Meilleur plan P avec schéma interne précédent
–
TempsES (S=IS sur code) = 2827ms
TempsES ( S=IS sur la clé composée {code, annéeParution} ) = 99ms
N.B. Index sur {code, annéeParution} utilisé pour :
Opération de priorité 3:
SELECT *
FROM Livre
WHERE code = unCode AND annéeParution = uneAnnée
Hachage<{Livre},{ISBN}>
Livre
<<table>>
IndexSecondaire<Livre,{code, annéeParution}>
Opération de priorité 2:
SELECT *
FROM Livre
code = unCode
Indexer les colonnes individuelles ?
TempsES(S) = 3 509ms
Hachage<{Livre},{ISBN}>
Livre
<<table>>
IndexSecondaire<Livre,{code}>
IndexSecondaire<Livre,{annéeParution}>
Opération de priorité 3:
SELECT *
FROM Livre
WHERE code = unCode AND annéeParution = uneAnnée
Meilleur plan P avec schéma interne précédent
–
TempsES (BAL) > 50 010ms (meilleur cas)
TempsES ( S=IS sur annéeParution ) = 223 377 ms
Opération de priorité 4:
SELECT *
FROM Livre
WHERE annéeParution = uneAnnée
Hachage<{Livre},{ISBN}>
Livre
<<table>>
IndexSecondaire<Livre,{code, annéeParution}>
Hachage<{Livre},{ISBN}>
Livre
<<table>>
IndexSecondaire<Livre,{code, annéeParution}>
IndexSecondaire<Livre,{annéeParution}>
Accès multidimensionnel
Ex: accès par titre, code, annéeParution et nomEditeur
– seule ou en combinaison
Indexer toutes les combinaisons ?
Un index pour chacune des colonnes
Organisation multidimensionnelle
Index bitmap pour faible facteur de
sélectivité
Maintenance des contraintes d ’intégrité
Contrainte PRIMARY KEY(ISBN)
– hachage ou indexage sur ISBN
Heuristique
– hachage ou indexage sur PRIMARY KEY et UNIQUE
– Oracle :
index secondaire automatique
INSERT INTO Livre VALUES(…)
13.3.5 Sélection par intervalle
Index sur titre
Préfixe ~ intervalle
SELECT *
FROM Livre
WHERE titre = 'unPréfixe%'
13.3.6 Tri du résultat (ORDER BY)
Index primaire sur datePrêt
Index secondaire inutile dans ce cas
SELECT *
FROM PrêtEnCours
ORDER BY datePrêt
13.3.7 Calcul du minimum (MIN) et du maximum (MAX)
Index sur noSequence
SELECT MAX(noSequence)
FROM PrêtEnCours
13.3.8 Calcul d'agrégat (GROUP BY)
Index primaire, grappe ou hachage sur idUtilisateur
SELECT COUNT(*)
FROM PrêtEnCours
GROUP BY idUtilisateur
13.3.9 Jointure par égalité
Jointure sur la clé de jointure X
– bonne sélectivité de X
– volume de données suffisant
Heuristique
– 1. Organisation hétérogène sur X
grappe, hachage ou index primaire (pré- jointure)
– 2. Organisation homogène sur X
table interne ( boucle imbriquée )
Organisation hétérogène
TempsES (PJ) =
–
TempsES (BAL
Livre) + TempsES (BAL
Catégorie) = 50 120 ms
Pénalise TempsES (BAL pour Catégorie)
SELECT *
FROM Livre, Catégorie
WHERE Livre.code = Catégorie.code
Catégorie
<<table>>
Grappe<{Livre, Catégorie},{code}>
Livre
<<table>>
Organisation homogène
Livre ou Catégorie ?
– organisation sur code pour Livre
ne pénalise pas TempsES (BAL pour Catégorie)
– organisation sur code pour Catégorie
– faible sélectivité de la jointure...
Cas d ’une jointure sélective
Organisation hétérogène à éviter
– jointure sélective pour Exemplaire
SELECT *
FROM PrêtEnCours, Exemplaire
WHERE PrêtEnCours.idExemplaire = Exemplaire.idExemplaire
Hachage<{Exemplaire},{idExemplaire}>
PrêtEnCours
<<table>>
Exemplaire
<<table>>
13.3.10 Impact du plan d'exécution de l'optimiseur de requête
Heuristiques précédentes pas toujours pertinentes
Cas de requêtes complexes
– plusieurs sélections, jointures, ...
Tenter de prévoir le plan d ’exécution
– utiliser modèles de coût
Exemple
Heuristique : index primaire sur datePrêt ?
SELECT *
FROM PrêtEnCours
WHERE idUtilisateur = unIdUtilisateur ORDER BY datePrêt
PrêtEnCours
idUtilisateur = unIdUtilisateur
(Sélection par index secondaire sur
idUtilisatuer)
Tri par datePrêt
Exemple
Ne peut faire mieux que BIM avec organisation homogène
– voir estimations SELECT *
FROM Livre, Catégorie
WHERE Livre.code = Catégorie.code
Schéma 1 (privilégie sélection)
SELECT *
FROM Livre, Catégorie
WHERE Livre.code = Catégorie.code AND Livre.ISBN = unISBN
Hachage<{Livre},{ISBN}>
Livre
<<table>>
Catégorie
<<table>>
Hachage<{Catégorie},{code}>
Livre
Catégorie
(Boucle imbriquée avec hachage sur code de la table interne Catégorie
TempsES = 11ms )
ISBN = unISBN(Sélection par hachage sur ISBN
TempsES = 11ms )
Organisation sur
code de Livre ???
Schéma 2 (privilégie jointure)
SELECT *
FROM Livre, Catégorie
WHERE Livre.code = Catégorie.code AND Livre.ISBN = unISBN
IndexSecondaire<Livre,{ISBN}>
Livre
<<table>>
Catégorie
<<table>>
Hachage<{Catégorie},{code}>
L i v r e
C a t é g o r i e
( B o u c le im b r iq u é e a v e c h a c h a g e s u r c o d e d e l a t a b l e in t e r n e C a t é g o r ie T e m p s E S = 6 0 . 5 m s )
I S B N = u n I S B N( S é le c t io n p a r i n d e x s e c o n d a ir e s u r
I S B N
T e m p s E S = 5 5 m s )
Schéma 3 (index secondaires seulement)
IndexSecondaire<Catégorie,{code}>IndexSecondaire<Livre,{ISBN}>
Catégorie
<<table>>
Livre
<<table>>
L i v r e
C a t é g o r i e
( B o u c le i m b r iq u é e a v e c i n d e x s e c o n d a ir e
s u r c o d e d e l a t a b l e i n t e r n e C a t é g o r i e T e m p s E S = 3 3 m s )
I S B N = u n I S B N( S é le c t i o n p a r i n d e x s e c o n d a i r e s u r
I S B N
T e m p s E S = 5 5 m s )
SELECT *
FROM Livre, Catégorie
WHERE Livre.code = Catégorie.code AND
Livre.ISBN = unISBN
Cas complexe
SELECT idUtilisateur, nom, prénom,titre
FROM Employé, Utilisateur,PrêtEnCours,Exemplaire, Livre WHERE Employé.codeMatricule = unCodeMatricule AND
Employé. idUtilisateur = Utilisateur.idUtilisateur AND
Utilisateur.idUtilisateur = PrêtEnCours.idUtilisateur AND
PrêtEnCours.idExemplaire = Exemplaire.idExemplaire AND
Exemplaire.ISBN = Livre.ISBN
Utilis ateur {Clé prim aire : idUtilis ateur}
idUtilis ateur : VARCHAR(10) m otPas s e : VARCHAR(10) nom : VARCHAR2(20) prénom : VARCHAR2(20)
/ catégorieUtilis ateur : Dom aineCatégorieUtilis ateur
<<table>>
Em ployé {Clé prim aire : idUtilis ateur}
idUtilis ateur : VARCHAR(10) {Clé candi date : c odeMatri cul e}
codeMatricule : CHAR(6)
catégorieEm ployé : Dom aineCatégorieEm ployé
<<ta ble>>
PrêtEnCours
{Clé prim aire : idExem plaire}
idExem plaire : VARCHAR(10) datePrêt : DATE
id Utilis ateur : VARCHAR(1 0)
<<table>>
Exem plai re
{Clé prim aire : idExem plaire}
idExem plaire : VARCHAR(10) dateAchat : Date
s tatut : Dom aineStatut ISBN : CHAR(13)
<<table>>
Livre {Clé prim aire : ISBN}
ISBN : CHAR(13) titre : VARCHAR(50)
annéeParution : Dom ai neAnnée
<<table>>
1 . S é l e c t i o n p a r c o d e M a t r ic u l e
2 . S é l e c t i o n p a r id U t il is a t e u r p o u r l a
t a b l e i n t e r n e U t il is a t e u r
4 . S é l e c t i o n p a r id E x e m p l a ir e p o u r
l a t a b l e i n t e r n e E x e m p l a ir e 3 . S é l e c t i o n p a r
id U t il is a t e u r p o u r l a t a b l e i n t e r n e P r ê t E n C o u r s
5 . S é l e c t i o n p a r I S B N p o u r l a t a b l e
i n t e r n e L iv r e
Plan générique
U t i l i s a t e u r
( B o u c le i m b r i q u é e a v e c s é l e c t io n p a r id U t il i s a t e u r s u r la
t a b le in t e r n e U t i l is a t e u r )
c o d e M a t r i c u l e = u n C o d e M a t r ic u l e( S é l e c t io n p a r c o d e M a t r ic u le )
P r ê t E n C o u r s
( B o u c l e i m b r iq u é e a v e c s é l e c t io n p a r id U t i li s a t e u r s u r l a
t a b le in t e r n e P r ê t E n C o u r s ) E x e m p l a i r e
( B o u c le i m b r i q u é e a v e c s é l e c t io n p a r id E x e m p l a ir e s u r l a
t a b l e i n t e r n e E x e m p l a i r e ) L i v r e
( B o u c le i m b r iq u é e a v e c s é le c t io n p a r I S B N s u r l a t a b l e
in t e r n e L i v r e )
Schéma 1
Variante : index secondaires
Utilisateur {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) motPasse : VARCHAR(10) nom : VARCHAR2(20) prénom : VARCHAR2(20)
/ catégorieUtilisateur : DomaineCatégorieUtilisateur
<<table>>
Employé {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) {Clé candidate : codeMatricule}
codeMatricule : CHAR(6)
catégorieEmployé : DomaineCatégorieEmployé
<<table>>
PrêtEnCours {Clé primaire : idExemplaire}
idExemplaire : VARCHAR(10) datePrêt : DATE
idUtilisateur : VARCHAR(10)
<<table>>
Exemplaire
{Clé primaire : idExemplaire}
idExemplaire : VARCHAR(10) dateAchat : Date
statut : DomaineStatut ISBN : CHAR(13)
<<table>>
Livre {Clé primaire : ISBN}
ISBN : CHAR(13) titre : VARCHAR(50)
annéeParution : DomaineAnnée nomEditeur : VARCHAR(20) code : VARCHAR(10)
<<table>>
Hachage<{Employé},{codeMatricule}>
Hachage<{Utilisateur},{idUtilisateur}>
Hachage<{PrêtEnCours},{idUtilisateur}>
Hachage<{Exemplaire},{idExemplaire}>
Hachage<{Livre},{ISBN}>
Plan correspondant au schéma 1
E m p l o y é
U t i l i s a t e u r
( B o u c le i m b r i q u é e a v e c h a c h a g e s u r i d U t i li s a t e u r d e la
t a b le i n t e r n e U t i l is a t e u r )
c o d e M a t r i c u l e = u n C o d e M a t r ic u l e( S é l e c t i o n p a r h a c h a g e s u r c o d e M a t r i c u l e )
P r ê t E n C o u r s
( B o u c l e i m b r i q u é e a v e c h a c h a g e s u r i d U t i li s a t e u r d e la
t a b le i n t e r n e P r ê t E n C o u r s ) E x e m p l a i r e
( B o u c l e i m b r i q u é e a v e c h a c h a g e s u r i d E x e m p l a i r e d e l a
t a b l e i n t e r n e E x e m p l a ir e ) L i v r e
( B o u c l e im b r i q u é e a v e c h a c h a g e s u r I S B N d e la t a b l e
i n t e r n e L i v r e )
Raffinement
Organisation hétérogène
Utilisateur {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) motPasse : VARCHAR(10) nom : VARCHAR2(20) prénom : VARCHAR2(20)
/ catégorieUtilisateur : DomaineCatégorieUtilisateur
<<table>>
PrêtEnCours
{Clé primaire : idExemplaire}
idExemplaire : VARCHAR(10) datePrêt : DATE
idUtilisateur : VARCHAR(10)
<<table>>
Hachage<{Utilisateur, PrêtEnCours},{idUtilisateur}>
Autre possibilité
Utilisateur {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) motPasse : VARCHAR(10) nom : VARCHAR2(20) prénom : VARCHAR2(20)
/ catégorieUtilisateur : DomaineCatégorieUtilisateur
<<table>>
Employé {Clé primaire : idUtilisateur}
idUtilisateur : VARCHAR(10) {Clé candidate : codeMatricule}
codeMatricule : CHAR(6)
catégorieEmployé : DomaineCatégorieEmployé
<<table>>
PrêtEnCours {Clé primaire : idExemplaire}
idExemplaire : VARCHAR(10) datePrêt : DATE
idUtilisateur : VARCHAR(10)
<<table>>
Hachage<{Utilisateur, Employé, PrêtEnCours},{idUtilisateur}>
IndexSecondaire<Employé,{codeMatricule}>
13.3.11 Techniques sophistiquées de conception et mise au point
Enjeu important
Simulation
Banc d ’essais
Dernier recours
– surveillance
– mise au point
13.3.12 Modification du schéma conceptuel
Dernier recours
Introduction de redondance
– dénormaliser
– introduction d ’éléments dérivés
Favoriser lecture (SELECT)
Pénaliser écriture
– traitements de maintien de la cohérence
13.3.12.1 ALTERNATIVES DE TRADUCTION
Attributs multivalués
Associations
Généralisations
...
13.3.12.2 DÉNORMALISATION
Inclure Editeur dans Livre
Évite jointure coûteuse
Maintien de la cohérence
– nomEditeur ville dans Livre (modifiée)
– ajout de TRIGGER
Editeur
{Clé primaire : nomEditeur}
nomEditeur : VARCHAR(20) ville : VARCHAR(20)
<<table>> Livre
{Clé primaire : ISBN}
ISBN : CHAR(13) titre : VARCHAR(50)
annéeParution : DomaineAnnée nomEditeur : VARCHAR(20) code : VARCHAR(10)
<<table>> Livre
{Clé primaire : ISBN}
ISBN : CHAR(13) titre : VARCHAR(50)
annéeParution : DomaineAnnée nomEditeur : VARCHAR(20) code : VARCHAR(10)
{nomEditeur->ville}
nomEditeur : VARCHAR(20) ville : VARCHAR(20)
<<table>>
13.3.12.3 INTRODUCTION D'ÉLÉMENTS DÉRIVÉS
Index sur noSequence
Nouvelle table
PrêtEnCoursGénéral(maxNoSequence)
INSERT INTO PrêtEnCours
SELECT MAX(noSequence),…
FROM PrêtEnCours
INSERT INTO PrêtEnCours
SELECT maxNoSequence+1,…
FROM PrêtEnCoursGénéral UPDATE PrêtEnCoursGénéral
SET maxNoSequence = maxNoSequence+1
Oracle et Designer
Code Control Sequence de Designer
– maximum dans une table Code Control
– blocage du au verrouillage
SEQUENCE Oracle
– CREATE SEQUENCE nomSequence ...
INSERT INTO …(nomSequence.NEXTVAL,…)
– série de valeurs en antémémoire
pas de blocage
– problème de trous dans la SEQUENCE
13.3.12.4 FRAGMENTATION HORIZONTALE
Cas des données archivées
– table pour données récentes (e.g.
PrêtEnCours)
– table pour historique (e.g. PrêtArchivé)
– VIEW pour cacher la fragmentation
Oracle8
– fragmentation transparente de table
clause PARTITION
– fragmentation d ’index
13.3.12.5 FRAGMENTATION VERTICALE
Cas des colonnes volumineuses (LOB)
Traduction de généralisation par
délégation
7.3.12.6 CONCEPTION D'UN ENTREPÔT DE DONNÉES
E n t r e p ô t d e d o n n é e s ( « d a t a w h a r e h o u s e » )
B D o p é r a t i o n n e l l e
( O L T P )
A u t r e s o u r c e d e
d o n n é e s
E x t r a c t i o n : f i l t r a g e , s y n t h è s e , t r a n s f o r m a t i o n ,
f u s i o n B D
o p é r a t i o n n e l l e ( O L T P )
A n a l y s e ( O L A P ) , p r o s p e c t i o n
Caractéristiques
Entrepôt de données = redondance contrôlée
Requêtes d ’analyse de données (OLAP)
–
complexes
–
imprévisibles
–
grand volume
Relational OLAP - ROLAP
–
schéma en étoile
Multidimen s ion a l OLAP -MOLAP
–
cube de données
T able virtuelle matérialisée
P arallélisme
Organisations multidimensionnelles
Schéma en étoile (star schema)
SommairePrêt {Clé primaire : noSequence}
noSequence : INTEGER nombre : INTEGER
catégorieUtilisateur : DomaineCatégorieUtilisateur code : VARCHAR(10)
noPériode : INTEGER
<<table>>
Catégorie {Clé primaire : code}
code : VARCHAR(10)
descripteur : VARCHAR(20) codeParent : VARCHAR(10)
<<table>>
codeParent
Période noPériode : INTEGER année : INTEGER
semestre : DomaineSemestre mois : DomaineMois
<<table>>
Table de faits
Dimensions : cube de données
Tables de dimensions :
hiérarchie de granularité
13.4 Paramètres du mécanisme
d'allocation d'espace : cas de Oracle
DATABASE
– fichiers de données (DATAFILE)
– fichiers de journalisation (REDO LOG)
– fichiers de contrôle (control file)
– utilisé par un serveur (instance)
CREATE DATABASE nomDatabase ...
Démarrage du serveur
Installation d'un serveur Oracle
– DATABASE automatiquement créée
Démarrage d ’un serveur Oracle
– ouvre fichier de configuration
e.g. C:\orawin\DATABASE\initorcl.ora (si SID = orcl)
– ouvre fichier de contrôle
e.g. C:\orawin\DATABASE\ctl1orcl.ora
– ouvre fichiers de données/journal
– la DATABASE est montée (mounted)
TABLESPACE
DATABASE = ensemble de TABLESPACE
– minimum 1 (SYSTEM TABLESPACE)
contient le dictionnaire de données
– peut ajouter des TABLESPACE
TABLESPACE
– espace pour TABLE (et autres objets)
– = ensemble de DATAFILE
CREATE TABLESPACE des2 DATAFILE ‘c:\orawin\dbs\WDBdes2.ora’
SIZE 14M DEFAULT STORAGE (PCTINCREASE 0) ONLINE;
ALTER TABLESPACE SYSTEM ADD DATAFILE
‘c:\orawin\dbs\WDBSYS1.ora’ SIZE 60M REUSE
Clauses TABLESPACE et STORAGE
CREATE TABLE Client...
INITRANS 1 MAXTRANS 5 PCTFREE 10 PCTUSED 40
TABLESPACE user_data STORAGE(
INITIAL 20480
NEXT 20480
MINEXTENTS 1 MAXEXTENTS 10 PCTINCREASE 10)
Taille des
EXTENTS
EXTENT, SEGMENT
EXTENT
– granule d'allocation d'espace
– ensemble de blocs contigus
– dans un DATAFILE du TABLESPACE
Clause STORAGE de défaut
niveau TABLESPACE
niveau DATABASE
SEGMENT
– ensemble de EXTENT d ’un objet
(DATA, INDEX, ROLLBACK, TEMPORARY SEGMENTS)
Conception des TABLESPACE
1. Unité de base de prise de copie
2. Sélectivement désactivés (offline)
3. TABLESPACE sur des disques différents
– réduire congestion
4. EXTENT de même taille ensemble
5. Limiter fragmentation
TABLESPACE de défaut
–
associé au authorizationID
Allocation sérielle par défaut
Enregistrements de taille variable
–
adressage structuré par bloc
ROWID
–
gestion des débordements par chaînage
–
voir volume 1, chap.2
PCTFREE
–
réservé pour UPDATE
PCTUSED
–
seuil pour retour dans FREE LIST
INITRANS
–
entrée de transaction
MAXTRANS
E n t ê t e d e b l o c
( p a r t i e f i x e + p a r t i e v a r i a b l e ) R é p e r t o i r e d e t a b l e s R é p e r t o i r e d e l i g n e s
E s p a c e l i b r e
E s p a c e o c c u p é