BDD
BDD
BDD
14 septembre 2010
Table des matières
1 Introduction 1
2 Modèle de Chen 3
2.1 Généralités sur l’information et sur sa représentation . . . . . . . . . . . . . 3
2.2 Notions de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3 Structure des types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3.1 Type d’entités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3.2 Type d’attribut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3.3 Type d’association . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3.4 Représentation par diagramme . . . . . . . . . . . . . . . . . . . . . 5
2.4 Notion d’occurrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.5 Représentation des concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.5.1 Notion de clé et de clé primaire . . . . . . . . . . . . . . . . . . . . . 7
2.5.2 Cardinalité de rôle . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.5.3 Cas particulier des classes d’associations binaires . . . . . . . . . . . 9
2.6 Diagramme entité-association . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.7 Dépendances entre attributs . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.8 Quelques propriétés du modèle entité-association . . . . . . . . . . . . . . . 12
2.8.1 Bases de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.8.2 Non redondance de l’information . . . . . . . . . . . . . . . . . . . . 13
2.8.3 Extension de la notion de clé d’une classe d’associations . . . . . . . 13
2.9 Entités faibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.10 Exemple : gestion d’un parc informatique . . . . . . . . . . . . . . . . . . . 17
2.11 Le langage de modélisation de données EXPRESS . . . . . . . . . . . . . . 19
2.11.1 Les entités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.11.2 Les instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.11.3 Les contraintes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
i
ii TABLE DES MATIÈRES
5 Modèles classiques de BD 49
5.1 Le modèle hiérarchique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.1.1 Contexte et définitions . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.1.2 Techniques de transformation entre le niveau logique et le niveau
physique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
5.1.3 Caractéristiques du modèle hiérarchique . . . . . . . . . . . . . . . . 53
5.1.4 Définition du modèle hiérarchique . . . . . . . . . . . . . . . . . . . 53
5.1.5 Limitations du modèle hiérarchique . . . . . . . . . . . . . . . . . . 53
5.1.6 Réalisations techniques . . . . . . . . . . . . . . . . . . . . . . . . . 54
5.2 Le modèle réseau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.2.1 Les spécifications du CODASYL . . . . . . . . . . . . . . . . . . . . 55
5.2.2 Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
5.3 Définitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
5.3.1 Réalisations techniques . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.4 Exemple d’un SGBD de type DBTG . . . . . . . . . . . . . . . . . . . . . . 58
TABLE DES MATIÈRES iii
6 Le modèle relationnel 67
6.1 Description générale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
6.2 Un exemple intuitif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
6.3 Définitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.3.1 Domaine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.3.2 Relations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
6.3.3 Attributs et clés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.3.4 Schéma relationnel d’une base de données . . . . . . . . . . . . . . . 71
6.4 Conception du schéma relationnel . . . . . . . . . . . . . . . . . . . . . . . . 73
6.4.1 Principe initial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
6.4.2 Simplification du schéma . . . . . . . . . . . . . . . . . . . . . . . . 74
6.4.3 Règles de traduction . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
7 Algèbre relationnelle 79
7.1 Principe et définitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
7.2 Opérateurs ensemblistes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
7.2.1 Union . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
7.2.2 Intersection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.2.3 Différence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.3 Projection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.4 Sélection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
7.5 Produit cartésien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
7.6 Jointures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7.6.1 Jointure naturelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7.6.2 θ-jointure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
7.7 Opérateur de renommage . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
7.8 Opérations de mise à jour . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
7.9 Représentation d’une requête . . . . . . . . . . . . . . . . . . . . . . . . . . 86
7.9.1 Représentation sous forme d’arbre . . . . . . . . . . . . . . . . . . . 86
7.9.2 Notation linéaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
7.10 Opérateurs étendus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
7.10.1 Opérateurs d’agrégation . . . . . . . . . . . . . . . . . . . . . . . . . 87
7.10.2 Regroupements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
7.10.3 Jointures externes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
7.11 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
iv TABLE DES MATIÈRES
Introduction
Une entreprise ou une organisation doit conserver la trace d’un volume élevé d’infor-
mations. Ces dernières peuvent être, par exemple, les noms, les salaires des employés, des
adresses de fournisseurs ou toute autre information utile et donc à consulter. Traditionnel-
lement, différentes parties de ces informations sont conservées par différents départements
et/ou différents individus, chacun ayant à sa charge les informations qu’il utilise le plus
souvent. Lorsque l’on veut obtenir une information, il faut alors déterminer :
– l’endroit où elle est rangée ou stockée,
– s’adresser au département ou à la personne concernée,
– et enfin la rechercher parmi toutes les informations locales au département ou à la
personne.
Souvent, plusieurs informations prises individuellement ne fournissent pas toujours
les résultats souhaités. Nous devons utiliser ces différentes informations pour obtenir une
information composée « plus riche en information ». Cela implique la navigation d’une
information d’un département à un autre ou d’une personne à une autre.
Pour toutes ces raisons, une tendance s’est développée pour combiner toutes les infor-
mations d’une organisation dans une base de données intégrée. Le stockage des données y
sera entièrement centralisé. Dans l’idéal, il n’existera qu’un exemplaire de chaque élément
de données. Les mises à jour ne sont exécutées qu’une seule fois.
Une base de données sera donc un ensemble organisé et intégré de données. Elle
correspond à une représentation fidèle des données et de leur structure avec le minimum
possible de contraintes imposées par le matériel. Enfin, elle doit pouvoir être utilisée pour
une application pratique sans duplication de données.
Le SGBD (Système de Gestion de Bases de Données) est le logiciel qui supporte une
telle organisation. C’est en fait un ensemble de logiciels fournissant l’environnement pour
décrire, mémoriser, manipuler et traiter des ensembles de données tout en assurant pour
celle-ci la sécurité, la confidentialité et l’intégrité sachant qu’un grand nombre d’utilisateurs
ayant des besoins variés interagit avec ces ensembles de données.
L’objectif de ce cours est d’introduire les différents concepts liés à l’étude des bases de
données. Nous partons du modèle de Chen, qui permet de formaliser les concepts de base
utiles en représentation de données, puis nous suivons l’évolution historique des bases de
1
2 CHAPITRE 1. INTRODUCTION
données depuis les anciens modèles tel le modèle hiérarchique (encore utilisé dans certains
cas) jusqu’aux modèles les plus récents tels les bases de données orientées objets et les
bases de données utilisant la logique comme formalisme de base (appelées bases de données
déductives). Nous insistons particulièrement sur le modèle relationnel qui tend à devenir
le modèle le plus utilisé et nous présentons le formalisme des dépendances fonctionnelles
permettant de générer des modèles relationnels « propres ». Un langage support au modèle
relationnel, SQL, est présenté en détail. Enfin, le lecteur trouvera également un chapitre
sur les problèmes de transactions et de reprises après pannes.
Chapitre 2
3
4 CHAPITRE 2. MODÈLE DE CHEN
Exemple 2.2.1 : Une personne pourra être représentée par une entité. Cette entité
possédera plusieurs attributs : nom, prénom, numéro de sécurité sociale par exemple. Deux
entités de type personne peuvent être reliées entre elles par une association représentant
la parenté. Enfin, l’attribut « numéro de sécurité sociale » a pour domaine de valeur les
entiers contraints par les règles de formation du numéro INSEE.
âge nom
Personne
nb habitants
Voiture
couleur âge
Les occurrences représentent les valeurs décrites par les types ou les classes. L’ensemble
des occurences décrit une description en extension associée aux types ou aux classes.
– une occurrence d’entité est un élément du monde réel ayant une existence propre. En
général elle est dénotée par un ensemble d’occurrences (de valeurs) d’attributs.
– une occurrence d’attribut (ou une valeur d’attribut) est une valeur, un résultat d’une
fonction pour un type particulier d’attribut.
– une occurrence d’association est un cas particulier d’information, représentant un
lien entre plusieurs occurrences d’entités du monde réel.
Exemple 2.4.1 : Une occurrence de Personne sera dénotée par son nom et son prénom,
ou par son numéro de sécurité sociale, mais dans ce cas, l’occurrence de l’entité Personne a
une existence propre qui est indépendante de ce que peut être son nom ou son numéro de
sécurité sociale.
Une occurrence d’attribut est la valeur « Dupont » de l’attribut nom, de même que la
valeur « 30 » est une occurrence de l’attribut âge et la valeur « rouge » une occurrence de
l’attribut couleur.
On peut représenter des occurences d’un diagramme comme sur la figure 2.3.
2.5. REPRÉSENTATION DES CONCEPTS 7
Personne
nom
âge
nb habitants
Voiture
couleur
âge
Personne
nom=”Dupont”
âge=30
Possède
Voiture
couleur=”rouge”
âge=10
2.5.2 Cardinalité du rôle d’une classe d’entités dans une classe d’asso-
ciations
Définition 2.5.2 (rôle).
Le rôle d’une classe d’entités E dans une classe d’association A est l’interprétation de la
présence d’une entité de classe E dans une association de classe A. 2
Par exemple, le rôle joué par « Personne » dans la classe d’association « Possède »
avec « Voiture » est « propriétaire ». « Personne » peut jouer d’autres rôles dans d’autres
classes d’association.
Définition 2.5.3 (cardinalité).
La cardinalité est une mesure de l’étendue des interventions possibles d’une entité quelconque
d’une classe E dans des associations d’une classe A.
La cardinalité est un intervalle [min, max ] (noté également min..max ) sur N, où min et
max représentent respectivement le nombre minimal et le nombre maximal d’interventions
possibles. 2
Exemple 2.5.1 : Supposons que l’on ait une représentation d’un service de location de
voiture comportant une classe d’entités « Voiture » associée à une classe d’association
« Location ». Dans ce cas, la cardinalité du rôle « est louée » de « Voiture » dans « Location »
est [0, n] : une voiture peut ne pas être louée ou être louée par plusieurs personnes à des
moments différents.
Supposons maintenant que l’on ait une classe d’entités « Personne » liée à une classe
d’association « Travail » par le rôle « emploie ». On suppose que « Travail » est liée
également à « Service » et représente donc le fait qu’une personne travaille dans un service.
La cardinalité du rôle « emploie » peut être :
– [1, 1] si un employé n’est attaché qu’à un seul service ;
– [1, n] si un employé est rattaché à plusieurs services. 2
Travaille Emploie
Employé Travail Service
[1, n] [1, n]
Voiture Client
Location
[1, n] Réalise
Employé
Travaille Emploie
Employé Travail Service
[1, n] [1, n]
num employé pourcentage nom service
nom nom bâtiment
âge
salaire
Travaille Emploie
Employé Travaille Service
[1, 1] [1, n]
* 1
Employé Service
employé employeur
Une notation alternative, appelée crow’s foot (« patte de corbeau », en relation avec
la forme d’un des éléments de la notation) et utilisée dans certaines méthodes et outils,
propose une autre façon de noter les associations et les multiplicités correspondantes. Les
entités sont reliées directement par une ligne avec le nom de l’association. Des symboles
permettent de représenter les cardinalités :
12 CHAPITRE 2. MODÈLE DE CHEN
– un cercle représente 0
– une barre verticale représente 1
– la « patte de corbeau » représente « plusieurs »
On combine ensuite ces symboles pour donner la multiplicité des associations. Par
exemple, dans la figure 2.8 la multiplicité de l’entité Artiste est [1, 1] et celle de Chanson
est [0, n].
Artiste Chanson
interprète
Voiture Client
Location
Employé Date
Voiture Client
Relation
Employé Location
Voiture Client
Location
date location
Employé
Critique : « Location » n’est plus exactement une classe d’associations. Elle est
identifiée par :
– les clés des trois entités « Voiture », « Client » et « Employé » et
– l’attribut propre « date location »
Malgré l’entorse au formalisme entité-association, nous adopterons cette solution à
cause de sa simplicité de compréhension et de représentation.
La possibilité d’étendre la clé d’une seule classe d’associations avec un ou plusieurs
attributs est souvent utilisée pour éviter des surcharges d’entités fictives représentant :
– la date ;
– l’heure ;
– ...
– ou plus généralement, un domaine de valeurs (classe d’entités à un seul attribut).
nom équipe
On se pose maintenant la question de savoir quelle est la clé primaire de l’entité Équipe.
Elle ne peut pas être l’attribut nom équipe de Équipe, car des équipe dans des services
différents peuvent avoir le même nom (par exemple, deux services différents ont une équipe
« qualité »). Il faut avoir également la connaissance du nom du service de l’équipe pour
pouvoir identifier l’équipe. On dit alors que Équipe est une entité faible.
Définition 2.9.1 (entité faible).
Une entité E est dite faible si sa clé primaire est composé d’attributs propres à l’entité mais
également de d’attributs clés d’autres entités lié à E par des associations fonctionnelles de
E vers ces entités. On dit que ces associations sont des associations supports de E. 2
16 CHAPITRE 2. MODÈLE DE CHEN
nom équipe
Remarque : Une solution couramment employée pour pallier le problème des entités
faibles est d’introduire un identifiant unique (sous forme d’un entier par exemple) comme
attribut des entités concernées. On peut par exemple penser au numéro de sécurité sociale
dans le cas d’une personne (le nom et le prénom ne permettant pas d’identifier de façon
unique une personne). Cette technique permet de simplifier le problème, mais introduit un
attribut « artificiel » parfois difficile à manipuler, sa signification n’étant pas très intuitive
(il est plus facile de travailler avec un couple Nom/Prénom qu’un numéro de sécurité sociale
pour un utilisateur par exemple).
Dans notre cas, l’introduction d’un tel attribut nous amènerait au diagramme présenté
sur la figure 2.14.
num équipe
nom équipe
Syntaxe :
ENTITY E ;
Att_1 : Type_1 ;
Att_2 : Type_2 ;
...
Att_n : Type_n ;
END_ENTITY ;
2
20 CHAPITRE 2. MODÈLE DE CHEN
Les types Type_i peuvent être des types de base (integer, real, boolean, ou string)
ou des collections ou agrégats (list, array, bag, set). Par exemple, on peut écrire :
ENTITY Eleves ;
Nom : STRING ;
Prenom : STRING ;
Age : INTEGER ;
Cours_suivis : LIST[1 : ?] OF STRING ;
END_ENTITY ;
Les agrégats peuvent s’utiliser avec un intervalle (qui, lorsqu’il est absent, est équivalent
à [0 : ?]) dont les bornes indiquent le minimum et le maximum des valeurs des index de
ces agrégats.
Notons que la déclaration une_liste : LIST OF INTEGER ; est équivalente à
une_liste : LIST[0 : ?] OF INTEGER ;
et ? désigne la valeur indéterminée. On peut aussi écrire une liste dont les index
minimum et maximum sont contraints (par exemple LIST [1 : 6] of ...). Les agrégats
permettent la description de cardinalités dans les associations.
Enfin, toujours pour le typage des attributs, signalons qu’un attribut peut être typé
par une autre entité. Cette possibilité est très importante, elle permet la conception de
modèles complexes. Par exemple :
ENTITY Eleves_bis ;
Nom, Prenom : STRING ;
Age : INTEGER ;
Cours_suivis : LIST[1 : ?] OF cours ;
END_ENTITY ;
L’entité cours est définie par :
ENTITY Cours ;
Titre : STRING ;
Volume_horaire : INTEGER ;
END_ENTITY ;
décrit deux élèves, Dupond et Durand, qui suivent les cours de bases de données et
d’architecture pour Dupond et le cours de bases de données pour Durand.
L’ensemble des instances d’un schéma ou d’un modèle EXPRESS correspond à une
description en extension du modèle. La description en intension est donnée par le code
EXPRESS source. L’utilisation d’instances est essentielle pour la validation de modèles.
Contraintes locales
Les contraintes locales sont associées à une entité. Par exemple :
ENTITY Eleves_bis ;
Nom, Prenom : STRING ;
Age : INTEGER ;
Cours_suivis : LIST[1 : ?] OF cours ;
WHERE
wr1 : age <= 50 and 18<=age
wr2 : SIZEOF(cours_suivis) >= 1 ;
END_ENTITY ;
La contrainte étiquetée par wr1 indique que l’âge d’un élève est compris entre 18 et 50
ans alors que wr2 indique que le nombre de cours suivis par un élève est au moins égal
à deux. Elle utilise la fonction prédéfinie SIZEOF qui donne le nombre d’éléments d’un
agrégat.
Les contraintes locales décrivent des contraintes d’intégrité sur chaque entité.
Contraintes globales
Les contraintes globales portent sur toutes les instances des entités définies dans un
schéma. Par exemple, la règle nommée pas_de_cours_de_bdd indique que l’ensemble des
instances de l’entité cours dont le titre vaut ’bases de donnees’ est vide.
RULE pas_de_cours_de_bdd FOR (cours)
WHERE
QUERY(x <* cours | x.titre = ’bases de donnees’)=[] ;
END_RULE ;
QUERY est un itérateur sur les instances. Il permet le codage des quantificateurs existentiels
et universels en logique.
Les contraintes globales sont utilisées pour la description des contraintes d’intégrité de
schémas.
Contraintes d’unicité
Une contrainte d’unicité introduite par le mot clé UNIQUE définit la notion de clé pour
les instances. C’est une forme de contrainte globale.
Par exemple :
ENTITY Eleves_ter ;
Nom : UNIQUE STRING ;
Prenom : STRING ;
Age : INTEGER ;
Cours_suivis : LIST[1 : ?] OF cours ;
WHERE
wr1 : age <= 50 and 18<=age
wr2 : SIZEOF(cours_suivis) >= 1 ;
END_ENTITY ;
Indique que les noms associés à chaque élève seront uniques dans l’ensemble des instances
de Eleves_ter.
Les contraintes d’unicité sont utilisées pour la description de clés.
Cette brève description du langage EXPRESS permet de coder les schémas entité-
association vus précédemment et de les valider à partir d’un ensemble d’instances. De
plus, cette approche utilisant EXPRESS autorise la description formelle des contraintes
d’intégrité.
Compatibilité
[0, n]
[1, n] [1, 1] [1, n]
Ordinateur Matériel Type Ordinateur [1, n]
référence type
nb postes cons
capa mem [1, n] pays [1, n]
nb disquettes revendeur
nb disk nb postes max
prix mem max
Configuration uc Logiciel Base
capa disquette
capa disk
[1, n] [1, n]
[1, n]
[1, n]
Installation Logiciel Système
concepteur concepteur
origine provenance
prix
[1, n]
Figure 2.15 : Un diagramme entité-association présentant l’exemple de la gestion d’un parc informatique
23
24 CHAPITRE 2. MODÈLE DE CHEN
Chapitre 3
Les systèmes que nous considérons doivent permettre de stocker les informations
contenues dans les bases de façon pérenne et de retrouver et de manipuler rapidement ces
informations suivant différents critères. Dans ce chapitre, nous allons survoler les problèmes
posés par le stockage des informations et nous intéresser plus particulièrement aux fichiers
de données.
On pourra remarquer que pour l’utilisateur (et même l’administrateur) d’une base de
données, la manipulation et la structure des fichiers sont transparentes : ce chapitre permet
simplement de mieux appréhender les notions qui peuvent contraindre l’implantation d’une
base de données ou l’écriture de requêtes.
Le lecteur souhaitant approfondir le sujet pourra consulter [27, 42, 41, ?].
25
26 CHAPITRE 3. STOCKAGE DES INFORMATIONS
(e) Une cellule de mémoire flash (f) Un exemple de mémoire flash embar-
quée dans une clé USB
Figure 3.2 : Principe de fonctionnement d’un disque dur magnétique et « intérieur » d’un
disque dur
Chaque disque magnétique est séparé en pistes (tracks) qui sont des cercles concentriques.
Chaque piste est elle-même découpée en secteurs, de taille variable suivant la position de
la piste, mais contenant la même quantité d’information (512 octets normalement pour
3.2. HIÉRARCHIE DE COMPOSANTS DE STOCKAGE DANS UN ORDINATEUR29
piste
secteur
secteur géométrique
Figure 3.3 : Vue d’un plateau avec une piste, un secteur géométrique et un secteur
Disques optiques
Les disques optiques offrent une capacité de stockage plus importante que les disques
durs magnétiques (en ne considérant bien sûr qu’un seul plateau du disque dur magnétique).
La lecture d’un disque optique se fait en utilisant un laser de faible puissance qui est réfléchi
par des creux et des bosses disposés sur la surface du disque. On peut maintenant réécrire
des informations sur un même support optique.
On trouve dans cette catégorie de périphériques de stockage les CD (Compact Disc),
les DVD (Digital Versatile Disc), les mini-discs, les disques Blu-ray etc.
Cache
Mémoire principale
échange de données
principale pour un accès plus rapide. Les caches actuels ont une capacité de 1 Mo 2 et
les données peuvent être lues ou écrites entre le microprocesseur et le cache en 1 ns. Par
contre, pour faire transiter une donnée entre le cache et la mémoire principale, il faut plus
de temps (environ 10-100 ns). Les caches sont souvent réalisés avec des SRAM.
La mémoire principale (que l’on appelle souvent abusivement RAM) est l’endroit où l’on
stocke toutes les informations et les instructions des programmes en cours. Actuellement,
les ordinateurs ont une mémoire principale d’environ 1 Go (on trouve bien évidemment des
machines avec des capacités plus importantes). Le temps d’accès à la mémoire principale
est toujours le même : il varie entre 10 et 100 ns. On accède à un octet de la mémoire
principale par une adresse. La mémoire principale est souvent réalisée avec de la DRAM.
2. On rappelle qu’en informatique, l’unité de base est l’octet (byte en anglais) qui correspond à un
nombre binaire à 8 chiffres.
3.2. HIÉRARCHIE DE COMPOSANTS DE STOCKAGE DANS UN ORDINATEUR31
La mémoire virtuelle
Supposons que l’on veuille faire tourner plusieurs applications sur un même ordinateur,
par exemple un système d’exploitation, un atelier de développement logiciel, un navigateur
internet, un lecteur de courrier électronique. Il se peut très bien que la mémoire nécessaire
pour ces applications (code à exécuter, pile, données à manipuler) soit supérieure à la
mémoire principale disponible sur l’ordinateur. Dans ce cas, on ne peut pas utiliser en
même temps toutes ces applications.
Les ordinateurs disposent depuis une vingtaine d’années d’un mécanisme de mémoire
virtuelle qui permet de résoudre (en partie) ce problème.
Lorsque l’on exécute un programme sur un ordinateur, ce programme manipule un
certain nombre de données : variables, résultats de lecture de fichiers etc. Ces données
occupent un espace d’adressage virtuel, qui s’étend pour la plupart des machines sur 32
bits, soit environ 4 milliards d’adresses. On peut donc représenter la mémoire virtuelle par
un espace de 4 Go.
Comme la mémoire virtuelle est beaucoup plus importante que la mémoire principale
disponible sur la plupart des machines, une partie de son contenu est stocké sur le système
de stockage secondaire. C’est ce que l’on appelle le swap sur les machines Unix par exemple.
Les informations sont échangées entre le système de stockage secondaire et la mémoire
principale par blocs entiers, que l’on appelle des pages.
– les fichiers physiques qui sont les données enregistrées sur un support physique. Ces
fichiers sont indépendants de tout traitement et ont une existence matérielle. Ils
peuvent être transportés, lus ou faire l’objet de traitements, archivés et détruits.
– les fichiers logiques qui sont des collections de données, éventuellement identiques
à celles contenues dans des fichiers physiques, utilisées à un moment donné par un
programme. Ils n’ont d’existence que pendant la durée de leur utilisation, que l’on
peut représenter par l’instant entre deux primitives : OPEN, qui permet d’ouvrir un
fichier, et CLOSE, qui permet de le fermer.
Les fichiers sont organisés par un système de fichiers, qui permet par exemple d’associer
un nom à un fichier physique particulier. Parmi les grands types de systèmes de fichiers,
on peut citer ext3, NTFS, NFS, FAT32, HFS etc. Le système de fichiers fourni un certain
nombre de primitives permettant de travailler avec les fichiers :
– création d’un fichier
– mise à jour d’un fichier
– destruction d’un fichier
– lecture d’un fichier
Certains systèmes de fichiers peuvent proposer directement d’autres primitives (recherche
dans un fichier par exemple).
Le lecteur souhaitant approfondir le sujet pourra consulter [42, 41].
capacité (b)
13 Tertiaire
12
11
10 Secondaire
8 Principale
6
Cache
5
2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 temps (s)
Le problème qui se pose est le suivant : les données que l’on manipule ne peuvent pas
être contenues la plupart du temps dans la mémoire principale (qui est assez rapide, cf.
3.2.1). Pour traiter ces données, il va donc falloir transférer fréquemment des blocs du
disque dur vers la mémoire principale. Or un transfert de bloc est coûteux : si on prend
une moyenne de 15 ms comme temps de transfert, une machine aurait le temps d’exécuter
des millions d’instructions pendant le transfert !
Le fait de stocker les données à manipuler sur un disque dur va donc amener à faire un
certain nombre d’optimisations, soit à bas niveau (organisation des données sur les différents
disques par exemple), soit parfois à haut niveau. En effet, lorsque l’on conçoit un algorithme
et que l’on calcule sa complexité en temps, on a souvent tendance à considérer que les
données manipulées sont accessibles à moindre coût en mémoire principale. Mais dans
un système de taille conséquente, les données ne « tiennent » pas en mémoire principale.
Certains algorithmes très efficaces en mémoire principale ne le seront plus si on doit utiliser
34 CHAPITRE 3. STOCKAGE DES INFORMATIONS
la mémoire secondaire.
Prenons par exemple un tri de données. On suppose que l’on dispose d’une collection
de données indexées par une clé sur laquelle on peut construire un ordre et on cherche à
trier ces données grâce à cet ordre. Normalement, les algorithmes les plus efficaces pour
cela sont les algorithmes de type « Quicksort ». Ces algorithmes ne seront pas du tout
efficaces pour des données n’étant pas contenues en mémoire principale (car on ferait alors
beaucoup de transferts entre le disque dur et la mémoire principale). On utilise alors des
variantes de l’algorithme de tri/fusion 3.1, dont le principe est de trier des sous-ensembles
des données avant de fusionner les sous-listes ainsi triées.
Par exemple, le tableau 3.1 présente l’utilisation de cet algorithme sur deux listes déjà
triées.
La complexité de cet algorithme est bien connue : O(n log(n)). Il est donc assez efficace.
Dans le cas de tri de données stockées sur un système de stockage secondaire, on utilise un
algorithme de tri/fusion en deux phases dans lequel on trie d’abord des parties des données
qui peuvent être contenues totalement dans la mémoire principale avant de les fusionner.
On pourra consulter [43] pour plus de détails.
On peut également optimiser l’accès aux données contenues dans le disque dur en
utilisant une technique « miroir » consistant à dupliquer les disques pour « paralléliser »
les entrées/sorties ou utiliser des techniques de buffering prédictif pour précharger des
données que l’on sait nécessaires pour la suite de l’application. Ces techniques ne seront
pas abordées ici, mais on pourra consulter par exemple [29] pour approfondir le sujet.
étape L1 L2 sortie
0 {2, 4, 8, 10} {1, 5, 12, 13} {}
1 {2, 4, 8, 10} {5, 12, 13} {1}
2 {4, 8, 10} {5, 12, 13} {1, 2}
3 {8, 10} {5, 12, 13} {1, 2, 4}
4 {8, 10} {12, 13} {1, 2, 4, 5}
5 {10} {12, 13} {1, 2, 4, 5, 8}
6 {} {12, 13} {1, 2, 4, 5, 8, 10}
7 {} {} {1, 2, 4, 5, 8, 10, 12, 13}
que les instances d’entités du chapitre 2 constituent des enregistrements. Dans ce cas, un
enregistrement va donc être constitué d’un ensemble de champs représentant des attributs.
Un enregistrement peut être de longueur fixe ou variable. Par exemple, si l’on considère
des enregistrements représentant chacun un numéro de sécurité sociale, tous les enregistre-
ments auront la même taille. Par contre, si on considère des enregistrements contenant des
noms de personnes, on pourra choisir de les stocker avec une taille variable.
Les enregistrements sont stockés dans des blocs. Il se peut qu’un enregistrement soit
plus petit qu’un bloc. Dans ce cas, on peut choisir d’ajouter d’autres enregistrements
dans le bloc pour gagner de la place. Il se peut également qu’un enregistrement soit plus
gros qu’un bloc. Dans ce cas, il faut être capable de le découper pour pouvoir le stocker
efficacement sur plusieurs blocs.
Enfin, on ajoute en début d’enregistrement un certain nombre d’informations dans une
entête (header en anglais), comme par exemple la longueur de l’enregistrement ou une date
de dernière modification.
–
le numéro du cylindre du disque ;
–
le numéro de la piste dans le cylindre ;
–
le numéro du bloc dans la piste ;
–
éventuellement, un décalage (offset) ou une clé permettant de trouver le début de
l’enregistrement dans le bloc.
– une adresse logique qui est une chaine d’octets.
Les adresses logiques sont reliées aux adresses physiques via une table de correspondance
stockée sur le disque. L’utilisation d’adresses logiques permet d’avoir un système souple :
on peut par exemple déplacer des adresses logiques sans réellement déplacer des blocs, ce
qui serait coûteux. De plus, l’accès à une adresse logique peut se faire rapidement au niveau
du processeur.
Enfin, les enregistrements comportent de plus en plus de références vers d’autres
enregistrements. Cette référence, parfois appelée pointeur, est une information contenant
l’adresse d’un autre enregistrement. Dans ce cas, la traduction des adresses physiques
en adresses logiques est plus compliquée. Par exemple, lorsque l’on charge en mémoire
principale un bloc contenant un enregistrement, on a son adresse logique. Si ce bloc contient
lui-même un pointeur vers un autre enregistrement, quelle va être la nature de cette adresse
en mémoire principale, physique ou logique ? Pour résoudre cela, on utilise des mécanismes
dit de pointer swizzling.
Les modifications d’enregistrements (ajout, destruction, mise à jour) sont des opérations
qui vont être simples si l’on considère que les enregistrements ne sont ordonnés sur le disque.
Cependant, en pratique, on utilisera au moins une clé pour classer les enregistrements. On
trouvera dans les enregistrements sur le disque par ordre de clé croissante.
Ceci peut évidemment poser problème. On peut être quasiment certain que l’insertion
d’un nouvel enregistrement par exemple va nous obliger à déplacer un certain nombres
d’autres enregistrements contenus dans le bloc visé. Il se peut que le bloc puisse accueillir
le nouvel enregistrement, mais également que l’on soit obligé d’utiliser un nouveau bloc
(bloc de débordement) pour déplacer les enregistrements du bloc visé de plus grande clé.
Dans ce cas, il faudra bien sûr régler le problème des adresses pouvant pointer sur ces blocs
déplacés.
Lorsque l’on efface un enregistrement, il faut être capable de gérer l’espace ainsi libéré,
soit en rendant tous les enregistrements du bloc contigus, soit en maintenant une table
contenant toutes les adresses disponibles. Là encore, il faudra gérer les adresses extérieures
sur les enregistrements effacés.
Enfin, la mise à jour se ramène à un effacement et une création.
Évidemment, tous ses problèmes sont rendus plus compliqués dès lors que l’on travaille
avec des enregistrements qui n’ont pas de longueur fixe.
38 CHAPITRE 3. STOCKAGE DES INFORMATIONS
10
20
20
40
50
60
On suppose ici que les valeurs autres que la clé contenues dans l’enregistrement (repré-
sentées sur la figure en blanc) sont telles que leur taille excède largement un bloc. Lorsque
l’on veut chercher par exemple l’enregistrement correspondant à la valeur 60 dans un tel
3.5. CONCLUSION 39
fichier, on est obligé sans index de le parcourir en entier pour trouver le bon enregistrement,
ce qui est coûteux en accès disque.
On peut alors construire un index comme présenté sur la figure 3.7.
10
10 20
20 20
40 40
50 50
60 60
Cette fois-ci, l’index nous permet de trouver facilement l’enregistrement que nous
cherchons. De plus, la table d’index est de petite taille et tient (normalement) en mémoire
principale. Dans notre recherche, on effectuera qu’un seul accès disque pour récupérer
l’enregistrement au lieu d’en effectuer six si on n’avait pas d’index.
Nous reviendrons sur la notion d’index dans le chapitre 8.
3.5 Conclusion
Nous venons de présenter brièvement dans ce chapitre les moyens de stockage de données
sur un ordinateur. Nous avons exposé les différences de temps d’accès et de taille de stockage
entre mémoire principale, système de stockage secondaire et système de stockage tertiaire.
Enfin, nous nous sommes rapidement intéressés au stockage d’enregistrement dans un
fichier situé sur un disque dur.
Évidemment, il n’était pas question dans ce cours chapitre de regarder en détail tous
les problèmes liés au stockage des données (optimisation de l’utilisation du disque dur,
accès efficace aux données, index particuliers, index multidimensionnels etc.). Le lecteur
intéressé pourra lire les chapitres 11 à 14 de [27] comme introduction.
40 CHAPITRE 3. STOCKAGE DES INFORMATIONS
Chapitre 4
Les bases de données ont été définies dans le but d’éliminer les problèmes qui se posent
dans le cas d’une gestion de volumes d’informations lorsque des fichiers de données sont
utilisés.
Les fichiers représentent le seul moyen de mémorisation des informations sur un ordina-
teur. Ils constituent une structure de représentation pour les données comme nous l’avons
vu dans le chapitre 3. Par contre, ils ne fournissent pas une structure de représentation
pour les informations dans le sens où la sémantique des informations mémorisées dépend
du programme qui accède à ce fichier.
Les fichiers présentent des inconvénients liés au fait que chaque utilisateur ou chaque
groupe d’utilisateurs qui met en œuvre une application particulière utilise ses propres
programmes et parfois ses propres fichiers.
Lorsque plusieurs applications manipulent les mêmes informations ou bien des informa-
tions similaires, on obtient :
– une redondance des informations pouvant conduire à des incohérences entre différentes
représentations d’une même information ;
– une dépendance logique concernant les programmes d’application. En effet, ces
programmes ont besoin de tenir compte de la structure des fichiers, y compris ceux
qui ne les concernent pas (par exemple pour des raisons de compatibilité dans le cas
de représentations d’informations déjà stockées dans d’autres fichiers) ;
– une dépendance physique concernant les programmes d’application : ces programmes
doivent être modifiés chaque fois que la structure physique du fichier est modifiée.
On peut remarquer que la simple juxtaposition de fichiers ne constitue pas un système
de gestion de bases de données comme nous le verrons plus loin dans ce chapitre. Les
problèmes relevés ci-dessus restent entiers.
41
42 CHAPITRE 4. APPORTS DES BASES DE DONNÉES ET SGBD
FS 1 FS 2 FS 3 niveau interne
assure la centralisation des données et donc permet de traiter les éventuels problèmes
d’incohérence.
Ce niveau permet de représenter la totalité de la base de données et d’en assurer la
cohérence.
– le niveau interne concerne la représentation physique des données. Il permet de gérer
les structures physiques de stockage, a priori de manière totalement transparente
pour l’utilisateur sauf du point de vue des temps de réponse.
Ce niveau permet d’assurer l’indépendance physique des données.
Pour réaliser cette indépendance, les descriptions logicielles de tous les fichiers sont
rassemblées dans un programme unique, appelé schéma. Tous les accès aux fichiers transite-
ront par ce schéma. Les programmes ne connaı̂tront que les noms symboliques des variables
et ne connaı̂tront plus, à la limite, la notion de fichier. Dès lors, le schéma organisera
comme il l’entend la disposition des données sur les supports, sans que les programmes
s’en préoccupent.
4.3.5 Partageabilité
De multiples accès (qui peuvent être simultanés) peuvent concerner une même donnée,
et donc converger sur le même bloc physique. Le SGBD doit gérer cette simultanéité,
essentiellement pour la prévention d’interblocages.
4.3.7 Cohérence
Il s’agit d’assurer la cohérence des données. Par exemple, à la suite d’une panne, il faut
que les données retrouvent un état cohérent à la reprise du système. La gestion de tels
problèmes est également dévolue au SGBD.
nous nous attachons à montrer quelle(s) caractéristique(s) présentée(s) en section 4.3 ils
implantent.
On s’aperçoit sur la figure qu’il existe deux types d’utilisateurs émettant des commandes
sur le SGBD :
– l’administrateur du SGBD ;
– les utilisateurs « normaux » du SGBD.
L’administrateur du SGBD peut utiliser le langage de description de données pour
modifier le schéma de la base de données. Un compilateur LDD traduit les commandes de
l’administrateur écrites en DDL et celles-ci sont passées au moteur d’exécution. Celui-ci
demande ensuite au gestionnaire d’index et de fichiers de modifier le schéma. C’est ce
dernier composant qui contient les informations sur le schéma de la base puisque c’est lui
qui gère les index, les enregistrements etc. L’indépendance des données et des programmes
est donc assurée par ce composant.
Lors d’une requête par un utilisateur, cette requête est transformée par le compilateur
de requête en un plan (une séquence d’actions) que le SGBD va appliquer pour répondre à
la requête. Le moteur d’exécution va ensuite préparer des requêtes pour des enregistrements,
des index etc. qui sont des informations de petite taille. Le gestionnaire de buffers se charge
ensuite de demander les pages correspondantes et le gestionnaire de stockage lit et écrit les
pages sur le disque dur (ou autre système de stockage secondaire). L’efficacité des échanges
est donc assurée par ces derniers éléments.
Enfin, le gestionnaire de transaction, le module de logging et de reprise et le module
de contrôle de la concurrence permettent de gérer les aspects transactionnels que nous
aborderons dans le chapitre 12. Ce sont ces éléments qui sont chargés la cohérence et la
partageabilité des données.
relations qui existent entre toutes les données contenues dans les différents fichiers.
– un SGBD associe deux types de concepts différents et complémentaires :
1. un modèle de structuration des données qui doit être le mieux adapté au système
d’informations (notion statique),
2. une méthode d’accès, traduite en programmes d’échange, efficaces et complets,
entre les supports périphériques de la base de données et les programmes
d’utilisation (notion dynamique).
Nous allons maintenant nous intéresser à différents modèles de bases de données, avant
de détailler plus précisement le modèle relationnel dans le chapitre 6.
48 CHAPITRE 4. APPORTS DES BASES DE DONNÉES ET SGBD
Application Administrateur
commandes
de tran-
requêtes saction commandes
maj LDD
Compilateur Gestionnaire
Compilateur LDD
de requêtes de transactions
plan de
requêtes
Moteur Contrôle de la
Logging et reprise
d’exécution concurrence
requêtes
index et métadonnées métadonnées
fichiers
Gestionnaire
pages de log Table des verrous
d’index/fichiers
commandes
sur les données,
pages métadon-
nées, index
Gestionnaire
Buffers
de buffers
lit/écrit
pages
Gestionnaire
de stockage
L’organisation des données est représentée par un modèle de données, outil utilisé pour
représenter l’organisation logique de ces données. Ce modèle permet la représentation des
objets et des relations. Les modèles de données utilisés dans les SGBD se distinguent par
la façon de représenter les relations entre les données.
Les principaux modèles de données utilisés pour les bases de données sont les modèles
hiérarchique, réseau, relationnel et à objets. Ces modèles diffèrent dans le traitement des
associations. Nous présentons ici rapidement les modèles hiérarchiques et réseau, les modèles
relationnels et à objets étant présentés plus en détail dans les chapitres 6 et 13.
49
50 CHAPITRE 5. MODÈLES CLASSIQUES DE BD
Chaque arbre comprend une racine et des branches qui permettent l’accès aux différents
niveaux de données. Le niveau d’une donnée mesure sa distance à la racine.
La racine est située au niveau supérieur. Les autres données, qualifiées de dépendantes,
se situent au niveau des nœuds de l’arbre (on parle de parents et d’enfants). On obtient
alors des chemins d’accès hiérarchique. 2
Adresse Niveau 1
Un attribut très important dans la base est celui qui joue le rôle de clé. Nous reparlerons
de cette notion lorsque nous aborderons le modèle relationnel dans le chapitre 6.
Définition 5.1.3 (groupe simple).
Un groupe simple est formé d’un ensemble d’attributs représentés sous la forme d’une
arborescence. 2
Individu
Individu
Dans une base de données utilisant le modèle hiérarchique, seuls les attributs peuvent
avoir des valeurs.
Définition 5.1.5 (groupe répétitif ).
Un groupe répétitif est un groupe dont au moins un des groupes constitutifs peut se répéter
plusieurs fois. Ce dernier est placé entre parenthèses. 2
Représentant
Numéro Montant
Il faut noter que la racine d’un groupe composé doit être toujours considérée comme un
groupe répétitif car la base de données représente un ensemble de données pour lesquelles
on a un arbre. Les parenthèses sont donc omises au niveau de la racine.
E1
R1 R2
A1 A2 A3 A4 A5
E1 R1 A1 none
A2 R2 none none
A3 A4 A5 none
– avec HISAM, on accède de façon indexée à des arbres de bases de données, chaque arbre
étant stocké dans des emplacements physiquement contigus en séquence hiérarchique.
Le type élément racine doit contenir un élément clé pour indexer chaque arbre de la
base.
Dans le cas de l’organisation directe hiérarchisée, les enregistrements sont reliés par des
pointeurs de deux types :
– pointeurs-fils (reliant une racine à ses enfants) ;
– pointeurs-frères (reliant des enregistrements ayant le même père).
L’enregistrement parent est relié au premier de ses enfants. Les deux sous-organisations
utilisées pour cette organisation directe sont appelées HDAM (Hierarchical Direct Access
Method ) et HIDAM (Hierarchical Indexed Direct Access Method ) :
– avec HDAM, on a accès aux enregistrements racines par un algorithme d’adressage
sur les éléments-clés de ces enregistrements (par hash-coding et zone de débordement).
– avec HIDAM, on dispose d’une zone index (répertoire) qui contient les valeurs clés
des enregistrements racines et des pointeurs liant chaque valeur clé à l’enregistrement
5.1. LE MODÈLE HIÉRARCHIQUE 53
correspondant.
Professeurs 1
Élèves N
Professeurs M
Élèves N
Figure 5.8 : Une association maillée qui ne peut pas être directement représentée dans le
modèle hiérarchique
5.2.2 Description
L’approche qui conduit à construire le modèle réseau consiste à introduire dans le
modèle hiérarchique le concept de référence. Ce concept introduit plus de souplesse dans la
description des données.
Ce modèle permet de représenter des relations d’attributs dans un ensemble d’entités et
des associations entre ensembles d’entités. Formellement, un lien Lij définit une connexion
entre deux types d’enregistrements Ei et Ej . Pour chaque enregistrement x de type Ei , le
lien identifie un ensemble d’enregistrements de type Ej et réciproquement. C’est de cette
façon que sont représentées les associations entre ensembles d’entités. Ces liens pourront
être de type 1..1, 1..N ou M..N . Dans le cas du modèle réseau, ce dernier type de lien est
représenté comme étant deux relations de type 1..N .
Une référence sera un pointeur sur un enregistrement. Cela permet d’éliminer les
redondances qui auraient été présentes dans le modèle hiérarchique.
Par ailleurs, on pourra supprimer une référence par simple suppression du pointeur et
sans supprimer les enregistrements référencés. Pour l’utilisateur, le réseau est transparent
et selon le traitement à effectuer, l’utilisateur travaillera sur plusieurs arborescences qui
peuvent varier.
5.3 Définitions
On peut caractériser un SGBD de type réseau par les définitions ci-dessous :
– élément de données (data item)
L’élément de données est la plus petite unité de données logique dans la base qui se
caractérise par un nom et des attributs (type, longueur).
– enregistrement (record )
Un enregistrement est le regroupement d’un ou plusieurs éléments de données.
– type d’enregistrement (record type)
Un type d’enregistrement est un ensemble d’enregistrements de même nature qui
décrivent les éléments d’un même ensemble d’entités.
5.3. DÉFINITIONS 57
– ensemble (set)
Un ensemble représente le lien entre deux types d’enregistrements. Chaque ensemble
possède un nom unique et plusieurs ensembles d’un même type peuvent exister. Un
ensemble relie un type d’enregistrement propriétaire (owner ), situé en « tête » du
lien et un type d’enregistrement membre situé en « bout » de lien.
– type d’ensemble (set type)
Un type d’ensemble possède un type d’enregistrement propriétaire et un ou plusieurs
types d’enregistrement membre. Chaque type d’ensemble est défini indépendamment
de tout autre type d’ensembles.
– espace (area)
L’espace désigne un sous-ensemble de la base, ce qui implique le stockage de chaque
type d’enregistrements dans un espace. Plusieurs types d’enregistrement peuvent être
stockés dans le même espace.
– schéma (schema)
Le schéma décrit la base de données en fonction de son contenu et de sa structure.
Il y a un schéma de base, décrit par le DDL, indépendamment des langages utilisés
pour accéder aux données (DML).
– sous-schéma (subschema)
Le sous-schéma est un sous-ensemble logique du schéma décrivant uniquement les
types d’enregistrements nécessaires à une application particulière, ainsi que les types
d’ensembles et les espaces devant être accessibles à cette application. Il peut y
avoir plusieurs programmes d’application utilisant le même sous-schéma et plusieurs
sous-schémas dans la base.
– le LMD (Langage de Manipulation de Données)
Le langage de manipulation est un langage souvent procédural. Il permet le parcours
du réseau. Il se compose de deux types d’instructions :
– déplacement d’un pointeur à l’intérieur du réseau avec positionnement d’autres
pointeurs appelés pointeurs « courants »,
– manipulation des contenus d’enregistrements par transfert dans une zone de travail
définie à l’intérieur du programme.
Par ailleurs, nous y trouvons les instructions classiques d’ajout, de suppression et de
recherche d’une occurrence d’enregistrements.
Deux autres instructions (propres au modèle réseau) permettent la connexion et
la déconnexion d’une occurrence d’enregistrement par rapport à une occurrence
d’ensemble.
Fournisseur
[1, m]
[1, n]
Produit
Après décomposition, le modèle DBTG donne le diagramme présenté sur la figure 5.10.
L’enregistrement « FP », dit connection record, a été introduit pour représenter l’asso-
ciation maillée. Dans ce cas nous obtenons deux autres liens qui sont :
1. F-FP dont le propriétaire est Fournisseur et qui est membre de FP ;
2. P-FP dont le propriétaire est Produit et qui est membre de FP.
60 CHAPITRE 5. MODÈLES CLASSIQUES DE BD
Fournisseur Produit
M N
Déclaration en LDD
En utilisant le schéma de la figure 5.10, nous obtenons la définition des enregistrements
et des sets.
RECORD NAME IS FP
5.5. EXEMPLES DE MANIPULATION DE DONNÉES 61
OWNER IS FOUR.
ORDER IS SORTED BY DEFINED KEYS.
DUPLICATES ARE NOT ALLOWED.
MEMBER IS FP.
INSERTION IS AUTOMATIC.
RETENTION IS FIXED.
KEY IS ASCENDING PNO IN FP.
SET SELECTION IS BY VALUE OF FNO IN FOUR.
OWNER IS PROD.
ORDER IS SORTED BY DEFINED KEYS.
DUPLICATES ARE NOT ALLOWED.
MEMBER IS FP.
INSERTION IS AUTOMATIC.
RETENTION IS FIXED.
KEY IS ASCENDING FNO IN FP.
SET SELECTION IS BY VALUE OF PNO IN PROD.
Utilisation de GET
ou bien
FIND FOUR OCCURRENCE FOR ’F4’.
GET FNOM IN FOUR, STATUT IN FOUR.
5.5. EXEMPLES DE MANIPULATION DE DONNÉES 63
permettent de récuperer les fournisseurs ayant pour numéro ’F4’. Dans le deuxième cas,
on ne sélectionne que le nom et le statut.
Utilisation de STORE
Soit à créer l’occurrence de l’enregistrement FP suivante :
F5 P6 700
Il faut transférer les différents éléments dans le conteneur de données (la current unit)
par l’ordre MOVE puis ranger les données dans la base par l’ordre STORE.
MOVE ’F5’ TO FNO IN FP.
MOVE ’P6’ TO PNO IN FP.
MOVE 700 TO QTE IN FP.
STORE FP.
Utilisation de ERASE
Soit à supprimer ’F3’ dans FOUR.
FIND FOUR OCCURRENCE FOR ’F3’.
ERASE [ALL] FOUR.
Attention, l’option [ALL] détruit tous les descendants même dans un set.
Utilisation de MODIFY
Soit à ajouter 10 au statut du fournisseurF3.
FIND FOUR OCCURRENCE FOR ’F3’.
GET FOUR.
ADD 10 TO STATUT IN FOUR.
MODIFY FOUR.
Par exemple, on cherche à connecter une occurrence de S pour une valeur S4 dans
l’occurrence SETX possédée par l’occurrence x de X.
64 CHAPITRE 5. MODÈLES CLASSIQUES DE BD
FIND ... x.
FIND S OCCURRENCE FOR S4.
CONNECT S TO SETX.
FIND ... x.
FIND S OCCURRENCE FOR S4.
RECONNECT S WITHIN SETX.
La recherche du propriétaire d’un set est réalisée par l’utilisation du mot clé OWNER.
FIND OWNER F-FP.
Considérons l’exemple suivant où l’on souhaite trouver les valeurs de numéros de
produits PNO pour le fournisseur de numéro F3.
5.5. EXEMPLES DE MANIPULATION DE DONNÉES 65
Imbrication de boucles
Considérons l’exemple consistant à trouver un autre produit de même fournisseur pour
tous les fournisseurs du produit P4, et à imprimer le nom du fournisseur et le numéro de ce
produit.
MOVE ’P4’ TO PNO IN PROD.
FIND ANY P USING PNO IN P.
MOVE ’NO’ TO EOF.
PERFORM UNTIL EOF=’YES’.
FIND NEXT FP WITHIN P-FP.
IF EOF NOT =’YES’
FIND OWNER WITHIN F-FP.
GET FOUR.
MOVE ’NO’ TO FOUND..
PERFORM UNTIL FOUND =’YES’.
66 CHAPITRE 5. MODÈLES CLASSIQUES DE BD
Le modèle relationnel
Le modèle réseau est une évolution logique du modèle hiérarchique. Par rapport à
ce dernier, le modèle réseau a permis l’élimination des redondances de données et la
création de chemins d’accès multiples à une même donnée. Ces SGBD sont répandus dans
les organisations qui utilisent des bases de données, car ils permettent une assez bonne
adéquation entre les contraintes de fidélité au réel perçu et de simplicité d’utilisation.
Cependant, il ne permet pas la prise en compte de phénomènes plus complexes et ne résout
pas tous les problèmes d’indépendance des structures de données et des traitements. C’est
pourquoi une troisième approche pour la résolution de ces problèmes a conduit aux modèles
de quatrième génération : les modèles relationnels.
Le modèle relationnel est dû a Codd [23, 24] et a pris une importance grandissante dans
le domaine du traitement et le stockage des données informatiques. La principale raison
de son succès est sa simplicité technique : les relations manipulées par ce modèle sont des
tableaux à deux dimensions, au sens le plus ordinaire du terme, avec lignes et colonnes.
C’est pourquoi ce modèle est qualifié de plat.
Les éléments contenus à l’intersection d’une ligne et d’une colonne sont les données
stockées. Un sous ensemble d’une ligne ou d’une colonne constitue une information. Tout
le problème est de bien définir les variables qui constituent ces tableaux. Cela relève de
l’analyse fonctionnelle et qui peut être parfois difficile à résoudre (car on veut modéliser le
monde réel).
Ce modèle dispose lui aussi d’un langage de manipulation de données (LMD) et un
langage de description de données (LDD). Un vocabulaire spécifique, issu en grande partie
de la théorie des ensembles, accompagne la description de ce type de bases de données.
Dans ce chapitre, nous étudierons en détail ce modèle.
67
68 CHAPITRE 6. LE MODÈLE RELATIONNEL
Type Ordinateur
type cons pays rev nb postes mem max uc capa lec capa disk
Micral 75 Bull France Camif 1 512 IN80486 1044 40
Mac II Apple USA Apple 1 256 Mo68020 1044 60
Système
Logiciel de base
Logiciel
Configuration
Installation
Compatibilité
6.3 Définitions
Le but du modèle relationnel est de percevoir une réalité sous la forme de tableaux de
valeurs appelés relations 1 :
– une relation a un nom ;
– une colonne d’une relation est un attribut ;
– une ligne d’une relation est un n-uplet ou un tuple ;
– l’ordre des lignes et des colonnes n’a pas d’importance (car c’est une relation ensem-
bliste).
On doit pouvoir interpréter chaque ligne d’un tel tableau à l’aide d’une phrase simple
et cohérente.
6.3.1 Domaine
Dans un premier temps, puisque nous allons travailler avec des relations, il faut définir
sur quels ensembles les relations vont porter. On introduit pour cela la notion de domaine.
Définition 6.3.1.
Un domaine est un ensemble de valeurs que peut prendre un élément donné (souvent un
attribut). Il sera représenté par un type. 2
On remarquera que pour le modèle relationnel, les types utilisés doivent être atomiques :
entiers, réels, chaı̂nes de caractères etc ou sous-ensembles de ces types. On ne peut pas
utiliser par exemple comme type une structure complexe comme une liste ou un ensemble.
6.3.2 Relations
On peut fournir deux définitions équivalents d’une relation. Ces deux définitions sont
dites extensionnelles, car on explicite le contenu des relations.
1. On parlera également de tables
70 CHAPITRE 6. LE MODÈLE RELATIONNEL
Définition 6.3.2.
Soient R une relation et D1 , . . . , Dn les domaines utilisés pour définir les colonnes de la
relation (des domaines peuvent être identiques). Alors R peut être définie comme :
– un sous-ensemble du produit cartésien D1 × . . . × Dn
– un ensemble {t1 , . . . , tm } de n-uplets de valeurs tel que ∀i ∈ {1, . . . , m} ti =
(di,1 , . . . , di,n ) où pour tout j ∈ {1, . . . , n} di,j prend ses valeurs dans Dj .
On dira que m est la cardinalité de R.
On dira que n est l’ ordre de R. 2
Pour représenter une relation, on utilise la notation sous forme de tableau suivante :
D1 ... Di ... D1
t1 d1,1 ... d1,i ... d1,n
... ... ... ... ... ...
tj dj,1 ... dj,i ... dj,n
... ... ... ... ... ...
tm dm,1 ... dm,i ... dm,n
La relation peut également être représenté sous la forme d’un tableau comportant 4
n-uplets :
On peut remarquer que comme les relations sont des ensembles de n-uplets, l’ordre
de représentation des tuples importe peu. La représentation d’une relation n’est donc pas
unique.
6.3. DÉFINITIONS 71
Définition 6.3.3.
Un attribut est une fonction nommée d’une relation dans un de ses domaines. 2
Par exemple, dans la relation Logiciel de base, l’attribut type est une fonction vers
l’ensemble des chaı̂nes de caractères représentant les types d’ordinateurs.
Une case situé sur la colonne de l’attribut Att d’une relation contiendra donc une valeur
d’attribut prise dans le domaine de l’attribut Att.
Un attribut ou un groupe d’attributs dont la connaissance d’une valeur identifie un
n-uplet de la relation de façon unique est appelé superclé d’une relation.
Définition 6.3.4.
Soit R une relation possédant n attributs Att1 , . . . , Attn . L’ensemble non vide d’attributs
{Att c1 , . . . , Att cm } ⊆ {Att 1 , . . . , Att n } est une superclé de R si et seulement si :
Étant donnés deux tuples t1 = (d1,1 , . . . , d1,n ) et t2 = (d2,1 , . . . , d2,n ) de R, alors t1 = t2
si et seulement si ∀i ∈ {1, . . . , m} d1,ci = d2,ci . 2
Définition 6.3.5.
Soit {Att c1 , . . . , Att cm } une superclé d’une relation R. Si ∀i ∈ {1, . . . , m} {Att c1 , . . . , Att cm }−
{Att ci } n’est pas une superclé de R, alors {Att c1 , . . . , Att cm } est une clé de R. 2
Définition 6.3.6.
Le schéma d’une relation est définie de façon intensionnelle par l’énoncé de :
– son nom ;
– ses attributs ;
– des domaines de valeurs de ses attributs ;
72 CHAPITRE 6. LE MODÈLE RELATIONNEL
On note des deux façons suivantes la relation R, d’attributs Att 1 , . . . , Att n de domaines
respectifs D1 , . . . , Dn :
R {Att 1 : D1 , . . . , Att n : Dn }
R({Att 1 : D1 , . . . , Att n : Dn }) 2
On peut remarquer que le fait d’utiliser des ensembles pour les attributs d’une relation
montre bien que l’ordre importe peu. Il peut y avoir plusieurs schémas pour une même
relation. Lorsque les domaines sont omis, on dit qu’on obtient un schéma simplifié.
Lors de la construction du schéma d’une relation, on spécifie également la clé primaire
de la relation.
Définition 6.3.7.
Soit R une relation. La clé primaire de R est une clé {Att 1 , . . . , Att m } de R. Pour
représenter cette clé primaire, on souligne les attributs correspondants dans la relation. 2
Exemple 6.3.2 : Par exemple, le schéma simplifié associé à la relation Configuration est
le suivant :
Configuration {type, nom syst, nom log, mem min, disk min} 2
Nous venons donc de voir la définition en intension du schéma d’une relation. Une
définition en extension d’une relation est définie par l’ensemble de ses tuples.
Nous pouvons maintenant définir le schéma relationnel d’une base de données.
Définition 6.3.8.
Le schéma relationnel d’une base de données est défini par l’ensemble des schémas des
relations qui composent la base et un ensemble de contraintes appelé contraintes d’intégrité.2
Les contraintes d’intégrité seront définis plus en détail dans le chapitre 9. On peut
toutefois citer les contraintes correspondant aux clés étrangères qui spécifient qu’un ou
plusieurs attributs d’une relation doivent prendre leurs valeurs non pas dans leur domaine
en entier, mais dans les valeurs d’attributs apparaissant dans une autre relation.
Définition 6.3.9.
Soit R(∆) une relation. On dit que {Att 1 , . . . , Att n } ⊆ ∆ sont des clés étrangères réfé-
rençant les attributs {Att 0 1 , . . . , Att 0 n } d’une relation R0 ssi les valeurs prises par le tuple
hAtt 1 , . . . , Att n i ne peuvent être que des valeurs du tuple hAtt 0 1 , . . . , Att 0 n i apparaissant
dans des n-uplets de R0 . On le note de la façon suivante :
R {. . . , Att 1 , . . . , Att n }
{Att 1 , . . . , Att n } référence R0 {Att 0 1 , . . . , Att 0 n }
2
Exemple 6.3.3 : Par exemple, les valeurs d’attributs apparaissant dans la colonne
nom sys de la relation Logiciel de base ne peuvent apparaı̂tre que comme valeurs d’attribut
de la colonne nom sys de la relation Système. 2
Nous verrons dans 9 qu’un certain nombre de contraintes sont imposées sur les clés
étrangères (en particulier, elles sont souvent des clés primaires de la relation référencée).
6.4. CONCEPTION DU SCHÉMA RELATIONNEL 73
Principe 6.4.1.
1. pour chaque classe d’entités, on définit une relation composée
– des attributs et des domaines de la classe d’entités ;
– de la clé primaire de la classe d’entités.
2. pour chaque classe d’associations, on définit une relation composée
– des attributs et des domaines de la classe d’associations ;
– de la clé primaire de la classe d’associations. Cette clé est composée des clés
primaires de chacune des classes d’entités en liaison et des attributs propres de
l’association. 2
maintenant s’occuper des classes d’associations, en tenant compte des problèmes évoqués
dans la section précédente. L’algorithme ? ? présente la traduction associée.
On peut remarquer que s’il existe plusieurs classes d’entités dont le rôle a une multiplicité
[0, 1] ou [1, 1], il suffit de choisir une de ces entités pour appliquer le bloc « sinon » de
l’algorithme précédent.
Exemple 6.4.2 : Supposons que nous ayons le schéma entité-association présenté sur la
figure 6.1.
On obtient alors comme schémas de relations :
E1 {Att 1,1 , . . . , Att 1,c1 , Att 1,(c1 +1) , . . . , Att 1,n1 }
E2 {Att 2,1 , . . . , Att 2,c2 , Att 2,(c2 +1) , . . . , Att 2,n2 }
E3 {Att 3,1 , . . . , Att 3,c3 , Att 3,(c3 +1) , . . . , Att 3,n3 }
E4 {Att 4,1 , . . . , Att 4,c4 , Att 4,(c4 +1) , . . . , Att 4,n4 }
A {Att A,1 , . . . , Att A,nA , Att 1,1 , . . . , Att 1,c1 , Att 2,1 , . . . , Att 2,c2 , Att 3,1 , . . . , Att 3,c3 ,
Att 4,1 , . . . , Att 4,c4 }
2
Exemple 6.4.3 : Supposons que nous ayons le schéma entité-association présenté sur la
figure 6.2.
On obtient alors comme schémas de relations :
76 CHAPITRE 6. LE MODÈLE RELATIONNEL
E1 E3
[1, n] [1, n]
[1, n] [1, n]
E2 E4
E1 E3
[1, 1] [1, n]
[1, n] [1, n]
E2 E4
E1 {Att 1,1 , . . . , Att 1,c1 , Att 1,(c1 +1) , . . . , Att 1,n1 , Att A,1 , . . . , Att A,nA , Att 2,1 , . . . , Att 2,c2 ,
Att 3,1 , . . . , Att 3,c3 , Att 4,1 , . . . , Att 4,c4 }
E2 {Att 2,1 , . . . , Att 2,c2 , Att 2,(c2 +1) , . . . , Att 2,n2 }
E3 {Att 3,1 , . . . , Att 3,c3 , Att 3,(c3 +1) , . . . , Att 3,n3 }
E4 {Att 4,1 , . . . , Att 4,c4 , Att 4,(c4 +1) , . . . , Att 4,n4 }
2
Il se peut qu’en utilisant ces principes on obtienne des clés composées de plusieurs
attributs qui soient simplifiables. Dans ce cas, il faut simplifier les clés et regrouper les
éventuelles relations possédant des clés identiques issues de ces simplifications. L’algorithme
? ? présente cette simplification.
Attention, il ne faut pas pour autant regrouper systématiquement deux relations ayant
des attributs de noms identiques. Par exemple, les locations et les ventes de voitures sont
représentables par deux relations semblables :
6.4. CONCEPTION DU SCHÉMA RELATIONNEL 77
Cet algorithme correspond bien à l’idée intuitive que l’on se fait d’une entité faible.
On remarquera que l’on ne s’occupe pas de la traduction des associations supports, car
celles-ci ne seront pas traduites en vertu de l’algorithme ? ?.
78 CHAPITRE 6. LE MODÈLE RELATIONNEL
Chapitre 7
Algèbre relationnelle
79
80 CHAPITRE 7. ALGÈBRE RELATIONNELLE
7.2.1 Union
Le résultat de l’union de R et de S est l’ensemble des tuples qui sont dans R ou dans S.
Syntaxe :
(MA) R∪S
(IN) UNION(R,S)
2
7.2.2 Intersection
Le résultat de l’intersection de R et de S est l’ensemble des tuples qui sont à la fois
dans R et dans S.
Syntaxe :
(MA) R∩S
(IN) INTER(R,S)
2
7.2.3 Différence
Le résultat de la différence de R et de S est l’ensemble des tuples qui sont dans R mais
pas dans S.
Syntaxe :
(MA) R−S
(IN) DIFF(R,S)
2
7.3 Projection
La projection est un opérateur qui permet à partir d’une relation R de créer une relation
identique à R mais qui ne possède que certaines colonnes de R.
Syntaxe :
(MA) πAi ,Aj ,...,Al (R)
(IN) PROJ(R, Ai, Aj,..., Al)
2
La valeur de la requête πAi ,Aj ,...,Al (R) est donc une relation de schéma {Ai , Aj , . . . , Al }
qui est la restriction de R aux colonnes Ai , Aj , . . . , Al (dans cet ordre). Comme on ne
travaille qu’avec des ensembles, les tuples identiques sont éliminés.
Exemple 7.3.1 : Considérons la requête ayant pour résultat la liste des numéros de
référence et des types de tous les ordinateurs ?
Cette requête est la projection de la relation Ordinateur sur les attributs ref et prix,
soit πref , prix (Ordinateur ). On obtient la relation suivante :
ref type
10 Micral 75
11 Goupil G4
25 Mac II
2
82 CHAPITRE 7. ALGÈBRE RELATIONNELLE
7.4 Sélection
L’opérateur de sélection appliqué à une relation R produit une nouvelle relation définie
à partir d’un sous-ensemble des tuples de R. Les tuples sélectionnés sont ceux qui vérifient
une propriété P impliquant les attributs de R. P est une expression logique, dont la valeur
est soit vraie, soit faux. On peut définir une expression logique formellement de la façon
suivante :
Définition 7.4.1.
Soient a et b deux attributs ou constantes. Alors :
– a < b, a ≤ b, a = b, a ≥ b, a > b, a 6= b sont des termes ;
– si t1 et t2 sont des termes, t1 ET t2 et t1 OU t2 sont des expressions logiques ;
– si e1 et e2 sont des expressions logiques, e1 ET e2 et e1 OU e2 sont des expressions
logiques. 2
Nous avons utilisé ici les opérateurs arithmétiques classiques, mais on peut également
utiliser d’autres opérateurs qui correspondent aux types des attributs de la relation.
Le résultat de la requête σP (R) est une relation de schéma identique à celui de R, soit
{A1 , A2 , . . . , An }. Les tuples présents dans le résultat de la requête sont ceux qui vérifient
P.
Exemple 7.4.1 : Considérons la requête ayant pour résultat les caractéristiques des
SGBD qui coûtent moins de 1000 F.
Cette requête est la sélection de tous les logiciels de classe SGBD et tels que leur prix
est inférieur à 1000 F, soit σclasse = ”SGBD” ET prix < 1000 (Logiciel ). On obtient une relation
de même schéma que Logiciel qui ne contient aucun tuple. 2
On peut remarquer que les opérations de projection et de sélection sont des opérations
d’extraction respectivement verticale et horizontale.
Syntaxe :
(MA) R×S
(IN) PROD(R, S)
2
D E
d1 e1
S
d2 e2
d3 e3
Le résultat du produit cartésien R × S est la table suivante :
A B C D E
a1 b1 c1 d1 e1
a1 b1 c1 d2 e2
R×S a1 b1 c1 d3 e3
a2 b2 c2 d1 e1
a2 b2 c2 d2 e2
a2 b2 c2 d3 e3
2
Si deux attributs dans les relations R et S ont le même nom, par exemple A, on peut
les distinguer en les préfixant par le nom de la relation : R.A et S.A.
7.6 Jointures
Les opérateurs de jointures sont des opérateurs qui permettent de construire une relation
à partir de deux relations qui ont une propriété commune (représentée par un attribut
« commun »). Nous allons examiner différents types de jointures.
Syntaxe :
(MA) R 1 S
(IN) JOIN(R, S)
2
3. On dit que deux attributs sont communs s’ils portent le même nom.
84 CHAPITRE 7. ALGÈBRE RELATIONNELLE
B C D
2 5 6
S
4 7 8
9 10 11
Le résultat de la jointure naturelle R 1 S est la table suivante :
A B C D
R1S 1 2 5 6
3 4 7 8
2
7.6.2 θ-jointure
La jointure naturelle nous permet de « fusionner » deux relations possédant des
attributs communs. Il peut être également intéresser de grouper deux relations n’ayant
pas d’attributs communs en utilisant une propriété à vérifier par les tuples. Pour cela, on
utilise une θ-jointure.
Exemple 7.6.2 : Par exemple, considérons la requête ayant pour résultat la provenance
des systèmes d’exploitation installables sur Micral 75.
La provenance des systèmes d’exploitation est définie par l’attribut prov de la relation
Système. Les systèmes d’exploitation installables sur Micral 75 vont être définis par l’attribut
nom sys de la relation Logiciel de Base.
La requête considérée est donc :
7.7. OPÉRATEUR DE RENOMMAGE 85
Syst ème 1type = ”Micral75” AND Syst ème.nom sys = Logiciel de Base.nom sys Logiciel de Base
2. sélection des tuples suivant le critère type = ”Micral75” AND Syst ème.nom sys =
Logiciel de Base.nom sys :
provenance
USA
2
Syntaxe :
(MA) ρS(Ai1 ,...,Ain ) (R)
(IN) REN(R, S, (Ai1,..., Ain))
2
Le résultat de ρS(Ai1 ,...,Ain ) (R) est une relation de nom S qui possède les mêmes tuples
que R, mais dont les attributs sont nommés Ai1 , . . . , Ain dans cet ordre.
πtype,concept
On suppose ici que les variables R, S, T et Reponse ont les schémas adéquats.
– MIN(A) = 1
– COUNT(A) = 3 2
7.10.2 Regroupements
Les opérateurs d’agrégation permettent de réduire une colonne d’une relation à une
valeur. Il se peut parfois que l’on ne veuille pas appliquer un opérateur d’agrégation à
l’ensemble d’une relation, mais l’appliquer séparément à des ensembles de tuples de la
relation. Par exemple, on pourrait chercher quel est le prix le moins cher dans chaque
classe de logiciel. Il faut regrouper les tuples de la relation Logiciel grâce à l’attribut classe,
puis appliquer MIN sur les colonnes désignées par prix dans chacune des classes obtenues.
L’opérateur de regroupement permet de grouper les tuples d’une relation suivant différentes
classes en utilisant les attributs de la relation.
Syntaxe :
γL (R)
où L est une liste d’éléments qui peuvent être :
– un attribut de la relation R. On dit que c’est un attribut de regroupement ;
– un opérateur d’agrégation appliqué à un attribut de la relation. Le nom de l’attribut
correspondant dans la relation résultat est précisé par le signe →. L’attribut ainsi
créé est appelé attribut d’agrégat.
2
Définition 7.10.1.
La relation retournée par γL (R) est construite de la façon suivante :
1. partitionnement des tuples de R. Chaque partition est composée des tuples ayant un
assignement particulier pour les attributs de regroupement de L ;
2. pour chaque groupe, construire un tuple constitué :
(a) des attributs de regroupement de L ;
(b) des agrégations calculées sur les tuples du groupe des attributs d’agrégats de L
2
Il faut bien remarquer que l’on ne construit qu’un seul tuple par groupe.
Syntaxe :
◦
– R 1P S est une jointure externe sur R et S suivant la condition P ;
◦
– R L 1P S est une jointure externe sur R et S suivant la condition P . On ne considère
que les tuples de R pour la jointure externe ;
◦
– R R 1P S est une jointure externe sur R et S suivant la condition P . On ne considère
que les tuples de S pour la jointure externe
2
7.11 Conclusion
L’algèbre relationnelle permet d’exprimer de façon très simple des requêtes sur des
relations. On ne dispose en effet que d’une seule structure de contrôle, la composition (on
n’a ni branchement conditionnel, ni répétition).
Par contre, il existe plusieurs solutions possibles pour construire une requête. Par
exemple, considérons la question suivante : « quel sont les nombres de lecteurs de disquettes
90 CHAPITRE 7. ALGÈBRE RELATIONNELLE
des ordinateurs en possédant plus de 2 ? ». La réponse à cette question peut être exprimée
comme le résultat de deux requêtes différentes :
L’algèbre relationnelle présentée dans le chapitre 7 est un moyen puissant de calcul sur
les relations. Elle permet de construire une relation contenant les éléments sur lesquels
porte une requête sur une base de données. L’algèbre relationnelle peut être assimilée à un
langage impératif : on doit décrire comment est obtenue la relation « réponse ».
L’objectif d’un langage assertionnel est d’offrir à l’utilisateur un moyen d’exprimer
une requête en ne décrivant que le résultat recherché à l’aide d’une assertion (expression
logique) sans devoir expliquer la façon de le trouver. En d’autres termes, l’assertion exprime
le « quoi » et non le « comment ». Dans ce cas, l’utilisateur qualifie les résultats et le
système compose la procédure de recherche.
Les objets manipulés par SQL sont les suivants :
– des tables : relations ou vues externes (cf. section 8.3.6) ;
– des colonnes (columns) attributs de « base » des relations ou des vues externes ou des
attributs calculés à partir des attributs de base (via des agrégations par exemple) ;
– des lignes (rows) qui représentent les tuples.
Des compléments sur ce chapitre peuvent être trouvés dans [20, 19, 27, 18].
91
92 CHAPITRE 8. SQL (STRUCTURED QUERY LANGUAGE)
Nous allons présenter ces trois langages dans ce qui suit. Nous considérerons une relation
R d’attributs A1 , . . . , An .
Les clauses GROUP, ORDER et HAVING sont optionnelles. Nous pouvons détailler informel-
lement la sémantique des clauses FROM, WHERE et SELECT :
– FROM permet de préciser quelles sont la ou les relations sur lesquelles on pose la
requête ;
– WHERE permet de poser une assertion qui doit être vérifiée par les lignes de la table
solution ;
– SELECT désigne les attributs des tuples qui doivent apparaı̂tre dans la solution.
Le bloc de qualification « simple » (i.e. ne contenant que les clauses SELECT, FROM et
WHERE) est donc une combinaison d’une projection et d’une sélection.
8.2.2 Projection
La syntaxe d’une projection simple est présentée dans ce qui suit.
Syntaxe (projection) :
SELECT Ai,..., Ap
FROM R;
Exemple 8.2.1 : La requête « quelle est la liste des numéros de référence et des types
de tous les ordinateurs ? » s’exprime de la façon suivante :
SELECT ref, type
FROM Ordinateur;
1. SQL est un langage insensible à la casse des caractères, que ce soit pour ses mots clés ou pour les
noms de relations ou d’attributs.
94 CHAPITRE 8. SQL (STRUCTURED QUERY LANGUAGE)
Remarquons que l’on peut obtenir des tuples identiques, en particulier si on ne sé-
lectionne pas la clé primaire parmi les colonnes de la projection. Pour éviter cela, on
peut utiliser le mot clé DISTINCT (on voit ici apparaı̂tre le fait qu’on travaille avec des
multi-ensembles dans SQL).
8.2.3 Sélection
Syntaxe (sélection) :
SELECT *
FROM R
WHERE F;
Ici, « * » désigne l’ensemble des colonnes de la relation R. F est une expression logique
permettant de sélectionner les colonnes.
La valeur NULL
La valeur NULL est une valeur qui peut être affectée à certains attributs lorsque :
– on ne sait pas quelle est la valeur de l’attribut ;
– aucune valeur ne peut convenir pour l’attribut ;
8.2. LE LANGAGE DE MANIPULATION DE DONNÉES 95
L’entête du résultat affiché portera les noms Nouveau nom 1 et Nouveau nom 2 au lieu
de col1 et col2.
Exemple 8.2.2 : On peut demander quels sont les prix en euros des ordinateurs :
SELECT prix / 6.55957 AS prix_euro
FROM Ordinateur ;
Exemple 8.2.3 : La requête permettant d’éditer le catalogue des ordinateurs dans l’ordre
des prix décroissants et des capacités de mémoire croissantes est :
SELECT *
FROM Ordinateur
ORDER BY prix DESC, capa_mem ASC ;
96 CHAPITRE 8. SQL (STRUCTURED QUERY LANGUAGE)
On travaille toujours avec des noms de variable dans une requête. Par défaut, si l’on ne
précise pas un nom d’alias, le nom utilisé est celui de la relation. Par exemple, FROM R est
en fait FROM R AS R.
Exemple 8.2.5 : Considérons la requête « quels sont les types, prix et constructeurs des
ordinateurs de référence 10 ? » est exprimée de la façon suivante :
8.2. LE LANGAGE DE MANIPULATION DE DONNÉES 97
8.2.8 Jointures
On peut exprimer différents tous les types de jointures avec SQL (jointure naturelle,
θ-jointure, jointure externe). Nous allons les détailler rapidement dans ce qui suit.
Produit cartésien
La forme la plus simple de jointure en SQL est ce que nous avons appelé le produit
cartésien en algèbre relationnelle.
θ-jointure
La θ-jointure est la forme la plus générale de jointure. Rappelons que l’on peut la voir
comme la composition d’un produit cartésien et d’une sélection.
Syntaxe (θ-jointure) :
R JOIN S ON F
SELECT provenance
FROM Systeme NATURAL JOIN LogicielBase ;
SELECT provenance
FROM Systeme JOIN LogicielBase USING nom_sys ;
La différence la syntaxe précédente est que l’utilisation de NATURAL impose une égalité
sur tous les attributs portant le même nom.
– les jointures externes qui sont de trois types :
– les jointures externes complètes par FULL OUTER JOIN ;
– les jointures externes à gauche par LEFT OUTER JOIN ;
– les jointures externes à droite par RIGHT OUTER JOIN.
Les jointures externes permettent d’utiliser toutes les tuples d’une ou des deux relations,
même si certains tuples ne devraient pas apparaı̂tre dans la jointure normale (on dit interne).
On complète alors les valeurs manquantes dans ces tuples par NULL.
Par exemple, si l’on considère les deux relations suivantes :
A B
R1 -10.5 15
-20.1 30
B C
R2 15 toto
40 titi
alors voici les résultats de quelques requêtes comportant des jointures, interne et
externes :
– SELECT * FROM R1 JOIN R2 ;
A B C
-10.5 15 toto
On remarquera que l’on peut préciser explicitement que l’on fait une jointure interne
en utilisant SELECT * FROM R1 INNER JOIN R2 ;.
– SELECT * FROM R1 LEFT OUTER JOIN R2 ;
8.2. LE LANGAGE DE MANIPULATION DE DONNÉES 99
A B C
-10.5 15 toto
-20.1 30 NULL
Exemple 8.2.6 : Considérons la requête « quels sont les types d’ordinateurs compatibles
à la fois avec un Micral 75 et un Mac II ? ». Cette requête s’exrprime de la façon suivante :
(SELECT type_comp FROM Compatibilite
WHERE type_ref = ’Micral 75’)
INTERSECT
(SELECT type_comp FROM Compatibilite
WHERE type_ref = ’Mac II’);
Seul l’opérateur COUNT peut s’utiliser avec * : COUNT(*) permet de compter tous les
tuples résultats d’une requête.
Exemple 8.2.7 : La requête suivante permet de récupérer le prix moyen des logiciels :
SELECT AVG(prix)
FROM Logiciel ;
8.2.11 Regroupements
Nous avons également vu les regroupements dans la section 7.10.2. Les regroupements
permettent de créer des classes distinctes dans les tuples d’une relation suivant un critère
et d’appliquer un opérateur d’agrégation à chacune de ces classes.
Syntaxe (regroupement) :
SELECT A1, OP1(A2)
FROM R
WHERE F
GROUP BY P ;
GROUP BY doit se trouver après la clause WHERE. P est une liste d’attributs de regroupe-
ment, définie comme suit :
Définition 8.2.1.
Soit
8.2. LE LANGAGE DE MANIPULATION DE DONNÉES 101
SELECT A1,...,Ap
FROM R
WHERE F
GROUP BY P ;
une requête SQL où chaque Ai est soit un attribut, soit un opérateur d’agrégation
appliqué à un attribut. Alors seuls les attributs apparaissant dans P peuvent apparaı̂tre sans
opérateur d’agrégation dans la clause SELECT. 2
Ceci correspond bien à la définition donnée en section 7.10.2 qui définissait la relation
renvoyée par un opérateur de regroupement. En effet, si l’on autorisait des attributs
n’apparaissant pas dans la clause GROUP BY à apparaı̂tre comme opérandes de l’opérateur
de sélection, quelle valeur de ces attributs choisir lorsque l’on construit la table résultat ?
Exemple 8.2.8 : Considérons la requête suivante : « quelle est la liste des types de
machines installées, avec pour chaque type le nombre d’exemplaire de machines ? ». Elle
s’exprime en SQL par :
SELECT type, COUNT(ref)
FROM Ordinateur
GROUP BY type;
Les agrégations sont bien entendu calculées en tenant compte des regroupements.
Exemple 8.2.9 : Considérons la requête suivante : « quelle est la liste des types de
machines installées, avec le nombre d’exemplaires, dont la capacité mémoire de chaque
exemplaire atteint ou dépasse 512 Ko et dont le nombre d’exemplaires installé atteint ou
dépasse 2 ? ». En ajoutant un tri par nombre croissant d’exemplaires, la requête s’écrit :
102 CHAPITRE 8. SQL (STRUCTURED QUERY LANGUAGE)
8.2.13 Sous-requêtes
Dans SQL, une requête peut elle-même être composée avec une autre requête. On parle
alors de sous-requêtes. Il existe plusieurs types de sous-requêtes :
– les sous-requêtes qui retournent une relation et qui sont utilisées dans une clause
FROM. Dans ce cas, il faut leur donner un nom d’alias pour pouvoir les utiliser ;
– les sous-requêtes qui retournent une constante. Dans ce cas, on peut les utiliser dans
la clause WHERE d’une requête. Par exemple :
SELECT prix
FROM Ordinateur
WHERE ref =
(SELECT ref
FROM Installation
WHERE nom_sys = ’UNIX’);
– les sous-requêtes qui retournent une relation et qui sont utilisées dans une clause
WHERE.
Dans ce dernier cas, on peut utiliser différents opérateurs, qui s’appliquent à une relation
R et renvoient un booléen :
– EXISTS R qui est vraie si et seulement si R n’est pas vide ;
– s IN R est vraie si et seulement si s apparaı̂t dans R (s peut être un tuple sous la
forme (A1, A2) par exemple). Le contraire de s IN R est s NOT IN R ;
– s OP ALL R où OP est un opérateur de comparaison est vraie si et seulement si s OP t
est vraie pour tout tuple t de R ;
– s OP ANY R où OP est un opérateur de comparaison est vraie si et seulement si s OP t
est vraie pour au moins un tuple t de R.
Exemple 8.2.10 : Considérons la requête « quelles sont les provenances des systèmes
installables sur un Micral 75 ? ». Elle s’écrit :
SELECT provenance
FROM Systeme
WHERE nom_sys IN
(SELECT nom_sys
FROM LogicielBase
WHERE type = ’Micral 75’)
8.2. LE LANGAGE DE MANIPULATION DE DONNÉES 103
Exemple 8.2.11 : Considérons la requête « quels sont les noms de logiciels qui ne sont
installés sur aucune machine ? ». Elle s’écrit :
SELECT nom, classe
FROM Logiciel
WHERE NOT EXISTS
(SELECT nom_log
FROM Installation
WHERE nom = nom_log);
8.2.14 Insertion
SQL propose deux formes d’insertion de données :
– une insertion de valeurs fournies en entrée (par « extension ») :
vk est une constante ou une expression calculée. La valeur vk est celle de l’attribut
Ak de la ligne en cours de remplissage. Si les Ak sont omis, les valeurs sont affectées
dans l’ordre de déclaration des colonnes.
Si un attribut n’apparaı̂t pas dans la liste, sa valeur est la valeur par défaut de son
type (NULL la plupart du temps).
ON peut insérer plusieurs tuples avec la même instruciton (attention, dans ce cas
l’insertion de tous les tuples est considérée comme une transaction, cf. chapitre 12) :
INSERT
INTO R(Ai, Aj,..., Ap)
SELECT Ci, Cj,..., Cp
FROM ...;
8.2.16 Suppression
La suppression de tuples d’une table se fait par la construction DELETE.
Le mot-clé DEFAULT permet de donner une valeur par défaut à l’attribut. Si aucune
valeur par défaut n’est précisée à la création de la table, NULL est utilisé.
Nous verrons dans le chapitre 9 comment écrire des contraintes sur les tables à la
création.
Cette requête supprime non seulement le contenu de la relation, mais également son
schéma.
ALTER TABLE R
DROP ATT2;
8.3.5 Index
Nous présentons rapidement les index dans cette section. Leur utilisation permet souvent
d’améliorer les performances lors de requêtes sur une base de données.
Définition 8.3.1.
Un index sur un attribut A d’une relation R est une structure de données efficace pour
trouver les tuples qui ont une valeur fixée pour A. 2
Lorsque les relations ont un nombre très important de tuples, il devient très coûteux
lors d’une requête de vérifier que tous les tuples de la relation vérifient une propriété. Par
exemple, supposons que l’on veuille les types des ordinateurs ayant un prix de 10 000 F,
et que la relation Ordinateur possède 10 000 tuples. Dans ce cas, on va devoir faire 10
000 vérifications alors qu’il ne risque d’avoir que quelques tuples vérifiant la condition.
L’utilisation d’un index sur l’attribut prix permet de trouver tout de suite les tuples
cherchés 3 .
Syntaxe (création d’un index) :
CREATE INDEX NomIndex ON R(ATT1,...,ATTn);
Remarquons que l’on peut créer un index sur plusieurs attributs. L’ordre des attributs
est alors important pour l’efficacité de l’optimisation. Il faut mettre en premier les attributs
susceptibles d’être les plus utilisés.
Syntaxe (destruction d’un index) :
DROP INDEX NomIndex;
Remarque : les index doivent être eux-mêmes stockés sur le disque, donc consomment
de la place. De plus, ils rendent plus difficiles les opérations de mise à jour. Il faut donc
réfléchir à l’usage « courant » de la base qui va être fait (beaucoup de modifications,
beaucoup de requêtes) avant de créer un index sur certains attributs d’une relation. 2
Exemple 8.3.2 : Considérons la requête qui permet de construire la vue Catalogue. Elle
contient le nom, la classe, le nombre d’installations et le prix des logiciels disponibles. Elle
s’écrit :
CREATE VIEW Catalogue (nom, nombre prix)
AS
(SELECT L.nom, count(I.reference), L.prix
FROM Logiciel L, Installation I
WHERE L.nom = I.nom_log
GROUP BY L.nom);
La destruction d’une vue n’affecte en rien les tables à partir desquelles elle était
construite.
8.3. LE LANGAGE DE DESCRIPTION DE DONNÉES 109
L’option RESTRICT (par défaut) permet de préciser que si l’on fait référence à cette
vue dans une autre vue ou dans une contrainte d’intégrité, la suppression va échouer.
Au contraire, l’option CASCADE va permettre de détruire automatiquement les vues et les
contraintes d’intégrité faisant référence à la vue.
Modifications de vues
On peut exécuter des modifications sur les tuples d’une table au travers d’une vue en
respectant certaines conditions.
Hypothèse 8.3.1.
Une vue peut être mise à jour si et seulement si :
– la requête SELECT ne porte que sur une seule relation R et ne possède pas d’agrégats
ni de regroupements ;
– la clause WHERE ne contient pas R dans une sous-requête ;
– la liste dans la clause SELECT (sans DISTINCT) doit contenir suffisamment d’attributs
pour pouvoir remplir les autres attributs de la relation avec leur valeur par défaut.2
Les vues sont utilisées pour la consultation d’informations. Plus précisément, elles sont
définies pour :
– donne une vision plus lisible d’un schéma, adaptée à un utilisateur ;
110 CHAPITRE 8. SQL (STRUCTURED QUERY LANGUAGE)
– permettre à un utilisateur d’être protégé contre les éventuels changements des relations
de la base de données (si l’on change une relation on peut toujours construire une
vue qui correspond à ce que l’utilisateur avait auparavant) ;
– fixer les accès à un schéma par la pose de droits d’accès (cf. section 8.4) ;
– simplifier une requête complexe en compilant dans une vue une partie de la requête
(réutilisation).
8.4.2 Privilèges
SQL définit neuf types de privilèges : SELECT, INSERT, DELETE, UPDATE, REFERENCES,
USAGE, TRIGGER, EXECUTE et UNDER. Nous nous intéresserons plus particulièrement aux
quatre premiers 4 .
Comme leur nom l’indique, les privilèges SELECT, INSERT, DELETE, UPDATE permettent
respectivement de poser une requête sur une relation, d’insérer un tuple dans une relation,
d’effacer un tuple d’une relation et de mettre à jour une relation.
On peut remplacer les privilèges par ALL qui est un raccourci pour tous les privilèges.
PUBLIC comme nom d’utilisateur représente l’ensemble des utilisateurs répertoriés dans la
base de données.
Exemple 8.4.1 : Considérons une base de données avec quatre utilisateurs de noms
d’usage yamine, bernard, serge et christophe. yamine est le propriétaire des relations
Ordinateur et Logiciel. Il donne les privilèges SELECT et INSERT à bernard et serge sur
Ordinateur et SELECT à bernard et serge sur Logiciel :
bernard donne à christophe les mêmes privilèges, mais sans l’option GRANT OPTION :
serge autorise quant à lui christophe à insérer un tuple dans Logiciel en n’utilisant
que l’attribut ref, et à utiliser SELECT sur les deux relations, tout cela sans GRANT OPTION :
GRANT SELECT, INSERT(ref) ON Ordinateur TO christophe
GRANT SELECT ON Logiciel TO christophe
christophe a reçu ses privilèges SELECT sur les deux relations par deux utilisateurs
différents. Il a également reçu le privilège INSERT(ref) deux fois : une fois indirectement
par bernard qui lui donne un privilège INSERT sur toute la relation Ordinateur et une
fois via le privilège INSERT donné par serge.
On peut résumer ce réseau de privilège via la figure 8.1. Une * représente le fait qu’un
utilisateur a une GRANT OPTION sur le privilège en question. ** indique que le privilège
dérive du fait que l’utilisateur est le propriétaire de la relation. Les traits en pointillés
indique une délégation sans GRANT OPTION.
bernard serge
SELECT ON SELECT ON
Ordinateur Ordinateur
* *
bernard serge
SELECT ON SELECT ON
Logiciel * Logiciel *
bernard serge
INSERT ON INSERT ON
Ordinateur Ordinateur
* *
christophe
christophe christophe christophe
INSERT(ref)
INSERT ON SELECT ON SELECT ON
ON
Ordinateur Ordinateur Logiciel
Ordinateur
– CASCADE, et dans ce cas on retire également les privilèges cités qui n’avaient été
fournis que par les utilisateurs listés dans TO à d’autres utilisateurs ;
– RESTRICT qui permet d’empêcher le retrait d’un privilège si ce privilège avait été
donné à d’autres utilisateurs.
On peut également utiliser REVOKE GRANT OPTION FOR pour retirer le pouvoir d’octroyer
un privilège.
Exemple 8.4.2 : Reprenons l’exemple 8.4.1. Supposons que yamine retire les privilèges
de serge de la façon suivante :
REVOKE SELECT, INSERT ON Ordinateur TO serge
REVOKE SELECT ON Logiciel TO serge;
GRANT ...
ON VueOrdinateur
...
Les vues sont donc également un élément important des mécanismes de sécurité du
langage SQL.
114 CHAPITRE 8. SQL (STRUCTURED QUERY LANGUAGE)
bernard
SELECT ON
Ordinateur
*
bernard
SELECT ON
Logiciel *
bernard
INSERT ON
Ordinateur
*
dans R1
R1 JOIN R2 t[R1] est inséré dans R1 t[R1] est effacée de R1 t[R1] vérifie P1 et
et t[R2] est inséré dans et t[R2] est effacée de t[R2] vérifie P2
R2 R2
Table 8.1 : Conséquence des modifications d’une vue sur les relations sous-jacentes
115
116 CHAPITRE 8. SQL (STRUCTURED QUERY LANGUAGE)
Chapitre 9
Contraintes et triggers
Les informations qui sont stockées dans une base de données doivent la plupart du temps
respecter des contraintes d’intégrité. Par exemple, le prix d’un ordinateur doit toujours
être positif. Ces contraintes peuvent être plus compliquées : un attribut d’une relation doit
apparaı̂tre également dans une autre relation, un certain nombre d’assertions doivent être
vérifiées lors d’une mise à jour d’une relation etc.
Dans ce chapitre, nous allons étudier différents types de contraintes d’intégrité : les
contraintes de clé tout d’abord, puis les assertions en enfin les triggers.
Les possibilités offertes par les contraintes et les triggers ne sont parfois pas complétement
implantées dans les SGBD. Vous pouvez vous référer à l’annexe A pour plus de détails sur
le SGBD que nous allons utiliser, PostgreSQL.
117
118 CHAPITRE 9. CONTRAINTES ET TRIGGERS
Si la clé primaire est composée d’un ensemble d’attributs, on sépare la clause PRIMARY KEY
dans CREATE TABLE.
Syntaxe (clé primaire) :
CREATE TABLE Table (
ATT1 Type1,
...
ATTn Typen,
PRIMARY KEY (ATTi,...,ATTj)
);
Cette syntaxe est à privilégier : on sépare bien les contraintes de la déclaration des
attributs.
Lorsque l’on déclare un ensemble d’attributs S comme clé primaire de la relation R,
deux contraintes s’imposent :
– aucun attribut de S ne peut avoir la valeur NULL ;
– deux tuples de R ne peuvent avoir les mêmes valeurs pour tous les attributs de S.
Dans la plupart des SGBD, les contraintes de clés primaires induisent la construction
automatique d’un index sur les attributs de la clé.
Exemple 9.1.1 : Reprenons l’exemple 8.3.1. La relation Configuration possède une clé
primaire, qui est composée des attributs nom, nom sys et type. On le déclare donc de la
façon suivante :
CREATE TABLE Configuration (
nom VARCHAR(20),
nom_sys VARCHAR(20),
type VARCHAR(20),
mem_min INTEGER DEFAULT 4,
disk_min INTEGER DEFAULT 1,
PRIMARY KEY (nom, nom_sys, type)
);
– on peut avoir plusieurs déclarations UNIQUE pour une table, mais une seule déclaration
PRIMARY KEY ;
– un élément UNIQUE peut avoir la valeur NULL.
Comme pour PRIMARY KEY, il y a deux façons de déclarer une clé étrangère.
Politique en cascade
Une autre politique de gestion des clés étrangères est de répercuter tous les changements
nécessaires pour garder l’intégrité de la base. C’est une politique par cascade. Par exemple,
la modification d’un tuple de Logiciel dont l’attribut nom est modifié et apparaı̂t dans
un tuple de Configuration sera répercutée sur le tuple correspondant de Configuration.
Cette politique ne concerne que les cas 3 et 4 présentés ci-dessus.
Politique NULL
Une autre politique est de mettre les valeurs des champs de Configuration à NULL.
Cette politique est appelée set-NULL.
9.1. CLÉS PRIMAIRES ET CLÉS ÉTRANGÈRES 121
Il se peut que l’on ait un système de dépendances circulaires entre clés étrangères.
Dans ce cas, comment faire pour pouvoir mettre à jour un tuple d’une des relations en
question ? Il y aura toujours une violation de contrainte de clé dans ce cas. Une solution
est disponible :
– grouper les mises à jour dans une transaction (cf. chapitre 12) ;
– retarder la vérification de cohérence des contraintes de clés étrangères.
Toute contrainte peut être déclaré DEFERRABLE ou NOT DEFERRABLE (c’est cette dernière
option qui est prise par défaut). Une contrainte NOT DEFERRABLE sera vérifiée à chaque
changement de la base la concernant. Une contrainte DEFERRABLE ne sera vérifiée qu’à la
fin de la transaction courante. Dans le cas d’une contrainte DEFERRABLE, on peut choisir
de la rendre INITIALLY DEFERRED et elle ne sera vérifiée effectivement qu’à la fin de la
transaction, ou INITIALLY IMMEDIATE et elle sera vérifiée avant modification, mais on
garde l’option de retarder la vérification.
2
122 CHAPITRE 9. CONTRAINTES ET TRIGGERS
Remarque : un attribut déclaré UNIQUE et NOT NULL est une clé primaire. 2
F est une expression logique dans laquelle ATTi apparaı̂t. Si un autre attribut apparaı̂t
dans F, celui-ci doit provenir d’une clause SELECT incluse dans F. 2
La contrainte exprimée par CHECK sera vérifiée à chaque fois que l’attribut concerné
sera modifié par un ajout ou une mise à jour. Attention, elle ne sera pas vérifiée si on ne le
modifie pas (ce qui peut être problématique si la contrainte concerne un attribut d’une
autre relation qui est lui mis à jour).
9.2. CONTRAINTES SUR LES ATTRIBUTS ET LES TUPLES 123
Exemple 9.2.1 : Considérons la relation Configuration. Supposons que l’on dispose d’une
relation Mémoire qui nous fournisse les différentes configurations mémoire valides via un
attribut mem. Dans ce cas, on peut écrire :
CREATE TABLE Configuration(
...
mem_min NUMBER(4)
CHECK (mem_min IN (SELECT mem FROM Memoire)),
...
);
CHECK F
);
F est une expression logique dans laquelle un ou plusieurs attributs de Table appa-
raissent. Si un attribut d’une autre relation apparaı̂t dans F, celui-ci doit provenir d’une
clause SELECT incluse dans F. 2
Les contraintes CHECK exprimées sur un tuple sont vérifiées à l’insertion d’un tuple de
la relation ou à la mise à jour d’un tuple de la relation.
Cette syntaxe permet de donner un nom à une contrainte. Il devient plus lisible en
cas de message d’erreur de repérer quelle contrainte est mise en cause. De plus, on pourra
utiliser ce nom pour modifier la contrainte ou l’effacer.
Exemple 9.2.3 :
CREATE TABLE Configuration (
nom VARCHAR(20),
nom_sys VARCHAR(20),
type VARCHAR(20),
mem_min INTEGER DEFAULT 4,
disk_min INTEGER DEFAULT 1,
CONSTRAINT CleConf PRIMARY KEY (nom, nom_sys, type),
CONSTRAINT CleEt1Conf FOREIGN KEY (nom)
REFERENCES Logiciel(nom)
DEFERRABLE INITIALLY DEFERRED,
CONSTRAINT CleEt2Conf FOREIGN KEY (nom_sys)
REFERENCES Systeme(nom_sys)
DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT CleEt2Conf FOREIGN KEY (type)
REFERENCES TypeOrdinateur(type),
CONSTRAINT IntegriteConf CHECK ((mem_min >= 0) AND (disk_min >= 0))
);
2
9.3. ASSERTIONS ET TRIGGERS 125
On peut également ajouter ou enlever une contrainte en utilisant la clause ALTER TABLE
vue dans la section 8.3.4 :
Syntaxe (modification d’une contrainte) :
ALTER TABLE Table DROP CONSTRAINT NomContrainte;
ALTER TABLE Table ADD CONSTRAINT NomContrainte
definition de la contrainte;
9.3.1 Assertions
Une assertion est une contrainte générale qui se déclare comme suit.
Syntaxe (création d’assertion) :
CREATE ASSERTION NomAssertion CHECK (F);
F est une expression logique. 2
La condition exprimée par une assertion doit être vraie à la création de l’assertion et
sera vérifiée à chaque modification de la base (cela peut donc être très coûteux, surtout si
la modification ne « concerne » par l’assertion). Notons également que tous les attributs
qui apparaissent dans F doivent être sélectionnés au moyen d’une clause SELECT.
Remarque : Le SGBD PostgreSQL n’implante pas encore les assertions. 2
126 CHAPITRE 9. CONTRAINTES ET TRIGGERS
9.3.2 Triggers
Les triggers sont des règles d’actions qui sont déclenchées par un événement particulier,
le plus souvent l’insertion, la mise à jour ou l’effacement d’un tuple. Lors d’un événement,
les triggers correspondants sont réveillés. Si une condition associé à un trigger est vérifiée,
celui-ci exécutera un certain nombre d’actions. Cette approche est très souple : on peut
spécifier finement un comportement pour tout événement qui pourrait être problématique
(mise à jour, effacement etc.).
Syntaxe (trigger ) :
CREATE TRIGGER NomTrigger
[AFTER|BEFORE] [UPDATE [OF att]|INSERT|DELETE] ON Table
REFERENCING
[OLD ROW AS NomAncienTuple],
[NEW ROW AS NomNouveauTuple],
[OLD TABLE AS NomAncienneTable],
[NEW TABLE AS NomNouvelleTable]
[FOR EACH STATEMENT|FOR EACH ROW]
WHEN (C)
BEGIN [ATOMIC]
Action1;
...
ActionP;
END;
Ces références vont permettre par exemple de comparer les valeurs avant et après
modifications.
– la clause WHEN précise la condition du trigger. Elle est facultative ;
– les actions du trigger sont encadrées par les mot-clés BEGIN et END (facultatifs dans
le cas d’une seule action). ATOMIC permet de préciser si les actions doivent être
considérées comme étant des transactions.
Exemple 9.3.1 : Supposons que l’on veuille que la moyenne des prix des logiciels (cf.
relation Logiciel ) soit toujours inférieure à 10000. Si l’on fait une mise à jour sur l’ensemble
des tuples de la relation, il se peut que la moyenne dépasse 10000 après un certain nombre
de modifications, puis redevienne inférieure à 10000. Il faut donc créer un trigger de type
FOR EACH STATEMENT (une modification d’un tuple peut violer la contrainte, alors que le
résultat de plusieurs modifications ne la violeront pas).
CREATE TRIGGER TriggerMoyenneLogiciel
AFTER UPDATE OF prix ON Logiciel
REFERENCING
OLD TABLE AS AncienneTable,
NEW TABLE AS NouvelleTable
FOR EACH STATEMENT
WHEN (10000 > (SELECT AVG(prix) FROM Logiciel))
BEGIN
DELETE FROM Logiciel
WHERE (nom, classe, concep, provenance, revendeur, prix)
IN NouvelleTable;
INSERT INTO Logiciel
(SELECT * FROM AncienneTable);
END;
En ce qui concerne des concepts avancés comme les triggers, il vaut toujours mieux se
référer au manuel d’utilisation du SGBD utilisé. Pour PostgreSQL, on pourra se référer à
la section A.1.
128 CHAPITRE 9. CONTRAINTES ET TRIGGERS
Chapitre 10
10.1 Introduction
Nous n’avons manipulé SQL jusqu’à présent qu’en utilisant des expressions déclaratives
et le bloc SELECT ... FROM ... WHERE. Or on a souvent besoin en programmation de
définir des procédures ou des fonctions complexes faisant appel par exemple à du contrôle
de flot : conditionnelles, boucles etc.
Un standard récent de SQL, PSM (Persistent Storage Modules), offre la possibilité de
stocker des procédures et des fonctions dans la base de données côté SGBD. PSM permet
d’écrire des fonctions complexes et possède les habituelles structures de contrôle (boucle,
branchement conditionnel etc.). Cela permet d’avoir une fonction ou une procédure qui sera
partagée par tous les utilisateurs du SGBD sans duplication de code inutile, de grouper des
traitements complexes sans avoir à faire des « allers-retours » entre le client et le serveur
et d’avoir des fonctions que le compilateur de requêtes n’a pas besoin d’optimiser à chaque
fois. PostgreSQL permet ainsi d’écrire des fonctions en utilisant PL/pgSQL, un langage
impératif enrichissant la syntaxe de SQL (cf. chapitre A pour un exemple de création de
fonction avec PL/pgSQL).
La plupart des utilisations de SQL se font à l’aide d’un langage de programmation
« classique » qui permettent eux de développer d’autres aspects des applications : logique
métier, interfaces graphiques etc. Des interfaces à SQL ont été définies pour COBOL,
C, C++, Ada etc. Ce problème d’intégration de SQL n’est pas trivial : les structures de
données manipulées par SQL (basiquement des bases de données relationnelles) ne sont pas
définies directement dans les langages de programmation, qui eux utilisent des structures
propres (par exemple des pointeurs en C). Deux problèmes se posent alors :
– le problème de l’impedance mismatch (par analogie avec l’électronique) : il se peut
que les types de données, même les plus élémentaires, ne soient pas codés de la même
façon entre le langage hôte et SQL. Il faut alors gérer cette différence, par exemple
lorsque l’on récupère le résultat d’une requête.
– le langage hôte doit avoir une représentation (typée si possible) de ce qu’est une base
129
130 CHAPITRE 10. SQL ET AUTRES LANGAGES
Contrainte
T < SQLTypes
Attribut
L’intégration de code SQL dans un langage hôte peut se faire de deux façons différentes
suivant deux axes :
– statiquement ou dynamiquement : en SQL statique, les instructions SQL sont trans-
formées à la compilation (par un compilateur ou un préprocesseur particulier) et
exécutées lors de l’exécution du programmation. En SQL dynamique, les instructions
sont construites et exécutées lors de l’exécution du programme hôte.
– intégré ou appelé : en SQL intégré, les instructions SQL apparaissent dans le code
source hôte comme des instructions « normales ». En SQL appelé, les instructions
SQL sont encapsulées dans des appels à des fonctions/méthodes.
Évidemment, le SQL statique est souvent intégré et le SQL appelé est toujours dyna-
mique. On remarquera enfin que du code SQL intégré statique est souvent transformé en
code SQL appelé lors de la phase de compilation.
Nous avons choisi ici de présenter :
– l’interface entre SQL et C au travers d’appels statiques intégrés. Nous présenterons
l’utilisation de l’utilitaire ecpg fourni par le SGBD PostgreSQL.
– l’interface entre SQL et Java au travers de JDBC (Java DataBase Connectivity).
JDBC est une API (Application Programming Interface) Java d’accès aux SGBD.
Elle est composée de plusieurs classes et interfaces dans le package java.sql. Ce
type d’utilisation fournit un accès homogène aux SGBD et une abstraction des SGBD
cibles. Il permet aussi d’écrire des requêtes SQL dynamiques et appelées depuis le
langage Java.
1. On remarquera en particulier que la classe Attribut est paramétrée, car chaque attribut a un type
particulier. Le type est également contraint pour n’autoriser que des types « compatibles » avec SQL
10.2. SQL STATIQUE INTÉGRÉ AVEC C 131
On obtient alors un fichier connexionC.c qui est un code source purement écrit en C
(cf. listing 10.2). On remarquera que ce code C fait appel à des fonctions d’une bibliothèque
fournie par PostgreSQL, libpg, qui permet d’utiliser SQL de façon dynamique en C. Le
compilateur ecpg transforme donc du code SQL statique en code SQL dynamique. Nous
reviendrons sur ce point dans la conclusion.
22
23 printf("Disconnected...\n");
24
25 return 0;
26 }
On compile ensuite classiquement le code C en n’oubliant pas de préciser où trouver les
fichiers header des bibliothèques nécessaires :
% gcc -c connexionC.c -I$POSTGRESQL_HOME/include
Puis on lie le code objet ainsi obtenu pour obtenir un exécutable (il faut là encore
préciser les bibliothèques utilisées pour le lien ainsi que le chemin où les trouver) :
% gcc -o connexionC connexionC.o -L$POSTGRESQL_HOME/lib -lecpg
Par exemple, une insertion dans la base de données en utilisant une variable peut se
faire avec le bloc de code suivant :
EXEC SQL BEGIN DECLARE SECTION;
char param[20];
EXEC SQL END DECLARE SECTION;
Par exemple, la requête présentée sur le listing 10.3 permet de trouver le prix de
l’application Oracle (on est alors sûr que l’on va obtenir un seul résultat).
Listing 10.3 : Une requête simple sur une base de données
1 #include <stdio.h>
2
3 int main() {
4 printf("Trying to connect to database\n");
5 EXEC SQL CONNECT TO ’[email protected]’ USER garion;
6 printf("Connected...\n");
7
8 EXEC SQL BEGIN DECLARE SECTION;
9 int prix;
10 EXEC SQL END DECLARE SECTION;
11
12 EXEC SQL SELECT prix INTO :prix FROM logiciel WHERE nom = ’Oracle’;
13
14 printf("Result: %d\n", prix);
15
16 printf("Trying to disconnect\n");
17 EXEC SQL DISCONNECT ALL;
18 printf("Disconnected...\n");
19
20 return 0;
21 }
Si la requête renvoie plus d’un attribut en réponse, il suffit de stocker les différentes
valeurs des attributs dans des variables différentes. Comment se passe maintenant le
traitement d’une requête qui renvoie plus d’une ligne comme réponse ? Il faut alors utiliser
un curseur qui permet de parcourir les lignes de la réponse.
Syntaxe (déclaration d’un curseur sur une requête) :
EXEC SQL DECLARE nomCurseur CURSOR FOR SELECT... ;
2
10.2. SQL STATIQUE INTÉGRÉ AVEC C 135
Par exemple, le listing 10.4 propose une requête embarquée permettant de trouver les
logiciels et le prix associé dans la base de données exemple que nous utilisons. La variable
sqlca.sqlcode permet de savoir quand il n’y a plus de ligne à parcourir dans la réponse 4 .
Listing 10.4 : Une requête plus complexe sur une base de données
1 #include <stdio.h>
2
3 int main() {
4 printf("Trying to connect to database\n");
5 EXEC SQL CONNECT TO ’[email protected]’ USER garion;
6 printf("Connected...\n");
7
8 EXEC SQL BEGIN DECLARE SECTION;
9 char nom[20];
10 int prix;
11 EXEC SQL END DECLARE SECTION;
12
13 EXEC SQL DECLARE curseur CURSOR FOR SELECT nom, prix FROM logiciel;
14
15 EXEC SQL OPEN curseur;
16
17 while (sqlca.sqlcode == 0) {
18 EXEC SQL FETCH NEXT FROM curseur INTO :nom, :prix;
19
20 if (sqlca.sqlcode == 0) {
21 printf("Software: %s, price: %d\n", nom, prix);
22 }
23 }
24
25 EXEC SQL CLOSE curseur;
26
27 printf("Trying to disconnect\n");
28 EXEC SQL DISCONNECT ALL;
29 printf("Disconnected...\n");
30
31 return 0;
32 }
Dans le cas d’un accès à une base de données, le programme ne communique pas
directement avec le moteur du SGBD. Il communique avec l’interface client du SGBD
qui gère le protocole de communication habituellement spécifique au SGBD. Jusqu’à
récemment, les applications client/serveur étaient réalisées par le constructeur du système
de gestion de bases de données. On ne pouvait donc pas changer le client ou le serveur
indépendamment l’un de l’autre. Des protocoles comme ODBC ou JDBC sont nés pour
pallier ce problème. ODBC a été développé pour fournir une API standard à SQL sur
les plates formes Microsoft. ODBC fournit une couche d’abstraction indépendante de la
base de données, mais dépendante de la plateforme (Microsoft uniquement). JDBC fournit
également une couche d’abstraction indépendante de la base de données, mais également
de la plateforme.
JDBC permet de développer des programmes clients (applications autonomes Java
ou applets) qui accèdent au SGBD. En ce qui concerne l’accès aux bases de données, un
programme Java qui utilise les services de JDBC est toujours structuré en deux couches
(cf. figure 10.3) :
– la première couche est orientée vers le programme Java. Elle est composée du
gestionnaire de drivers (driver manager ) JDBC. C’est un objet Java auquel s’adressent
les autres objets de l’application cliente Java pour obtenir des connexions vers les
bases de données.
10.3. JDBC ET LES ARCHITECTURES CLIENT/SERVEUR 137
– la seconde couche est orientée vers le SGBD. Elle nécessite des drivers JDBC qui
sont spécifiques aux bases de données auxquelles l’application cliente doit accéder.
Application Java
java.sql.DriverManager
Il existe des drivers JDBC pour Oracle, PostgreSQL, MySQL, Sybase, Informix, etc.
Les drivers JDBC peuvent être classés en quatre catégories (cf. figure 10.4).
– drivers de type 1 (JDBC-ODBC bridge). Ils définissent une interface entre l’API
ODBC et l’API JDBC. Le driver transforme les appels à l’API JDBC en appels
ODBC. Le driver ODBC se charge ensuite des requêtes natives en SQL. Cette solution
est très coûteuse en temps, car on a trois couches d’abstraction (JDBC, ODBC et la
couche native de base de données). De plus, les fonctionnalités de l’API JDBC sont
limitées par celles d’ODBC.
– drivers de type 2 (moitié Java, moitié natif). Ce type de driver est une interface entre
le driver manager JDBC et l’interface cliente du SGBD. Il est dédié à un SGBD
particulier et est donc moins ouvert que le précédent. Par contre, il y a une couche
de moins que dans le cas précédent, donc ce type de driver est beaucoup plus rapide.
– drivers de type 3. Il s’agit d’une interface entre le driver de JDBC et un service
d’accès aux données que nous appelons middleware. Il encapsule un protocole de
communication entre le driver du JDBC et le service middleware. Celui-ci peut
être utilisé par une applet Java. Ce type de drivers permet d’avoir un fort taux
d’abstraction, le serveur intermédiaire gérant par exemple les connexions aux différents
serveurs.
– drivers de type 4. Il s’agit d’une interface entre le driver manager de JDBC et
l’interface du serveur de SGBD. Il encapsule complètement l’interface cliente du
SGBD. Notons que les drivers 3 et 4 autorisent l’utilisation d’applets contrairement
aux drivers 1 et 2 car ces derniers nécessitent le chargement de codes natifs non
autorisés par le protocole de sécurité défini pour les applets Java.
Actuellement, il existe des drivers de type 4 pour pratiquement tous les SGBD.
CHAPITRE 10. SQL ET AUTRES LANGAGES
SERVEUR
SBGD SBGD SBGD SBGD
Interface serveur Interface serveur Interface serveur Interface serveur
Driver natif
Serveur intermédiaire
Couche ODBC
API ODBC API vendeur
CLIENT
Bridge JDBC-ODBC Driver JDBC Driver JDBC Driver JDBC
API JDBC API JDBC API JDBC API JDBC
Application Java Application Java Application Java Application Java
Figure 10.4 : Les différents types de drivers JDBC
138
10.4. FONCTIONNEMENT 139
10.4 Fonctionnement
JDBC interagit avec le SGBD grâce à un driver (comme ceux qui sont décrits ci-dessus).
L’accès aux données de fait en plusieurs étapes :
1. enregistrement du pilote JDBC ;
2. connexion avec la base de données ;
3. création d’une zone de description de requête ;
4. exécution de la requête ;
5. traitement des données retournées.
Nous allons les détailler dans ce qui suit.
Attention, cet appel peut lever une ClassNotFoundException si le driver n’est pas
accessible. Le chargement d’un driver JDBC enregistre automatiquement le driver auprès
du gestionnaire de drivers.
Exemple 10.4.1 : Supposons que l’on veuille enregistrer un driver pour le SGBD Post-
greSQL. On écrira alors :
try {
Class.forName("org.postgresql.Driver");
}
catch (ClassNotFoundException e) {
...
}
140 CHAPITRE 10. SQL ET AUTRES LANGAGES
Exemple 10.4.2 : Nous considérerons ici que nous voulons nous connecter via le SGBD
PostgreSQL à une base de donnée bdpolyIN306 située sur la machine serv-sun1. La
syntaxe de l’URL est alors jdbc:postgresql://serv-sun1/bdpolyIN306. 2
Exemple 10.4.3 : Supposons que l’on veuille maintenant se connecter à la base bdpolyIN306
située sur serv-sun1 en tant qu’utilisateur garion. On écrira alors (en supposant que l’on
a importé les paquetages nécessaires) :
try {
Connection connection;
connection = DriverManager.getConnection("jdbc:postgresql://serv-sun1/bdpolyIN306",
"garion");
}
catch (SQLException e) {
...
}
On peut également utiliser des batch updates qui permettent de regrouper plusieurs mises
à jour dans un seul objet de type Statement. Ceci permet d’améliorer les performances
des requêtes.
Remarque : on peut également utiliser des objets de « type » ResultSet plus complexes
comme qui permettent une navigation plus aisée (retours possibles dans la liste des lignes
par exemple) ou de modifier via le ResultSet la ou les tables concernées. 2
Il se peut que l’on ne connaisse pas la structure du résultat renvoyé (cas d’une requête
paramétrée par l’utilisateur par exemple). Pour obtenir ces informations, on utilise un objet
de type java.sql.ResultSetMetaData obtenu via la méthode de java.sql.ResultSet
suivante :
43 res.close();
44 statement.close();
45 connection.close();
46 } catch (ClassNotFoundException e) {
47 e.printStackTrace();
48 } // end of catch
49 catch (SQLException e) {
50 e.printStackTrace();
51 } // end of try-catch
52
53 } // end of main()
54 }
10.6 Conclusion
Nous venons de présenter ici deux façons d’intégrer SQL dans un langage de program-
mation hôte : une intégration statique dans le langage C et une intégration dynamique
dans le langage Java.
L’intégration statique de SQL dans C possède plusieurs avantages : la gestion des
variables partagées entre les deux langages est facilitée, le code SQL est vérifié par le
précompilateur epgc que nous avons utilisé et surtout le code SQL reste aussi proche
que celui que l’on peut écrire dans l’interpréteur. On remarquera qu’il existe également
une extension standardisée (cf. [26]) du langage Java pour y intégrer statiquement SQL :
SQLJ. SQLJ est fourni par un certain nombre de vendeurs, Oracle ou IBM par exemple[2].
Malheureusement, le SGBD PostgreSQL que nous utilisons ne fournit pas cette extension.
On remarquera également que la présentation qui a été faite reste sommaire : nous ne
parlons pas de la gestion des valeurs NULL, des résultats de requêtes vides et de la gestion
des erreurs par exemple.
L’intégration dynamique de SQL, comme nous l’avons vue à travers JDBC, permet quant
à elle d’éviter de passer par une phase de précompilation. On remarquera également que
les précompilateurs SQL statique transforme en fait le code statique en appels dynamiques
à des fonctions ou des méthodes du langage hôte permettant d’exécuter du code SQL sur
un serveur. Par contre, l’écriture de programme utilisant des appels SQL dynamiques est
plus lourde, la syntaxe SQL étant la plupart du temps « noyée » dans des chaı̂nes de
caractères du langage hôte. On remarquera également que l’on peut maintenant utiliser des
annuaires comme JNDI (Java Naming and Directory Interface) pour localiser des bases de
données via l’interface javax.sql.DataSource. On peut alors profiter de plus de services
que dans le cas d’une connexion obtenue « classiquement » par le DriverManager : pool
de connexions, transactions etc.
Enfin, on remarquera qu’il vaut mieux utiliser des requêtes préparées (prepared state-
ment) dans les deux cas si l’on veut les répéter un grand nombre de fois : la requête sera
optimisée par le SGBD et on pourra faire varier les paramètres de la requête sans que le
SGBD n’ait forcément à la recompiler. Par exemple, 10000 exécutions de la même requête
10.6. CONCLUSION 145
simple fait gagner 25% de temps en utilisant une requête préparée (cf. listings 10.6 et ? ?).
3 /**
4 * Une requete preparee repetee 10000 fois
5 *
6 * Created: Tue Jul 14 14:35:11 2009
7 *
8 * @author <a href="mailto:[email protected]">Christophe Garion</a>
9 * @version 1.0
10 */
11 public class RequetePreparee {
12
13 public static void main(String[] args) {
14 try {
15 Class.forName("org.postgresql.Driver");
16
17 String url = "jdbc:postgresql://serv-sun1.isae.fr/bdpolyIN306";
18 String user = "garion";
19 Connection connection = DriverManager.getConnection(url,
20 user,
21 "");
22
23 // on demande une requete
24 PreparedStatement statement;
25 statement = connection.prepareStatement("SELECT * " +
26 "FROM Ordinateur");
27
28 for (int i = 0; i < 10000; i++) {
29 statement.execute();
30 }
31 } catch (ClassNotFoundException e) {
32 e.printStackTrace();
33 } // end of catch
34 catch (SQLException e) {
35 e.printStackTrace();
36 } // end of try-catch
37 }
38 }
Chapitre 11
11.1 Objectifs
On souhaite définir une démarche rigoureuse pour la conception d’un schéma relationnel
optimal. La démarche que nous définissons dans ce chapitre est dite « démarche directe »,
elle ne fait pas appel au modèle entité-association 1 . On dispose donc d’un modèle relationnel
représentant un ensemble d’informations structuré.
Pour préciser ce que nous entendons par schéma relationnel optimal, reprenons la
relation Type Ordinateur que nous avions définie dans le chapitre 6 :
Type Ordinateur {type, cons, pays, rev , nb postes, mem max , uc, capa lec, capa disk }
Supposons que nous ayons les tuples suivants pour la relation :
type cons pays rev nb postes mem max uc capa lec capa disk
Micral 75 Bull France Camif 1 512 IN80486 1044 40
Micral 60 Bull France Camif 1 256 IN80486 1044 20
Mac II Apple USA Apple 1 256 Mo68020 1044 60
1. Les techniques de normalisation peuvent évidemment s’appliquer aux relations obtenues en traduisant
un modèle entité-association dans un modèle relationnel.
147
148 CHAPITRE 11. DÉPENDANCES FONCTIONNELLES
Nous allons donc chercher à utiliser une méthode pour définir « correctement » un
schéma relationnel. Les objectifs d’une telle approche sont de :
– minimiser les redondances d’informations,
– éviter les « anomalies de mises à jour »,
– privilégier les dépendances entre attributs.
Nous illustrerons ces objectifs à chaque fois qu’il sera possible dans ce qui suit.
A1 . . . An −→ B1
...
A1 . . . An −→ Bm
A1 . . . An −→ B1 . . . Bm ` A1 . . . An C1 −→ B1 . . . Bm C1
{A1 . . . An −→ B1 . . . Bm , B1 . . . Bm −→ C1 . . . Ck } ` A1 . . . An −→ C1 . . . Ck 2
– si f peut être dérivée de S en utilisant les règles d’Armstrong, alors f est une
dépendance fonctionnelle sur R conséquence de S (validité des règles).
– si f est une dépendance fonctionnelle sur R conséquence de S, alors f peut dérivée
de S en utilisant les règles d’Armstrong (complétude des règles).
On peut donc écrire :
S |= f ⇔ S ` f 2
Preuve :
On considère dans toute la suite de la preuve un ensemble de dépendances fonctionnelles S
sur une relation R(∆).
– la démonstration de la validité se fait par induction sur le nombre de règles utilisées :
– au rang 0, cela signifie que soit f ∈ S, soit on a utilisé l’axiome de réflexivité pour
trouver f . D’après la définition d’une dépendance fonctionnelle, si f est issue de
l’axiome de réflexivité, alors f est bien une dépendance fonctionnelle issue de S.
– supposons la relation d’induction vraie au rang n ≥ 0 (donc si on a utilisé n règles
pour dériver f , alors f est bien déduite de S). Supposons que l’on ait utilisé n + 1
règles pour dériver f . Deux cas se présentent :
– soit la dernière règle utilisée est la règle d’augmentation. f est donc de la forme
A1 . . . An C1 −→ B1 . . . Bm C1 et est issue d’une règle de la forme A1 . . . An −→
B1 . . . Bm . A1 . . . An −→ B1 . . . Bm a été produite en utilisant n règles d’Arm-
strong donc d’après l’hypothèse d’induction, A1 . . . An −→ B1 . . . Bm est bien
une dépendance fonctionnelle sur R conséquence de S.
D’après la définition d’une dépendance fonctionnelle, si A1 . . . An −→ B1 . . . Bm
est une dépendance fonctionnelle sur R conséquence de S, alors A1 . . . An C1 −→
B1 . . . Bm C1 est en également une (C1 est bien un attribut de R et on l’« ajoute »
de chaque côté). Donc A1 . . . An C1 −→ B1 . . . Bm C1 est bien une dépendance
sur R conséquence de S.
– soit la dernière règle utilisée est la règle de transitivité, donc f est de la
forme A1 . . . An −→ C1 . . . Ck et est conséquence de A1 . . . An −→ B1 . . . Bm et
B1 . . . Bm −→ C1 . . . Ck .
A1 . . . An −→ B1 . . . Bm et B1 . . . Bm −→ C1 . . . Ck ont été produites en utilisant
au plus n règles d’Armstrong, donc d’après l’hypothèse d’induction ce sont bien
des dépendances fonctionnelles sur R conséquences de S.
Soient t1 et t2 deux tuples de R s’accordant sur les valeurs des attributs A1 . . . An .
Comme A1 . . . An −→ B1 . . . Bm est une dépendance fonctionnelle, les valeurs
de B1 , . . . , Bm pour t1 et t2 vont être identiques. Or B1 . . . Bm −→ C1 . . . Ck
est une dépendance fonctionnelle sur R donc les valeurs de C1 , . . . , Ck sont les
mêmes pour t1 et t2 . Donc A1 . . . An −→ C1 . . . Ck est bien une dépendance
fonctionnelle sur R conséquence de S.
– la démonstration de la complétude se fait par l’absurde. Supposons que f = X −→ Y
soit une dépendance fonctionnelle sur R conséquence de S et que f ne puisse pas
être dérivée de S en utilisant les règles d’Armstrong.
152 CHAPITRE 11. DÉPENDANCES FONCTIONNELLES
Pour pouvoir vérifier si X −→ Y est une dépendance fonctionnelle déduite d’un ensemble
de DF S, il suffit donc de vérifier que Y ∈ {X}+ .
L’algorithme ? ? permet de calculer la fermeture d’un ensemble d’attributs.
Comme pour le système formel d’Armstrong, il existe un théorème de validité/complé-
tude pour cet algorithme.
2. J’ai utilisé des entiers comme valeurs pour faire plus simple mais on peut choisir des éléments plus
généraux
11.3. RÈGLES ET PROPRIÉTÉS DES DÉPENDANCES FONCTIONNELLES 153
Théorème 11.3.2.
L’algorithme de calcul de fermeture est valide et complet. Plus précisément, soient S un
ensemble de dépendances fonctionnelles sur une relation R et {A1 , . . . , An } des attributs de
R. Alors B ∈ {A1 , . . . , An }+ si et seulement si A1 . . . An −→ B peut être déduite de S. 2
Preuve :
La preuve de se théorème se fait classiquement en deux parties. On considère dans toute
la preuve que S est un ensemble de dépendances fonctionnelles sur une relation R et
{A1 , . . . , An } sont des attributs de R.
1. montrons que si B ∈ {A1 , . . . , An }+ , alors A1 . . . An −→ B peut être déduite de S.
Pour cela, la preuve s’effectue par récurrence sur le nombre d’« itérations » de
l’algorithme ? ? nécessaires à l’obtention de B.
– au rang 0, la preuve est évidente : on n’a pas appliqué le cœur de l’algorithme et
B est un des attributs de départ (on a une dépendance triviale).
– supposons la relation vraie au rang n ≥ 0 : tous les attributs ajoutés en utilisant
n fois le cœur de l’algorithme conduisent bien à une dépendance fonctionnelle.
Supposons que B ait été ajouté grâce à une règle C1 . . . Cm −→ B. On sait par
hypothèse de récurrence que R satisfait A1 . . . An −→ C1 . . . Cm . Par transitivité
on en déduit bien que A1 . . . An −→ B.
2. supposons que A1 . . . An −→ B soit une dépendance fonctionnelle déduite de S, mais
que B 6∈ {A1 , . . . , An }+ . Nous allons construire une instance de relation qui satisfait
toutes les dépendances de S sauf A1 . . . An −→ B et ainsi montrer que ce n’est pas
possible.
L’instance I de relation qui nous construisons contient exactement deux tuples qui
s’accordent sur les valeurs de {A1 , . . . , An }+ , mais qui ne s’accordent pas sur les
autres attributs de R. Dans ce cas, A1 . . . An −→ B n’est pas satisfaite par I (car
B 6∈ {A1 , . . . , An }+ ).
154 CHAPITRE 11. DÉPENDANCES FONCTIONNELLES
On remarquera que la complexité de ce cet algorithme est |S| × |R| (où |R| est le nombre
d’attributs de R). Cet algorithme est donc assez coûteux. . .
Attention, une dépendance fonctionnelle élémentaire n’a qu’un seul attribut en « partie
droite » de la DF.
On remarquera que si les attributs d’une relation dépendent tous fonctionnellement des
clés et des superclés de la relation, la dépendance existant avec les clés est une dépendance
fonctionnelle élémentaire contrairement aux superclés. On pourrait donc donner une nouvelle
définition d’une clé pour une relation R : il s’agit d’un sous-ensemble d’attributs de R tel que
tous les attributs de R dépendent fonctionnellement élémentairement de ce sous-ensemble.
Nous allons maintenant considérer les dépendances qui peuvent se déduire par tran-
sitivité des autres dépendances. On dit alors qu’elles sont redondantes. Pour vérifier que
X −→ A est redondante à partir d’un ensemble de dépendances fonctionnelles S, il suffit
de calculer {X}+ à partir de S − {X −→ A} et de vérifier que A ∈ {X}+ . Si oui, alors
X −→ A est redondante.
Définition 11.3.6.
Soit S un ensemble de dépendances fonctionnelles sur une relation R. On appelle couverture
irredondante de S l’ensemble de dépendances fonctionnelles élémentaires S 0 tel que :
– toute dépendance fonctionnelle élémentaire de S est dans la fermeture de S 0 ;
– aucune dépendance de S 0 n’est redondante. 2
Nous allons maintenant chercher à obtenir une couverture minimale (on dit aussi
irredondante) d’un ensemble de DF S. Il suffit pour cela de suivre la définition et de
simplifier les DF pour n’obtenir dans un premier temps que des DF élémentaires, puis
d’éliminer les DF redondantes (cf. algorithme ? ?).
Exemple 11.3.1 : Prenons l’ensemble de DF suivant pour une relation R :
11.4. NORMALISATION DE RELATIONS 155
– A −→ BC
– AB −→ D
– D −→ C
A −→ BC est en fait un raccourci d’écriture pour A −→ B et A −→ C. En utilisant
A −→ B et la règle d’augmentation avec A, on obtient A −→ AB. Avec la règle de
transitivité, on obtient A −→ D et donc AB −→ D n’est pas une dépendance fonctionnelle
élémentaire. On obtient pour dépendances fonctionnelles élémentaires :
– A −→ B
– A −→ C
– A −→ D
– D −→ C
Parmi ces DF, A −→ C est redondante : on peut l’obtenir en appliquant la règle de
transitivité sur A −→ D et D −→ C. La couverture irredondante de notre ensemble de
départ est donc :
– A −→ B
– A −→ D
– D −→ C 2
Décomposition SPI
Pour vérifier que la décomposition est SPI, une technique simple, dite méthode de
poursuite, est disponible. Il suffit de vérifier que « R1 1 . . . 1 Rn ⊆ R » 3 de la façon
suivante (on suppose que les attributs de R sont A1 , . . . , Am ) :
– considérer un tuple (a1 , . . . , am ) de R1 1 . . . 1 Rn
– construire un tableau avec pour colonnes les attributs de R et lignes chaque Ri
– remplir le tableau : pour chaque ligne, on utilise les aj lorsque la relation Ri contient
l’attribut en question et on remplace par une variable sinon
– ensuite, pour chaque DF X −→ Y , on considère deux lignes du tableau. Si ces deux
lignes s’entendent sur les valeurs de X, alors pour chaque attribut Aj de Y , si une des
lignes a aj pour valeur de Aj , on remplace l’éventuelle variable utilisée dans l’autre
ligne par Aj
– si une des lignes est (a1 , . . . , am ), alors la décomposition est SPI
Cette méthode est détaillée sur l’algorithme ? ?
La décomposition n’est pas SPI. Il suffit de considérer comme exemple les valeurs
suivantes pour les tables :
A B C D A B C D
1 2 3 4 1 2 3 4
5 6 7 8 5 6 7 8
2
Preuve :
La démonstration du théorème est simple et laissée comme exercice.
Preuve :
La démonstration du théorème est simple et laissée comme exercice.
Décomposition SPD
Pour vérifier que la décomposition vérifie les dépendances fonctionnelles initiales, il
suffirait de construire la jointure des relations de la décomposition et de vérifier que
les dépendances sont vérifiées sur la jointure. La construction d’une jointure étant très
coûteuse, ne pourrait-on pas vérifier la cohérence locale (i.e. au niveau de chaque relation)
des dépendances pour conclure à la vérification des dépendances fonctionnelles ?
Définition 11.4.1 (décomposition SPD).
Une décomposition R est sans perte de dépendances par rapport à un ensemble de dépen-
dances fonctionnelles S s’il existe F ⊆ S + tel que :
4. On note dans toute la suite du chapitre R(X, Y ) = πX,Y R.
11.4. NORMALISATION DE RELATIONS 159
Théorème 11.4.4.
Soit {R1 , . . . , Rn } une decomposition de R par rapport à S ensemble de dépendances
fonctionnelles sur R, alors {R1 , . . . , Rn } est sans perte de dépendances par rapport à S ssi
n
( S Ri ) + = F + .
S
2
i=1
Preuve :
La démonstration est aisée en remplaçant dans la définition d’une décomposition SPD F
Sn
par SRi .
i=1
Si l’on veut appliquer ce théorème, on doit calculer S + , ce qui peut être très coûteux
(exponentiel par rapport au nombre d’attribut dans S). On peut par contre utiliser
l’algorithme ? ? pour vérifier si une décomposition est SPD.
Théorème 11.4.5.
L’algorithme ? ? renvoie vrai si la décomposition donnée en entrée est SPD, faux sinon.2
Nous allons maintenant voir que la décomposition sous forme normale permet de
garantir les propriétés demandées. La plupart de ces formes normales ont été formalisées
par Codd.
160 CHAPITRE 11. DÉPENDANCES FONCTIONNELLES
Définition 11.4.2.
Soit R une relation d’attributs A1 , . . . , An . R est sous première forme normale (ou 1NF)
ssi pour tout i ∈ {1, . . . , n} les Ai a un domaine atomique. 2
La seconde forme normale permet de stipuler qu’une relation est telle que les attributs
de la relation n’appartenant pas à une clé dépendent fonctionnellement des clés de la
relation.
Définition 11.4.3.
Soit R une relation. R est sous seconde forme normale (ou 2NF) ssi R est sous première
forme normale et si tout attribut B n’appartenant pas à une clé de la relation est en
dépendance élémentaire avec toutes les clés de la relation. 2
La seconde forme normale impose donc la non existence de dépendances partielles vers
les clés de la relation. On peut aussi dire qu’un attribut n’appartenant pas à une clé de
la relation ne peut pas être en dépendance élémentaire avec une partie d’une clé de la
relation.
L’exemple typique de relation qui n’est pas sous deuxième forme normale est une rela-
tion gérant les DVD empruntés par un client. La relation Location comporte les attributs
NumClient, NomClient, AdresseClient, NumDVD, DateEmprunt. On voit que l’on a les dé-
pendances suivantes : NumClient −→ NomClientAdresseClient, NumClientNumDVD −→
DateEmprunt. (NumClient, NumDVD) est la clé de la relation (on suppose donc qu’un client
n’emprunte qu’une seule fois un DVD. . . ). Or NumClient −→ NomClientAdresseClient,
donc la relation n’est pas en 2NF, car on n’a pas NumClient NumDVD −→ NomClientAdresseClient.
On peut remarquer en effet que l’adresse et le nom du client seront dupliqués à chaque
location.
Par exemple, la relation Type Ordinateur n’est pas 3NF : sa seule clé est type, mais la
dépendance cons −→ pays existe.
Théorème 11.4.6.
Toute relation admet une décomposition en 3NF avec jointure conservatrice et préservation
des dépendances fonctionnelles. 2
11.4. NORMALISATION DE RELATIONS 161
Exemple 11.4.2 : Reprenons la relation Ordinateur dont la clé est l’attribut type. On voit
clairement que l’on a une dépendance fonctionnelle cons −→ pays en plus des dépendances
« classiques » concernant la clé type.
Dans ce cas, on peut séparer la relation en deux sous-relations via l’algorithme précé-
dent :
– une relation Constructeur {cons, pays}
– une relation Type Ordi {type, cons, rev , nb postes, mem max , uc, capa lec, capa disk }2
– rev −→ type qui signifie qu’un revendeur ne peut vendre qu’un seul type d’ordinateur
(on remarquera que l’instance proposée dans la section 11.1 viole cette dépendance).
Dans ce cas, la relation est bien 3NF : il n’y a pas d’attribut non clé dépendant d’attribut
non clé. Par contre, la deuxième dépendance entre un attribut non clé et un attribut clé
peut induire les anomalies suivantes :
– il est impossible d’enregistrer un revendeur sans modèle associé ;
– la suppression d’un tuple peut entraı̂ner la suppression d’un revendeur.
Définition 11.4.5.
Soit R une relation. R est en troisième forme normale de Boyce-Codd (ou BCNF) si
et seulement si R est 3NF et que les seules dépendances élémentaires sont de la forme
A1 . . . An −→ B où {A1 , . . . , An } est une superclé. 2
Théorème 11.4.7.
Toute relation R admet une décomposition en BCNF. La préservation des dépendances
fonctionnelles initiales n’est pas garantie. 2
Toute relation en BCNF est également en 3NF, mais on ne garantit pas que les
dépendances fonctionnelles initiales puissent être vérifiées par la jointure des sous-relations
construites par décomposition.
Nous présentons maintenant un algorithme de décomposition en BCNF.
– une relation Type Ordi {rev , cons, pays, nb postes, mem max , uc, capa lec, capa disk }2
La dépendance ici n’est plus fonctionnelle : c’est un ensemble de sports par exemple
qui dépend du nom. On parle alors de dépendance multivaluée.
Définition 11.5.1.
Soit R(∆) une relation d’ensemble d’attributs ∆ et X, Y, Z une partition de ∆. On dit
qu’il existe une dépendance multivaluée entre X et Y ssi toute valeur de X détermine un
ensemble unique de valeurs de Y indépendamment des valeurs de Z.
On le note X → → Y. 2
Dans l’exemple précédent, on a par exemple nom → → sport : un individu peut pratiquer
plusieurs sports et ces sports ne dépendent pas des moyens de transport qu’il utilise.
On peut également donner une autre définition des dépendances multivaluées.
164 CHAPITRE 11. DÉPENDANCES FONCTIONNELLES
Définition 11.5.2.
Soit R(∆) une relation d’ensemble d’attributs ∆ et X, Y, Z une partition de ∆. On dit qu’il
existe une dépendance multivaluée entre X et Y ssi si (x, y, z) et (x, y 0 , z 0 ) sont des tuples
de R, alors (x, y 0 , z) et (x, y, z 0 ) le sont aussi. 2
Proposition 11.5.2.
Les dépendances multivaluées triviales sont des dépendances multivaluées de la forme
suivante :
X →→ Y où Y ⊆ X. 2
On voit tout de suite qu’une relation en 4NF est également en BCNF (car toute
dépendance fonctionnelle est multivaluée).
L’algorithme présenté dans la section 11.4.4 permettant de mettre sous BCNF une
relation s’applique encore ici : on peut l’utiliser pour mettre une relation en 4NF.
Exemple 11.5.1 : Reprenons la relation sport-déplace. On a ici une première dépendance
nom →→ sport qui l’on va utiliser dans l’algorithme. On obtient alors deux relations :
– sport : {nom, sport}
– deplace : {nom, moyen}
Ces deux relations sont correctement normalisées. 2
11.6. CONCLUSION 165
11.6 Conclusion
Nous avons introduit dans ce chapitre les notions de dépendances fonctionnelles et
multivaluées et montrer que ces dépendances pouvaient entraı̂ner des anomalies de mises
à jour des relations concernées. Nous avons ensuite introduit différentes formes normales
permettant de résoudre ces problèmes. Ce chapitre n’est qu’une introduction à la normalisa-
tion de relations. Il existe en effet d’autres formes normales (5NF, ONF, DKNF), d’autres
dépendances (hiérarchiques, produit). On pourra se référer à [34, 27, 25].
On pourra toutefois remarquer la normalisation des relations n’est pas appliquée
systématiquement. En effet, la normalisation repose sur la décomposition de relations en
sous-relations. Les requêtes sur les relations vont donc induire des jointures qui sont toujours
pénalisantes en termes de performances. On trouvera donc souvent des relations qui sont
intentionnellement non normalisées pour garantir un certain niveau de performance. Dans
ce cas, il faut bien être conscient des conséquences que cela implique sur la redondance des
informations et des éventuelles anomalies de mises à jour.
166 CHAPITRE 11. DÉPENDANCES FONCTIONNELLES
Chapitre 12
Nous allons nous intéresser dans ce chapitre à deux aspects importants des SGBDs :
– la gestion des pannes du système. Ces pannes peuvent provenir par exemple d’un
problème réseau, d’un disque qui ne fonctionne plus, d’une panne de courant etc. Le
but d’un SGBD est de garantir la résilience du système, i.e. de garantir que l’on
pourra retrouver une base de données dans un état cohérent après redémarrage.
– la gestion de la concurrence dans un environnement multi-utilisateurs. Les données
de la base ne doivent pas être corrompues (au sens des contraintes d’intégrité de la
base) parce que des opérations légales sont effectuées en même temps.
Tous ces concepts sont indépendants du modèle de données utilisé dans le SGBD. Nous
allons dans un premier temps présenter la notion de transaction et les propriétés attendues
d’une transaction. Nous nous intéresserons ensuite au problème de la reprise d’un SGBD
après une panne. Nous aborderons ensuite le problème de la gestion de la concurrence dans
un environnement multi-utilisateurs. Nous présenterons des notions importantes : lock à
deux phases, sérialisabilité etc. Enfin, nous présenterons les solutions apportées par SQL
pour la gestion des transactions.
Dans cet exemple, ce qui est considéré comme une opération atomique (un transfert de
fonds de compte à compte) est en fait constitué de deux opérations de mise à jour de la
167
168 CHAPITRE 12. GESTION DES TRANSACTIONS
base de données. Dans ce cas, que se passe-t-il si pour une raison quelconque la seconde
transaction n’est pas effectuée ? On se retrouve alors avec une base de données dans un
état incohérent. Il faut considérer ces deux mises à jour élémentaires comme étant un tout.
On dira alors que ces deux opérations forment une transaction.
Définition 12.1.1.
Une transaction est une unité logique de travail. C’est une séquence d’opérations élémen-
taires qui est vue comme atomique d’un point de vue externe au SGBD. 2
Remarquons tout de suite que les opérations élémentaires de la base de données peuvent
amener la base à un état incohérent (après la première mise à jour, les comptes ne sont
pas équilibrés). C’est la transaction dans son ensemble qui, d’un point de vue externe, va
amener la base dans un état cohérent.
Le but de la gestion des transactions est de garantir qu’une transaction s’exécute dans
son ensemble ou ne s’exécute pas du tout (même si certaines de ses opérations élémentaires
se sont déjà exécutées). Elle sera donc vue comme indivisible d’un point de vue externe.
Enfin, on peut voir les opérations élémentaires que nous avons manipulées jusqu’à
présent comme des transactions élémentaires (séquence d’une seule opération). La propriété
précédente s’applique bien à ce cas particulier : une opération élémentaire de mise à jour
est soit effectuée dans sa totalité par le SGBD, soit n’est pas effectuée (même dans le cas
de mises à jour complexes).
Comment alors garantir ces différentes propriétés ? Nous allons voir que la propriété de
durabilité va être garantie par la possibilité de reprise après panne, l’atomicité et l’isolation
sont garanties par le scheduler qui gère la concurrence entre transactions. Reste le problème
de la cohérence, qui est en fait vérifiée par l’établissement de contraintes sur la base de
12.2. REPRISE APRÈS PANNE 169
UNDO:
ROLLBACK;
FINISH:
END TRANSACTION
modifications ont été réellement inscrites sur le disque contenant la base de données pour
pouvoir reconstruire la base de données dans un état cohérent. Le problème de la durabilité
des informations contenues dans la base de données est donc intimement lié à celui de la
reprise après panne du système. Cette possibilité de reprise après panne va être assurée
par un journal qui va contenir l’ensemble des mises à jour effectuées sur le système.
Dans ce qui suit, pour étoffer les explications, on considérera que l’on a disposition
quatre opérations qui représente les actions possibles du transaction manager :
buffer(X) bloc(X)
INPUT
WRITE
On remarquera que nos prenons ici des hypothèses très simplificatrices. Par exemple,
lorsque l’on lit ou écrit des éléments du disque, on ne peut utiliser que des blocs du disque
(cf. chapitre 3).
12.2.1 Journal
Pour pouvoir conserver l’ensemble des modifications et des opérations importantes qui
ont été effectuées sur la base de données, on utilise un journal (log en anglais). Ce journal
peut contenir différentes entrées :
– <START T> : la transaction T a démarré ;
– <COMMIT T> : la transaction T a réussi. Tous les changements effectués par T doivent
être inscrits sur le disque 1 ;
– <ABORT T> : la transaction n’a pas réussi. Dans ce cas, aucun de ses changements ne
doit apparaı̂tre sur le disque.
– des entrées du type <T, X, v> : la transaction T a changé la valeur de l’élément X
et l’ancienne valeur ou la nouvelle valeur (suivant le type de journalisation que l’on
1. Ce point peut poser de gros problèmes. En effet, nous ne sommes pas sûr que les changements ont
été effectivement inscrits sur le disque (c’est le rôle du buffer manager de s’occuper de cela. Il faut donc
peut-être forcer l’écriture des mises à jour.)
172 CHAPITRE 12. GESTION DES TRANSACTIONS
Principe 12.2.1.
1. si une transaction T modifie un élément X, alors on doit écrire <T, X, v> dans le
journal sur le disque avant que la nouvelle valeur de X ne soit écrite sur le disque.
Cette entrée signifie : l’ancienne valeur de X était v et X a été modifié en mémoire
principale.
2. si une transaction T est réussie, alors on écrit <COMMIT T> après que tous les chan-
gements effectués par la transaction ont été écrits sur le disque. 2
Remarquons que les écritures du journal sur le disque doivent être forcées par le log
manager via l’instruction FLUSH LOG : on ne peut évidemment pas les laisser en mémoire
centrale. De plus, si l’on suit les règles énoncées précédemment, on est obligé d’utiliser
FLUSH LOG avant d’écrire effectivement les nouvelles valeurs de C1 et de C2 sur le disque.
12.2. REPRISE APRÈS PANNE 173
Exemple 12.2.2 : Sur l’exemple précédent, supposons que nous ayons le journal suivant :
<START T>
<T,C1,1000>
<T,C2,500>
Dans ce cas, en remontant le journal, on sait que la transaction T ne s’est pas terminée
(par exemple parce que OUTPUT(C2) ne s’est pas effectuée). On peut donc annuler la
transaction en utilisant les entrées du journal et en prévenant bien sûr l’utilisateur que T a
été annulée. 2
1. écrire une entrée <START CHKPT(T1, ..., Tn)> dans le journal. T1, . . . , Tn sont
toutes les transactions actives au moment de l’écriture.
2. attendre que T1, . . . , Tn se finissent. Continuer à accepter des transactions.
3. quand T1, . . . , Tn ont fini, écrire <END CHKPT> dans le journal.
Dans ce cas, lors de la reprise du système, pour reconstruire un état cohérent de la
base, on parcourt le journal depuis la fin :
– soit on rencontre d’abord une entrée <END CHKPT> et dans ce cas, on sait que toutes les
transactions incomplètes ont démarré après l’entrée <START CHKPT(T1, ..., Tn)>
précédente. Les entrées du journal précédant <START CHKPT(T1, ..., Tn)> sont
donc inutiles.
– soit on rencontre d’abord une entrée <START CHKPT(T1, ..., Tn)> et dans ce cas,
le système est tombé en panne durant le point de contrôle. Il suffit donc de remonter
le journal jusqu’à la transaction Ti la plus ancienne.
Exemple 12.2.3 : Sur l’exemple précédent, on obtiendrait le journal par redo suivant :
Etape Action t MemC1 MemC2 DC1 DC2 Journal
0 0 1000 500 1000 500 <START T>
1 t := READ(C1) 1000 1000 500 1000 500
2 t := t - 100 900 1000 500 1000 500
3 WRITE(C1,t) 900 900 500 1000 500 <T,C1,900>
4 t := READ(C2) 500 900 500 1000 500
5 t := t + 100 600 900 500 1000 500
6 WRITE(C2,t) 600 900 600 1000 500 <T,C2,600>
7 600 900 600 1000 500 <COMMIT T>
8 FLUSH LOG 600 900 600 1000 500
9 OUTPUT(C1) 600 900 600 900 500
10 OUTPUT(C2) 600 900 600 900 600
12.2. REPRISE APRÈS PANNE 175
On vérifiera que l’on peut faire une reprise après panne en prenant le principe inverse
de l’undo-logging, i.e. en partant du début du journal et en rejouant toutes les transactions
qui se sont correctement déroulées. 2
Remarquons tout de suite que le journal sera plus volumineux que dans le cas de
l’undo logging ou du redo logging. De plus, l’entrée de commit <COMMIT T> peut être écrite
indifféremment après ou avant l’écriture effective des modifications sur le disque.
Exemple 12.2.4 : Toujours sur l’exemple précédent, voici un exemple de journal produit
par undo/redo :
Etape Action t MemC1 MemC2 DC1 DC2 Journal
0 0 1000 500 1000 500 <START T>
1 t := READ(C1) 1000 1000 500 1000 500
2 t := t - 100 900 1000 500 1000 500
3 WRITE(C1,t) 900 900 500 1000 500 <T,C1,1000,900>
4 t := READ(C2) 500 900 500 1000 500
5 t := t + 100 600 900 500 1000 500
6 WRITE(C2,t) 600 900 600 1000 500 <T,C2,500,600>
8 FLUSH LOG 600 900 600 1000 500
9 OUTPUT(C1) 600 900 600 900 500
10 600 900 600 900 500 <COMMIT T>
11 OUTPUT(C2) 600 900 600 900 600
Que se passe-t-il lors que le système tombe en panne après cette séquence ? Dans ce
cas, T2 et T3 sont considérées comme étant terminées. T1 est antérieure au démarrage du
point de contrôle et comme on trouve <END CHKPT> comme entrée, on considère que les
changements effectués par T1 ont été effectivement écrits sur le disque (car on écrit tous
les changements en mémoire principale sur le disque après <START CKPT(T)). On va donc
rejouer les transactions T2 et T3. Par contre, la première mise à jour de T2 étant effectuée
avant le point de contrôle, on est certain qu’elle a été écrite sur le disque (car on vide les
buffers).
12.3. GESTION DE LA CONCURRENCE 177
Par contre, si la panne arrive juste avant que l’entrée <COMMIT T3> ne soit écrite sur le
disque, alors T3 est considérée comme étant incomplète. Dans ce cas on va défaire T3 en
remontant dans le journal. 2
12.3.2 Séquentialité
Définitions
Dans cette section, nous nous intéressons de manière plus formelle aux conditions qui
doivent être réunies pour qu’un ensemble de transactions s’exécutant de façon concurrente
préserve la cohérence de la base de données. Nous partons pour cela d’une hypothèse forte :
Hypothèse 12.3.1.
Toute transaction exécutée isolément des autres transactions amène la base de données
d’un état cohérent vers un état cohérent. 2
12.3. GESTION DE LA CONCURRENCE 179
Exemple 12.3.1 : Soit une transaction T1 effectuant les opérations suivantes sur la base
de données :
– lecture d’un élément A
– modification de A
– lecture d’un élément B
– modification de B
Soit une deuxième transaction T2 effectuant les mêmes opérations. Un ordonnancement
pour T1 et T2 est :
Définition 12.3.2.
Soient T1 , . . . , Tn des transactions. Un ordonnancement σ sur T1 , . . . , Tn est séquentiel ssi
∀i ∈ {1, . . . , n} ∀i ∈ {1, . . . , n} i 6= j, si une action de Ti précède une action de Tj dans σ,
alors toutes les actions de Ti précédent toutes les actions de Tj dans σ. 2
Un ordonnancement séquentiel est donc un ordonnancement qui assure que les transac-
tions s’effectuent les unes après les autres.
180 CHAPITRE 12. GESTION DES TRANSACTIONS
Cet ordonnancement n’est pas séquentiel. Pourtant, il produira le même résultat qu’un
ordonnancement séquentiel : quelques soient les contraintes sur la base et en particulier sur
A et B, si T1 et T2 respectent l’hypothèse 12.3.1, alors ces contraintes seront respectées après
l’exécution de cette séquence d’actions. On dit alors que l’ordonnancement est sérialisable.
Définition 12.3.3.
Un ordonnancement est sérialisable si son exécution produit le même résultat qu’un ordon-
nancement séquentiel. 2
Définition 12.3.6.
Soit σ un ordonnancement comportant les transactions T1 et T2 . On dit que T1 précède T2 ,
noté T1 <σ T2 s’il existe une action A1 de T1 et une action A2 de T2 telles que :
– A1 précède A2 dans σ ;
– A1 et A2 concernent le même élément de la base ;
– au moins une des deux actions est une action d’écriture.
On construit un graphe de précédence de la façon suivante : chaque nœud du graphe
est une transaction et il existe un arc du nœud Ti au nœud Tj si Ti <σ Tj . 2
Un graphe de précédence pour cet ordonnancement est présenté sur la figure 12.2.
T1 T2 T3
Figure 12.2 : Graphe de précédence pour r2 (A); r1 (B); w2 (A); r2 (B); r3 (A); w1 (B); w3 (A); w2 (B)
Théorème 12.3.1.
Un ordonnancement ayant un graphe de précédence acyclique est sérialisable par conflit.
Un ordonnancement sérialisable par conflit a un graphe de précédence acyclique. 2
Preuve :
La seconde partie du théorème se montre rapidement par contraposée. La première partie
se montre par induction sur le nombre de transactions dans la base.
12.3.3 Verrouillage
Nous cherchons donc à avoir des ordonnancements de transactions concurrentes qui
soient sérialisables. Une des façons de garantir cette propriété est d’utiliser un système
de verrous. Le principe du verrouillage est que lorsqu’une transaction ne veut pas qu’un
élément de la base de données ne soit modifié tant qu’elle l’utilise, elle pose un verrou sur
cet objet. L’effet du verrou est d’empêcher tout autre transaction de modifier ou d’accéder
à la valeur de l’élément sur lequel le verrou est posé.
Nous allons donc poser deux grands principes :
182 CHAPITRE 12. GESTION DES TRANSACTIONS
Principe 12.3.1.
Une transaction ne peut accéder ou modifier un élément que si elle a un verrou sur cet
élément et qu’elle ne l’a pas relâché. Si une transaction a un verrou sur un élément, elle
devra relâcher ce verrou. C’est le principe de cohérence des transactions. 2
Principe 12.3.2.
Deux transactions ne peuvent pas posséder en même temps un verrou sur un même élément.
C’est le principe de légalité des ordonnancements. 2
On peut construire des systèmes de verrouillage simple. Nous allons nous intéresser ici à
un système de verrous un peu plus complexe composé de verrous exclusifs et partagés. On
pourra se référer à [27] pour plus de détails sur les verrous, en particulier sur les systèmes
de gestion des verrous.
Preuve :
La démonstration se fait classiquement par induction sur le nombre de transactions.
transactions d’un élément. Par contre, on ne peut pas avoir à la fois un verrou exclusif et
un verrou partagé sur un même élément.
Les possibilités de lecture ou d’écriture sur un élément suivant le type de verrou posé
sont représentées sur le tableau 12.4.
Lecture Écriture
X Oui Oui
Verrous
S Oui Non
Les possibilités d’obtention d’un verrou sur un élément par une transaction suivant
le verrou déjà posé sur cet élément par une autre transaction sont représentées sur le
tableau 12.5.
Demande de verrou
X S
Verrou X Non Non
posé S Non Oui
On pourra remarquer que les principes de cohérence des transactions, de légalité des
ordonnancements et de 2PL sont facilement transposables avec ce système de verrous de
deux types (cf. [27] pour plus de détails).
Le protocole d’accès aux données d’une base est le suivant :
Principe 12.3.3.
Une transaction qui souhaite accéder à la valeur d’un élément doit d’abord obtenir un
verrou S sur cet élément.
Une transaction qui souhaite modifier un élément doit d’abord obtenir un verrou X sur
cet élément.
Si une demande de verrou émise par la transaction T2 est refusée car elle entre en
conflit avec un verrou déjà détenu par la transaction T1 , la transaction T2 est mise en
attente. T2 devra attendre jusqu’à ce que T1 abandonne le verrou.
Une transaction possédant un verrou de type S sur un élément peut demander un verrou
de type X sans relâcher son verrou. 2
Nous allons maintenant revenir sur le trois problèmes que nous avions présentés en
début de section. Nous allons pour cela introduire trois notations :
– sli (X) signifie que la transaction Ti demande un verrou de type S sur l’élément X ;
– xli (X) signifie que la transaction Ti demande un verrou de type X sur l’élément X ;
184 CHAPITRE 12. GESTION DES TRANSACTIONS
L’opération UPDATE de la transaction T1 à l’instant t3 n’est pas acceptée, car c’est une
demande implicite de verrou X sur A qui rentre en conflit avec le verrou S détenu par la
transaction T2 . T1 est donc mise en attente. Pour des raisons identiques, T2 est mise en
attente à l’instant t4 .
Le verrouillage a résolu le problème de la perte d’une mise à jour en le remplaçant par
un problème de blocage (voir section 12.3.4).
La figure 12.7 présente la solution au problème de dépendance non validée en utilisant
le système de verrouillage.
L’opération UPDATE de la transaction T2 n’est pas acceptée à l’instant t4 , car c’est une
demande implicite de verrou X sur C1 qui entre en conflit avec le verrou S déjà détenu par
T1 . La transaction T2 est donc mise en attente.
De façon similaire, le RETRIEVE de la transaction T1 à la date t5 n’est également pas
accepté, car c’est une demande implicite d’un verrou S sur C3 qui entre en conflit avec le
verrou X déjà détenu par T2 . T1 est donc également mise en attente.
On se retrouve donc avec un blocage.
12.3.4 Blocage
Comme nous venons de le voir, le verrouillage peut générer des problèmes de blocage,
alors qu’il était censé résoudre les problèmes de sérialisabilité. Si un blocage se produit, il
est souhaitable que le système le détecte et le supprime.
Nous présentons une solution simple pour gérer ce problème : détecter le blocage revient
à détecter un cycle dans le graphe d’attente (wait-for graph). Il faut savoir que le système
maintient une table de verrous qui lui permet de savoir quelles sont les transactions en
attente d’un verrou et quelles sont les transactions possédant des verrous.
186 CHAPITRE 12. GESTION DES TRANSACTIONS
Définition 12.3.8.
Soit T1 , . . . , Tn des transactions. Un graphe d’attente est un graphe où :
– les nœuds du graphe sont les transactions ;
– il existe un arc du noeud Ti vers le nœud Tj ssi il existe un élément de la base A tel
que Tj possède un verrou sur A, Ti attende un verrou sur A et que Ti ne peut obtenir
son verrou que lorsque Tj aura relâché le sien. 2
S’il n’y a pas de cycle dans le graphe, alors les transactions vont pouvoir se terminer.
Dans le cas contraire, il y a un blocage. Il faut donc annuler une des transactions qui cause
le cycle.
On va choisir une des transactions bloquées comme victime et l’annuler, ce qui libère
ses verrous et permet aux autres transactions de poursuivre leur exécution.
Remarque : La victime a « échoué » et a été annulée sans avoir commis de faute. Certains
systèmes vont automatiquement relancer l’exécution d’une telle transaction depuis le début,
en faisant l’hypothèse que les conditions qui ont causé le blocage ne vont probablement
pas se reproduire. D’autres systèmes retournent simplement un message « victime d’un
blocage » à l’application ayant initié la transaction. On laisse alors au programme le soin
de gérer au mieux cette situation. 2
Il existe d’autres méthodes pour résoudre les blocages. Par exemple, on peut utiliser un
timeout pour les détecter, un ordre sur les éléments de la base etc. On peut se référer à
[27] pour plus de détails.
1. la relation est l’élément de plus haut niveau que l’on peut verrouiller ;
2. chaque relation est composée de blocs qui contiennent eux-même des tuples ;
3. enfin chaque tuple est verrouillable.
Dans le verrouillage intentionnel, il existe deux types de verrous. Tout d’abord, on peut
manipuler des verrous X et S qui ont le sens vu précédemment et qui s’appliquent aux
trois niveaux de granularité. Puis, on peut manipuler des verrous intentionnels : le verrou
intentionnel partagé (IS), le verrou intentionnel exclusif (IX).
Principe 12.3.4.
Le principe du verrouillage intentionnel est le suivant :
1. si l’on veut placer un verrou S ou X sur un élément, on commence en haut de la
hiérarchie ;
2. si on se situe sur l’élément que l’on veut verrouiller, on demande alors le verrou
correspondant ;
3. si l’élément est « plus bas » dans la hiérarchie, alors on pose un verrou intentionnel
correspondant sur ce nœud. 2
Pour déterminer si une transaction est autorisée à avoir un verrou particulier, on utilise
la table 12.9.
Verrou demandé
IS IX S X
IS Oui Oui Oui Non
Verrou IX Oui Oui Non Non
Posé S Oui Non Oui Non
X Non Non Non Non
A2
ar eI
1p rep not
or
re ady
A1 C J2
A3
(a) si le coordinateur a reçu le message ready T de la part de tous les sites, alors
il écrit l’entrée <Commit T> dans son journal et envoie le message commit T à
tous les sites ;
(b) si le coordinateur a reçu le message don’t commit T de la part d’un des sites,
alors il écrit l’entrée <Abort T> dans son journal et envoie le message abort T
à tous les sites ;
rt I
A2
abo
or
mm it
3 co
A1 C
A3
Le coordinateur attend que tous les sites lui répondent dans la phase II. Si un site ne ré-
pond pas après un certain temps, il considère qu’il lui a envoyé un message don’t commit T.
Cette procédure garantit que les transactions concernant des bases de données distribuées
seront durables : même en cas de panne d’un des sites, on pourra récupérer toutes les bases
dans un état cohérent.
190 CHAPITRE 12. GESTION DES TRANSACTIONS
Syntaxe (transaction) :
START TRANSACTION
...
[COMMIT|ROLLBACK]
Une transaction qui peut lire et écrire des données est déclarée comme suit (c’est ce
qui se passe par défaut) :
niveau d’isolation va permettre d’améliorer les performances du SGBD, mais peut conduire
à un état incohérent de la base.
On précise le degré d’isolation d’une transaction de la façon suivante :
Les niveaux d’isolation possibles sont présentés ici du plus strict (SERIALIZABLE qui
assura un comportement correct des transactions) au moins strict (READ UNCOMMITED).
Le fait de changer de niveau d’isolation peut amener des erreurs dans la gestion des
transactions (violation de la sérialisabilité). Ces comportements anormaux sont au nombre
de trois :
– la lecture salissante (dirty read ) : supposons que la transaction T1 effectue une mise
à jour sur un certain tuple, que la transaction T2 récupère ensuite ce tuple et que
la transaction T1 soit annulée par un ROLLBACK. Les valeurs des attributs du tuple
observé par T2 sont alors fausses.
– la lecture non renouvelable : supposons que la transaction T1 récupère un tuple, que
la transaction T2 effectue ensuite une mise à jour de ce tuple et que la transaction
T1 récupère de nouveau le « même » tuple. La transaction T1 a en fait récupéré le
même tuple deux fois mais a observé des valeurs différentes.
– le fantôme : supposons que la transaction T1 récupère un ensemble de tuples qui
satisfont une certaine condition. Supposons que la transaction T2 insère ensuite une
ligne qui satisfait la même condition. Si la transaction T1 répète maintenant la même
demande, elle observera une ligne qui n’existait pas précédemment (que l’on appelle
un fantôme).
Le tableau 12.10 présente maintenant quelles sont les violations de sérialisabilité
autorisées par les différents niveaux d’isolation.
On pourra remarquer que l’apparition de fantômes peut être évitée grâce à un ver-
rouillage du chemin d’accès utilisé pour obtenir les données considérées. Par exemple, on
peut verrouiller les entrées d’un index sur l’élément.
192 CHAPITRE 12. GESTION DES TRANSACTIONS
12.6 Conclusion
La gestion des transactions a pour but de garantir les propriétés ACID pour les
transactions gérées par un SGBD. Ces propriétés sont très importantes, car elles nous
garantissent que les transactions vont laisser une base de données dans un état cohérent,
même si elles s’exécutent de façon concurrente.
Les principes de la reprise après panne nous permettent d’assurer la durabilité des
transactions. Nous n’avons vu ici que les procédures de journalisation undo et undo/redo.
Le lecteur souhaitant plus de détails peut se référer à [27] et à [32].
En ce qui concerne la gestion de la concurrence parmi les transactions, il existe d’autres
méthodes de verrouillage : verrous mis à jour, verrous incrémentaux etc. La gestion de
la concurrence peut également être effectuée par points de contrôle temporisés et par
validation. Là encore, on pourra se référer à [27] et à [30]. Un type de sérialisabilité, la
sérialisabilité par vue, n’a pas été abordé ici. On pourra là encore trouver des détails dans
[27].
Nous avons survolé le problème des bases de données distribuées. Nous avons présenté
le principe du commit à deux phases, mais il en existe d’autres plus performants : commit
à trois phases par exemple. Nous n’abordons pas ces sujets par manque de place et de
temps.
Finalement, la gestion des transactions est assez transparente pour un utilisateur d’un
SGBD. Il existe toutefois des commandes SQL qui permettent de gérer plus finement les
transactions au niveau utilisateur.
Chapitre 13
Le modèle relationnel est un modèle simple. Il est composé de tables plates, les attributs
des relations sont des types simples etc. Malheureusement, ce modèle peut être parfois
insuffisant pour obtenir une modélisation plus fine de la réalité. Par exemple, on pourrait
vouloir avoir des attributs dans une relation qui ne soient plus des types simples, mais
des types complexes. Les caractéristiques des langages de programmation orientés objets
sont intéressantes pour la modélisation de bases de données : système de typage puissant
(collections, références), notion de classe, héritage. Il peut donc être utile d’introduire
ces notions dans un système de gestion de bases de données. De plus, les analyses et les
modèles de systèmes complexes sont de plus en plus faits actuellement via des modèles
objets supportant ces notions.
Un langage de programmation orienté objets comme C++, Java ou Smalltalk permet
de créer, manipuler et détruire des objets en mémoire virtuelle. La durée de vie d’un objet
n’excède pas la durée de vie du programme qui a servi à créer cet objet. Par ailleurs, comme
dans tous les langages de programmation, il est possible de sauvegarder les objets dans
des fichiers pour prolonger leur durée de vie. Ce type de sauvegarde fournit une première
forme de persistance des objets qui assez primitive. En effet :
– la persistance n’est pas transparente, elle doit être gérée explicitement par le ou les
programmes qui accèdent aux objets.
– la programmation de ces opérations de sauvegarde est délicate car les adresses des
objets sont souvent des adresses virtuelles et il faut donc les convertir en adresses du
support physique. De plus, lors de la définition de ce mécanisme d’adressage, il faudra
gérer les relations inter-objets : la sauvegarde d’un objet peut amener à sauvegarder
en fait tout un ensemble d’objets en relation avec ce dernier.
– l’accès aux objets en mémoire, contrôlé par le mécanisme de mémoire virtuelle, peut
avoir de très mauvaises performances, et même au pire nécessiter un accès disque par
objet.
– enfin, lorsque les objets sont stockés dans des fichiers physiques par des utilisateurs
concurrents, l’accès à ces objets et difficile et nécessite la connaissance de la structure
des fichiers. Cela engendre le problème de la dépendance physique entre programmes
et données.
193
194 CHAPITRE 13. BASES DE DONNÉES OBJETS
Nous retrouvons ici tous les problèmes engendrés par la gestion des données sur des
fichiers classiques.
L’approche base de données objets apporte une solution récente à la gestion transparente
de la persistance des objets. Deux approches pour la construction d’un système de gestion
de base de données à objets (SGBDO) ont été dégagées. L’approche relationnel-objet,
représentée par le standard SQL :1999 (ou SQL-99) et l’approche orientée objet représentée
par le standard de l’ODMG (Object Data Management Group).
Nous verrons également dans le chapitre ? ? qu’une solution actuellement utilisée pour
la persistance d’objets est l’utilisation d’un mécanisme transparent de mapping vers une
base de données relationnelle.
Ce chapitre présente les notions de base de données objets et de système de gestion de
bases de données objets (SGBDO).
Objectstore
Objet Gemstone
O2
ODMG
DB2
Relationnel INGRES
Oracle
SQL
IMS
Navigationnel IDS
System2000 IMS Fast-Path
Dédié R&D
1960 1970 1980 1990 2000
satisfont cet ensemble de besoins. Les domaines d’application non traditionnels des bases de
données comme l’ingénierie (CAO, FAO, génie logiciel, . . . ), la bureautique, l’administration
de réseaux et actuellement les données sur le web engendrent des besoins nouveaux. Dans
ce cas, le modèle de données doit être beaucoup plus riche avec de nouveaux types de base
(graphique, texte, données géométriques, . . . ) et doit permettre l’utilisation de structures
complexes (hiérarchie ou graphe). De plus, le langage doit permettre l’expression de calculs
ou de mises à jour complexes sur des objets de base. Enfin, de nouvelles fonctionnalités
sont attendues : objets multimédias, gestion de version ou de configuration, transactions
évoluées etc.
de ce problème implique des conversions de types ainsi que des contrôles de types qui
compliquent la programmation. On peut se référer à [44] et [15] pour plus de détails.
Sémantique Objets
entité objet
identité d’entité identité d’objet
type classe
attribut attribut
agrégation classe dépendante
généralisation héritage
associations
opération
encapsulation
Table 13.1 : Correspondance entre concepts issus de la sémantique et issus des objets
– la persistance (ou permance) des données : les objets sont stockés indépendamment
des programmes qui les créent et doivent être accédées rapidement grâce à des
opérations d’accès ;
– la gestion des transactions : les programmes d’accès à la base de données sont des
transactions qui vérifient les propriétés d’ACIDité (Atomicité, Cohérence, Isolation
et Durabilité) grâce aux mécanismes de contrôle de concurrence et de fiabilité ;
– l’indépendance physique des données : le schéma conceptuel de la base de données
n’impose pas d’implantation particulière des données, celle-ci est décrite par le schéma
physique ;
– la possibilité de requêtes : les requêtes portant sur le schéma conceptuel ou externe
doivent pouvoir être exprimées dans un langage déclaratif (comme SQL) qui se prête
à l’optimisation.
Définition 13.3.2 (SGBD objet).
La définition minimale d’un SGBDO est donnée par :
Approche relationnel-objet
Définition 13.3.4 (approche relationnel-objet).
L’approche relationnel-objet est fondée sur une extension du modèle relationnel et de son
langage de référence SQL avec les concepts de la programmation orientée objets. 2
Cette extension peut être vue comme une couche orientée objet traduite en relationnel.
On y considère que les concepts des bases de données relationnelles sont suffisamment
simples pour être étendus par des concepts orientés objets. Cette analyse a abouti au langage
SQL-99, extension de SQL2. Une extension plus poussée consiste à supporter directement
les concepts orientés objets au sein du SGBD relationnel ce qui implique d’étendre aussi
l’algèbre relationnelle (i.e. décrire les opérations propre aux concepts orientés objets par
des opérateurs dans l’algèbre relationnelle).
L’approche base de données orientées objets (BDOO) est duale à l’approche relationnel-
objet en ce sens qu’elle part d’un modèle à objets pour aboutir à un SGBDO. Il y a deux
façons de réaliser un SGBDOO selon que l’on parte d’un langage de programmation orienté
objets ou bien que l’on parte d’un modèle à objets.
Dans le premier cas, le langage doit être étendu pour permettre la définition de schémas
de bases de données à objets et l’écriture de programmes d’application qui intègrent des
appels au SGBDOO (db4o [3] avec Java et C#, ObjectStore avec C++ [7], Gemstone avec
Smalltalk [4]). Ces SGBDOO sont souvent appelés systèmes à objets persistants.
La seconde solution est de définir un modèle à objets qui intègre naturellement les
concepts des modèles de données sémantiques et des langages à objets. Le SGBDOO
peut alors être indépendant de tout langage de programmation et offrir une interface
multilangage (comme par exemple le SGBDOO O2 [15]).
La création d’objet persistant est alors complètement transparente. Il est alors par
contre nécessaire de réécrire un certain nombre de méthodes permettant d’assurer
la persistance pour chaque classe. De plus, l’héritage multiple n’étant pas implanté
3. OID se réfère à l’identifiant unique de l’objet.
4. Les opérateurs utilisés sont donnés à titre d’exemples.
13.4. CARACTÉRISTIQUES PRINCIPALES DES BASES DE DONNÉES OBJETS201
« proprement » dans tous les langages de programmation orientés objets, cette solution
peut être problématique (car on ne peut pas étendre dans ce cas une autre classe).
2. La persistance par référence associe la propriété de persistance directement à l’objet
et non à sa classe. C’est une technique qui permet de définir la persistance directement
par création explicite d’une racine de persistance ou par rattachement à un objet
persistant. Par exemple :
paris : site;
paris = new persistent (’paris’);
martin : employe;
martin = new employe(’martin’)
Ici, l’objet paris est persistant. Si l’objet martin est « stocké » dans l’objet paris,
alors il devient automatiquement persistant.
Avec cette technique, chaque nom d’objet persistant doit être mémorisé dans un
répertoire accessible par les programmes d’application.
Quelques éléments supplémentaires sur la persistance peuvent être trouvés dans [39] et
[40].
Objet
EnsembleEmployés
Les domaines d’application des bases de données à objets exigent des possibilités de
gestion de schémas beaucoup plus dynamiques et sophistiquées que celles offertes par les
systèmes relationnels.
Les aspects dynamiques suggèrent que des modifications puissent être faites à tout
moment, sans impact sur les utilisateurs concurrents, et qu’elles deviennent visibles aussitôt.
Les modifications de schémas agissent sur les définitions de classes et sur la structure
d’héritage dans le graphe de classes. Les invariants de classe que le schéma doit respecter
devront être conservés par toutes les mises à jour.
Les modifications de schémas supportées par un SGBDO sont classées en quatre
catégories :
1. modification d’un attribut de classe : transparente si l’encapsulation est respectée ;
2. modification d’une opération de classe : doit être répercutée sur les opérations des
programmes et éventuellement redéfinie dans les sous-classes ;
3. modification des liens d’héritage d’une classe ;
4. modification du graphe de classes.
Ces évolutions de schémas doivent préserver la cohérence du graphe de classes, des
données et des programmes d’application. La liaison tardive ou liaison dynamique est un
bon moyen pour éviter les modifications de programmes lors de la redéfinition d’opérations.
Elle est présente dans la plupart des langages de programmation orientés objet.
Trois approches permettent de maintenir les objets cohérents en présence de modifica-
tions de schémas :
1. approche manuelle : interdiction de mise à jour d’un type dès que des instances de ce
type existent. Il faut recopier les objets dans un nouveau type (assez lourd).
2. approche mise à jour immédiate : propage automatiquement et immédiatement les
mises à jour du schéma sur les objets concernés.
13.5. L’APPROCHE RELATIONNEL-OBJET 203
3. approche mise à jour paresseuse : diffère la mise à jour des objets jusqu’au prochain
accès en mémoire.
L’évolution de schémas est un besoin essentiel des SGBDO. Bien que plusieurs SGBDO
apportent des solutions à la mise à jour dans les cas simples, les modifications complexes
restent à la charge du programmeur. Le cas général ne peut être automatisé, mais peut être
grandement assisté par des outils et des interfaces graphiques permettant la manipulation
des schémas, de programmes et d’objets.
On peut bien évidemment utiliser d’autres UDT pour construire un UDT (i.e. un UDT
peut apparaı̂tre comme type d’un champ d’un UDT).
Exemple 13.5.1 : On peut définir un type Employe comme suit.
CREATE TYPE Employe AS (
nom CHAR(10),
date_naissance DATE
);
Un UDT SQL :1999 correspond à la notion de classe et peut alors avoir des opérations
associées. Pour déclarer une méthode associée à un type, on utilise la syntaxe suivante :
Syntaxe (déclaration d’une méthode d’un UDT) :
CREATE TYPE T AS (
...
nomChamp TypeChamp,
...
)
METHOD nomMethode() RETURNS Type;
13.5. L’APPROCHE RELATIONNEL-OBJET 205
Exemple 13.5.2 : On peut écrire une méthode age pour le type Employe qui permet de
calculer l’âge de l’employé.
CREATE TYPE Employe AS (
nom CHAR(10),
date_naissance DATE
)
METHOD age() RETURNS INTEGER(4);
On peut donc maintenant spécifier que les tuples d’une relation sont d’un certain type.
La relation possède comme attributs au moins les champs du type qui spécifie ses tuples.
On peut bien évidemment ajouter d’autres attributs dans la définition de la table. Il est à
noter que la définition de la clé primaire et des clés étrangères est de la responsabilité de la
table, non du type défini.
206 CHAPITRE 13. BASES DE DONNÉES OBJETS
Exemple 13.5.3 : On peut créer une table dont les tuples sont des employés, auxquels
on a ajoutés une date d’arrivée dans l’entreprise.
CREATE TABLE listeEmployes OF Employe(
date_arrivee DATE,
PRIMARY KEY(nom)
);
Exemple 13.5.4 : On peut maintenant écrire une requête qui nous donne les noms des
employés dont l’âge est inférieur à 35 ans.
SELECT l.nom()
FROM listeEmployes AS l
WHERE l.age() < 35;
Une notion importante dans les langages orientés objets est la notion d’identité d’un
objet (un objet a une identité unique). Une table dont les tuples sont définis comme étant
d’un certain type peut avoir une « colonne » référence qui permet d’avoir un identifiant par
tuple de la relation. Pour cela, on utilise la syntaxe suivante dans la déclaration de la table.
Syntaxe (référence) :
CREATE TABLE ... (
...
REF IS attRef [SYSTEM GENERATED|DERIVED]
...
);
On construit donc la référence à partir d’un ou plusieurs attributs (par exemple la clé
primaire de la table). Si on utilise le mot-clé SYSTEM GENERATED, c’est le SGBD qui est
responsable de cette référence. Si on utilise le mot-clé DERIVED, le SGBD utilisera la clé
primaire de la table comme référence.
Un type structuré peut être utilisé comme attribut d’une table (ce qui n’est pas autorisé
en SQL2). Si on utilise un type structuré « simple », il n’y a aucun problème, on aura bien
un attribut qui sera en fait composé des champs du type structuré (il faut toutefois faire
attention à l’insertion de tuples et entourer l’instance du type composé de parenthèses).
Par contre, on peut décider d’avoir un attribut dans une relation qui soit de type structuré
et qui soit un tuple d’une relation de ce type. Dans ce cas, comment identifier le tuple ?
On utilise alors une référence.
Syntaxe (référence vers un « même » type) :
attribut REF(Type) SCOPE Relation
13.5. L’APPROCHE RELATIONNEL-OBJET 207
Dans ce cas, attribut a pour type le type d’une référence vers un tuple de type Type.
le scope permet de ne considérer que des tuples appartenant à la table Relation (qui doit
bien entendu être de type Type).
Il est à noter que l’on peut également écrire des constructeurs et des destructeurs pour
les types structurés. On pourra se référer à [35] pour plus de détails.
L’accès aux éléments du tableau se fait de façon habituelle en utilisant l’opérateur [].
On peut noter que les tableaux permettent de coder, via les références des associations
entre tables.
On voit donc que chaque attribut est accessible par une « méthode » qui porte son nom.
De plus, on a besoin d’un nom de variable (ici t) pour le tuple pour pouvoir accéder aux
champs de l’objet. On peut ainsi se ramener à l’appel de méthode, voir la section 13.5.2.
Syntaxe (déréférencement) :
DEREF(att)
Cette commande renvoie le tuple entier référencé par att. On peut également accéder à
un champ particulier ci de l’objet référencé via la syntaxe suivante :
Denotable Object
Object Literal
Set<T>
Bag<T> Imm-Set<T>
List<T> Imm-Bag<T>
Array<T> Imm-List<T>
Imm-Array<T>
N
Type
Class
N key list
name Implementation extent name Exists ?
type
super types
Instanciation
Has
N
Object N
OID Property
has names ?
names
class
create Attribute Traversal path Relationship
delete attr name path name
exists attr type to cardinality 2 create
same has ? to type Has
set value delete
get value iterator add member
traverse remove member
Syntaxe :
class nomClasse extends C1 : ... : Cn
(extent nomExtent key nomCle)
{
attribute Type nomAtt1;
attribute enum nomEnum {’String1’, ..., ’Stringn’} nomAtt2;
attribute Struct nomStruct {Type1 champ1, ..., Typen champn} nomAtt3;
...
relationship [Set<C>|Bag<C>|List<C>|Array<C,i>|C] nomRelation
inverse C::relation;
...
TypeRetour nomMethode([in|out|inout] TypePar, ...) raises(nomException);
...
}
2
212 CHAPITRE 13. BASES DE DONNÉES OBJETS
On peut également spécialiser la classe Employe en une classe Ingenieur qui aura un
attribut supplémentaire.
class Ingenieur:Employe (extent ingenieurs) {
attribute string projet;
};
String nom;
String adresse;
public:
void affecter(Ref<Site> s);
String projet;
}
String nom;
Ref<Employe> chef;
public:
void ajouter(Ref<Employe> e );
}
La classe Persistent_Object fournit les opérations pour créer (new) et détruire les
objets persistants ou temporaires. La classe Ref sert à manipuler les références d’objets en
surchargeant les opérateurs de manipulation des pointeurs de C++ comme ->.
a.p
Syntaxe :
SELECT [DISTINCT] a.p
FROM Collection a
WHERE C(a)
ORDER BY ...
La clause SELECT est suivie d’une liste d’expression construites avec la notation pointée.
La clause FROM est suivi d’un ensemble de déclarations de variables typées avec une collection.
La plupart du temps, cette collection est en fait un extent (cf. 13.6.2). La clause WHERE est
suivie d’une expression booléenne qui ne peut manipuler que des variables déclarées dans
la clause FROM.
Le résultat de cette requête est un bag, c’est-à-dire un multi-ensemble non ordonné. On
peut demander à obtenir un ensemble plutôt qu’un bag en utilisant le mot-clé DISTINCT
après SELECT. On peut demander à obtenir une liste en utilisant la clause ORDER BY. On
voit ici que la syntaxe d’OQL est très proche de celle de SQL.
Expressions quantifiées
Il existe deux expressions OQL permettant de tester universellement ou existentiellement
une propriété sur une collection.
Pour tester si les éléments d’une collection C vérifient tous une propriété P , on utilise
la syntaxe suivante.
Syntaxe :
FOR ALL x IN C : P(x)
Pour tester s’il existe au moins un élément d’une collection C vérifiant une propriété P ,
on utilise la syntaxe suivante.
216 CHAPITRE 13. BASES DE DONNÉES OBJETS
Syntaxe :
EXISTS x IN C : P(x)
Agrégations et regroupements
On peut, tout comme dans SQL utiliser des expressions d’agrégations dans OQL. Dans
SQL, ces expressions s’appliquaient à une colonne particulière d’une relation. Dans OQL,
les opérateurs d’agrégations s’appliquent à une collection dont les membres sont d’un
certain type :
– l’opérateur COUNT peut être appliqué à n’importe quelle collection ;
– les opérateurs SUM et AVG peuvent être appliqués à des collections de type numérique ;
– les opérateurs MIN et MAX peuvent être appliqués à des collections dont le type est
« comparable ».
L’opérateur GROUP BY utilise la syntaxe suivante :
Syntaxe :
GROUP BY f1:e1, ..., fn:en
où chaque fi est un nom d’attribut et ei une expression. On appelle f i : ei un attribut
de partition. 2
Chaque ei peut utiliser des variables apparaissant dans la clause FROM de la requête.
Comment construire alors le résultat de la requête ? Supposons que la clause FROM concerne k
variables x1 , . . . , xk . Chaque xi parcourt une collection Ci . Pour chaque tuple hm1 , . . . , mk i ∈
C1 × . . . × Ck qui vérifie les expressions exprimées dans la clause WHERE, on évalue les
expressions de la clause GROUP BY et on obtient une liste de valeurs e1 (m1 , . . . , mk ),
. . . , en (m1 , . . . , mk ) qui représente le groupe auquel hm1 , . . . , mk i appartient.
La valeur retournée par la clause GROUP BY est en fait une collection intermédiaire
Struct (f1:v1, ..., fn:vn, partition:P). Les n premières valeurs de la structure
représentent le groupe et le dernier champ partition représente les hm1 , . . . , mk i qui
appartiennent au groupe. La clause SELECT s’applique ensuite sur ces différentes structures
avec des restrictions identiques au cas de SQL.
De la même façon qu’en SQL, on peut utiliser une clause HAVING pour restreindre les
tuples de chaque partition.
Notons également que comme OQL est très intégré au langage de programmation hôte,
on peut affecter des variables du langage de programmation aux résultats d’une requête
OQL si le type de la variable est compatible avec le résultat fourni par la requête.
Cependant, la plupart des fonctionnalités de ces SGBDRO restent les mêmes que celles
décrites dans ce chapitre. Attention par contre, certaines fonctionnalités, comme l’héritage
entre UDT par exemple, ne sont pas toutes disponibles. Nous nous concentrerons donc
dans ce chapitre sur les SGBDOO.
OPAL [4] s’appuie sur SmallTalk et offre une persistance par héritage. Le modèle de
données de OPAL est une extension simple de Smalltalk (modèle tout objet). La classe
Object de Smalltalk est étendue avec des classes permettant la manipulation d’objets
persistants et les transactions. Un objet de OPAL obtient un identifiant persistant lors de
sa création. Une classe Collection fournit les classes de manipulation des agrégats. Afin de
pouvoir compiler les programmes d’application OPAL, Smalltalk, non typé, est enrichi par
les contraintes de types qui associent à chaque attribut le nom d’une classe OPAL donnant
son type.
Le modèle de persistance est un modèle par héritage de la classe Object de GEMSTONE.
Seules les instances de classes OPAL peuvent être gérée par le SGBDO.
GEMSTONE fournit une interface de programmation (API) pour des langages comme
C, C++, Smalltalk. Elle permet à un programme d’application de créer (instantiate),
lire (fetch), ou écrire (store) des objets de la base avec conversion automatique de
format, de lire des méta-informations (fetch info) et d’envoyer des messages (send) ou
des instructions Smalltalk (execute) à un objet OPAL. Dans ce dernier cas, l’opération
OPAL est exécutée directement par le SGBDO car Smalltalk est interprété.
13.7.2 Objectstore
OBJECTSTORE [7] étend C++ et offre une persistance par référence. Le modèle
de données d’Objectstore est une extension de C++ intégrant des objets (instances de
classes) et des valeurs (structures et littéraux). Ce modèle a servi de base à la définition de
l’interface ODMG avec C++.
Chaque objet d’une classe C++ gérée par Objectstore a un identifiant persistant et
peut être directement accédé à partir de son adresse virtuelle par une technique propriétaire.
Les associations binaires sont supportées par des liens inverses. Une bibliothèque de classes
C++ fournit des opérations pour gérer les collections, les transactions et les exceptions.
Objectstore offre deux types d’interface aux langages C++ et C : une bibliothèque
accessible par macros et un langage de plus haut niveau, le DML, qui doit être pré-compilé
(le pré-compilateur fait partie du SGBDO).
Un schéma est un fichier de définitions (header ) contenant des classes C++, utilisant
des expressions DML pour spécifier les collections et les liens inverses.
Le modèle de persistance ressemble à celui de l’ODMG. La persistance est spécifiée
uniquement lors de la création (par une première instruction new) et exige l’attachement à
un nom persistant ou à un objet persistant (par une seconde instruction persistent).
218 CHAPITRE 13. BASES DE DONNÉES OBJETS
13.7.3 Orion
Les deux prochains SGBD présentés dans ce chapitre s’appuient sur un modèle à objets
de plus haut niveau sémantique qu’un modèle basé sur les langages à objets.
Le modèle objet de Orion s’inspire de Lisp et de Smalltalk dans la mesure où tout
est considéré comme un objet (l’objet est l’unique moyen de représentation d’entités
conceptuelles). Il offre une persistance par héritage.
L’état d’un objet est défini par la valeur de ses propriétés. Un objet peut être primitif,
auquel cas il ne contient qu’une valeur, ou plus complexe auquel cas il possède un identifiant
unique et des propriétés pouvant référencer d’autres objets. Un objet est complètement
défini par ses propriétés (son état) et ses opérations. Une opération Orion est une fonction
Lisp qui opère sur l’objet est qui restitue son état. L’état d’un objet et ses opérations ne
sont pas directement accessible à un utilisateur. L’interface unique d’accès à un objet est
définie par les messages auxquels l’objet peut réagir (comme dans Smalltalk).
Orion offre une hiérarchie de classes primitives avec la classe Object qui est la racine
de trois classes : TypePrimitif, Collection et Classe. Cette dernière est une métaclasse
qui fournit des primitives pour concevoir de nouvelles classes et contient des objets qui
sont des classes.
Orion propose deux concepts importants de modélisation : les objets composés (compo-
site objects) et les versions d’objets.
Un objet composé est organisé sous forme d’une hiérarchie d’objets composants. Il
modélise la relation fait partie de (part_of) entre objets, différente de la relation est un
(is_a) fournie par la hiérarchie de classe. Les objets composés permettent le maintien
automatique de l’intégrité référentielle.
Un objet peut posséder un certain nombre de versions. Le fait qu’un objet puisse avoir
des versions doit être déclaré au niveau de sa classe. Une version peut être dérivée de l’objet
original ou d’une version de l’objet. Le support de la gestion des versions est motivé par
les applications techniques.
L’unique point d’entrée d’Orion est une extension simple du langage Lisp avec des
possibilités orientées objets et des fonctions de bases de données. La syntaxe de ces fonctions
combine celle de Lisp et d’autres langages de programmation à objets.
13.7.4 O2
O2 [15] offre un modèle de données objets avec valeurs et objets complexes. L’originalité
du modèle est sa capacité à traiter les valeurs avec la même généralité que pour les objets.
Une valeur a un type qui est atomique ou construit (tuple, liste, ensemble). Un objet a un
identifiant, une valeur donnant son état et des opérations applicables. Il appartient alors à
une classe qui est définie par son type d’objet et ses opérations.
L’héritage multiple est supporté, les conflits de noms sont résolus par renommage
explicite. O2 supporte aussi la liaison dynamique. Contrairement aux autres SGBDO qui
supporte l’héritage structurel, O2 offre une sémantique d’héritage plus riche basée sur
l’inclusion de type. L’héritage par inclusion de type est particulièrement intéressant pour
les classes collections.
13.8. CONCLUSION 219
L’unité de définition de données est le schéma qui comprend les définitions de types, de
classes, d’opérations et de noms persistants. La notion de schéma O2 permet de regrouper
les classes pour mieux contrôler la visibilité du graphe de classes. Un schéma correspond
alors à un module et peut importer ou exporter des définitions depuis ou vers d’autres
schémas.
Le modèle de persistance est par référence. Il permet à tout objet ou toute valeur O2
de persister indépendamment de son type. La persistance est obtenue par attachement à
une racine persistante, déclarée dans le schéma par un nom global associé à une valeur ou
à un objet O2.
O2 supporte des interfaces avec des langages comme C ou C++. Le langage O2C est
essentiellement une intégration de O2 dans C qui sert alors à l’écriture des opérations.
En revanche, le couplage avec C++ repose sur un mécanisme d’import/export de classes
O2. L’outil O2link convertit une classe C++ en une classe O2 et génère les opérations
pour créer (O2_new), lire (O2_read) et écrire (O2_write) de façon transparente des objets
C++ dans une classe O2. L’outil ExportToC++ fait l’opération inverse. Il convertit une
classe O2 en une classe C++ et ajoute les opérations pour créer, lire et écrire les objets
C++. De plus O2 supporte le langage de requête OQL de l’ODMG qui peut être appelé
depuis O2C ou C++ ou en mode interactif.
13.8 Conclusion
Nous avons donné les fonctions d’un SGBDO. Par comparaison aux SGBD relationnels,
les SGBDO
– offrent un plus grand niveau d’abstraction ;
– peuvent gérer les opérations partagées comme les données et supportent les objets
complexes grâce à un modèle plus riche qui combine les concepts des modèles à objets
et des modèles sémantiques ;
– apportent une solution génériques aux applications complexes qui étaient jusqu’alors
supportées par des systèmes dédiés.
Les SGBDO offrent les fonctions de persistance aux langages à objets en facilitant
l’intégration du langage de bases de données et du langage de programmation. La persistance
des objets s’obtient par une technique d’attribution de la persistance par héritage ou par
référence ou une combinaison des deux. Elle s’accompagne d’opérations algébriques de
manipulation des collections et de facilité d’évolution de schéma.
Deux approches à la définition d’un SGBDO ont émergé : l’approche relationnel-objet
avec le standard SQL :1999 et l’approche orientée objet avec le standard ODMG.
– l’approche relationnel-objet enrichit le modèle relationnel et SQL avec un langage
de types pour la définition d’objets complexes avec sous-typage. L’avantage de cette
approche est la compatibilité ascendante avec SQL standard et sa traduction dans
une algèbre pour objets complexes. Cependant, comme pour SQL, l’intégration avec
un langage de programmation implique des conversions de types et de données et
complique la programmation (deux langages différents). Un nouveau standard est en
cours de finalisation : SQL :2003.
220 CHAPITRE 13. BASES DE DONNÉES OBJETS
PostgreSQL [9] est un SGBD relationnel 1 libre (cf. [5]). Il est historiquement issu de
POSTGRES, un SGBD développé à l’Université de Californie à Berkeley. La documentation
de PostgreSQL se trouve en ligne [10]. Une forte communauté d’utilisateurs de PostgreSQL
est présente sur Internet, voir par exemple [1] pour la communauté francophone. On
remarquera également que PostgreSQL est utilisé non seulement dans des projets libres
(Creative Commons, Debian), mais également dans l’industrie plus traditionnelle ou des
gros organismes étatiques : Skype, University of California at Berkeley (évidemment. . . ),
State Department américain, Fujitsu, Cisco (cf. [9], onglet « About », puis « Featured
users » ainsi que l’onglet « Témoignages » dans [1]). Nous allons présenter rapidement
dans ce qui suit l’utilisation de ce SGBD.
Les exemples qui seront utilisés ici fonctionnent au Centre Informatique de SUPAERO.
On pourra se référer à [28] pour plus d’informations sur l’installation des différents logiciels.
Nous choisirons la base bdmexico86 disponible au Centre Informatique de SUPAERO
comme exemple dans ce qui suit. Le propriétaire de cette base est l’utilisateur michel dont
le mot de passe est platini.
Pour des raisons de lisibilité, les captures d’écran ont été placées en fin de chapitre.
221
222 ANNEXE A. UTILISATION DU SGBD POSTGRESQL
-- corps de la fonction
PL/Tcl, PL/Perl, et PL/Python. Nous allons choisir ici de travailler avec PL/pgSQL,
puisqu’il se rapproche beaucoup de SQL. Il faut donc déclarer dans la base de données
l’utilisation de ce langage 3 :
Le bloc permet donc de déclarer un certain nombre de variables, puis d’effectuer des
opérations. On dispose d’un certain nombre d’opérateurs, dont les plus utilisés sont présentés
dans le tableau. On peut bien sûr utiliser des blocs de qualification (cf. exemple ci après)
pour instancier des variables. On se référera à [10] pour plus de renseignements.
Opérateurs Signification
:= affectation
RETURN retour d’une expression
IF ... THEN ... ELSE ... END IF; conditionnelle
LOOP ... END LOOP; boucle sans condition se finissant par
EXIT ou RETURN
WHILE cond LOOP ... END LOOP; boucle tant que
FOR var IN ... LOOP ... END LOOP; boucle pour
FOR var IN requete LOOP ... END LOOP; boucle pour utilisant une requête :
var prend successivement les valeurs
de la requête et est habituellement
déclarée avec le type record
Exemple A.3.1 : Supposons que nous disposions de deux numéros d’identification d’étu-
diants et que nous voulions connaı̂tre quel est celui qui a la meilleure moyenne de notes
3. Attention, il faut avoir des droits de super-utilisateur pour faire cela (essentiellement pour des raisons
de sécurité). Les étudiants n’auront donc pas à utiliser cette commande normalement.
224 ANNEXE A. UTILISATION DU SGBD POSTGRESQL
contenues dans une table Notes(id, module, note) 4 . On veut donc écrire une fonction
bestStudent qui nous renvoie le numéro d’identification du meilleur étudiant. Voici la
fonction correspondante écrite avec PL/pgSQL :
CREATE OR REPLACE FUNCTION bestStudent(id1 INTEGER, id2 INTEGER)
RETURNS INTEGER AS $BESTSTUDENT$
DECLARE
moy1 REAL;
moy2 REAL;
BEGIN
moy1 := AVG(note) FROM Notes WHERE id = id1;
moy2 := AVG(note) FROM Notes WHERE id = id2;
4. Évidemment, tout cela peut être fait très simplement avec l’opérateur MAX. . . De plus, nous utilisons
ici des variables intermédiaires qui ne servent à rien.
A.3. ÉCRITURE DE FONCTION ET DE TRIGGER 225
Tout d’abord, avec PostgreSQL le code exécuté dans un trigger provient obligatoirement
d’une fonction utilisateur, que nous venons de voir, renvoyant un type trigger. De plus, on
ne peut pas utiliser des variables pour référencer l’ancien tuple et le nouveau, les noms
seront obligatoirement NEW et OLD. Enfin, il ne peut pas y avoir de clause WHEN, il faudra
utiliser la fonction utilisateur pour faire les vérifications. Supposons que l’on dispose d’une
fonction trigger_moyenne(), le trigger va alors s’écrire :
INSERT INTO Logiciel VALUES (’Oracle10’, ’SGBD’, ’Oracle’, ’USA’, ’Oracle’, 15000);
INSERT INTO Logiciel VALUES (’Oracle10’, ’SGBD’, ’Oracle’, ’USA’, ’Oracle’, 15000),
(’PostgreSQL2’, ’SGBD’, ’Oracle’, ’USA’, ’Oracle’, 0);
fonctionne correctement.
On utilise ici l’instruction RAISE EXCEPTION pour pouvoir lever une exception si l’in-
sertion n’est pas correcte.
Une fonction trigger peut retourner :
– NULL pour signaler que l’insertion en cours doit être ignorée (la levée d’une exception
produit le même résultat) ;
– une ligne qui a la structure attendue par la relation.
On consultera bien évidemment [10] pour plus de détails.
5. On n’utilise pas ici les variables NEW et OLD car elles n’ont pas de sens pour un trigger par statement
226 ANNEXE A. UTILISATION DU SGBD POSTGRESQL
A.4 pgAdmin
pgAdmin [8] est un outil graphique permettant d’administrer et d’interagir avec Post-
greSQL. Il est très souple et complet (il permet par exemple d’avoir une explication sur les
requêtes réellement faites lors d’une interrogation de la base). On le lance soit en ligne de
commande depuis un terminal avec la commande pgadmin3, soit depuis les menus de votre
environnement s’il y est disponible. On obtient alors une fenêtre identique à celle présentée
sur la figure A.1.
On peut alors voir les détails de la base bdmexico86 comme montré sur la figure A.4.
On remarquera que la base est organisée en schémas et que le schéma par défaut est
public 6 . On trouvera donc sous ce schéma les relations, fonctions, triggers, . . . dont on a
besoin.
Enfin, il est toujours possible d’utiliser les menus contextuels sur un objet particulier
via le clic-droit de la souris. On peut ainsi visualiser plus facilement le contenu d’une
relation via le sous-menu « Afficher les données » disponibles sur la table Match comme
présenté sur la figure A.5 (on remarquera qu’une des icônes principales symbolisant une
table permet également de le faire).
On peut créer et supprimer une base de données (il sest également possible d’imposer
un mot de passe et un utilisateur responsable de la base) :
Syntaxe (création et destruction d’une base) :
createdb nom_base
dropdb nom_base
dropuser nom_user
Vous pouvez utiliser les manuels en ligne (commande man dropuser par exemple) pour
obtenir plus d’informations sur les options disponibles.
Exemple A.5.1 : Pour se connecter en tant qu’utilisateur eleve2 sur la base bd2, on
utilise la syntaxe suivante :
psql -h serv-sun1 bd2 eleve2
On obtient alors une invite de commandes. On peut envoyer des requêtes SQL directe-
ment à la base en utilisant le moniteur. Il existe toutefois quelques commandes spéciales
qui sont résumées dans le tableau
Attention, le moniteur interactif n’est pas forcément très facile à utiliser. Il vaut mieux
utiliser pgAdmin présenté en section A.4 si l’on n’est pas à l’aise avec la ligne de commande.
Commande Effet
\? liste les commandes psql possibles
\dt liste les tables existant dans la base
\h affiche l’aide sur les commandes SQL
\h command affiche l’aide de command
\i file.sql interpréte les commandes SQL de file.sql
\q quitte le client psql
Un menu est également disponible sous le triangle inversé en haut à droite de la vue
pour sauver la requête dans un fichier ou l’importer.
La perspective QuantumDB ne permet pas de travailler directement avec des fichiers.
Pour cela, il faut rebasculer sur une perspective classique, comme la perspective Java
et ouvrir un fichier avec pour extension .sql pour obtenir un éditeur SQL permettant
d’exécuter les requêtes sur une base (via un clic droit sur le fichier correspondant). On
remarquera que l’on peut toujours ajouter la vue Database Bookmarks dans n’importe
quelle perspective via « Window -> Show View -> Other... ».
La commande RAISE EXCEPTION ’problem with average price’ qui comporte une
chaı̂ne de cararctères s’écrira alors RAISE EXCEPTION ’’problem with average price’’
(on double les cotes).
Figure A.3 : Les bases de données disponibles sur le serveur et quelques détails sur
bdmexico86
A.7. UTILISATION AVEC JAVA 233
Figure A.7 : Une explication détaillée des opérations effectuées par PostgreSQL
A.7. UTILISATION AVEC JAVA 237
243
244 BIBLIOGRAPHIE
[37] T. W. Olle. The CODASYL Approach to Data Base Management. Wiley, 1978.
[38] E. Pinheiro, W.-D. Weber, and L. A. Barroso. Failure trends in a large disk drive
population. In Proceedings of the 5th USENIX Conference on File and Storage
Technologies (FAST’07, 2007. Available on http://labs.google.com/papers/disk_
failures.pdf.
[39] B. Reinwald, T. J. Lehman, H. Pirahesh, and V. Gottemukkala. Storing and using
objects in a relational database. IBM Systems Journal, 35(2) :172–191, 1996.
[40] V. Srninivasan and D. T. Chang. Object persistence in object-oriented applications.
IBM Systems Journal, 36(1), 1997. Available on http://www.research.ibm.com/
journal/sj/361/srinivasan.html.
[41] A. Tanenbaum. Systèmes d’exploitation. Dunod, 2nd edition, 2003. In French.
[42] A. Tanenbaum. Architecture de l’ordinateur. Dunod, 2006.
[43] J. S. Vitter. External memory algorithms. In Proceedings of the 7th Annual ACM
Symposium on Principles of Database Systems, pages 119–128, 1998.
[44] Wikipedia. Object-relational impedance mismatch. http://en.wikipedia.org/wiki/
Object-Relational_Impedance_Mismatch.