L'explication d'un tel succès tient dans les avantages que ce modèle apporte, par rapport à ses prédécesseurs de type "hiérarchique", "réseau" ou Codasyl et C.J Date, un des spécialistes fondateurs du relationnel en donne un résumé en un seul mot : "la simplicité". Simplicité tout d'abord pour l'utilisateur dans la conception, la définition, l'installation de la base de données. En reprenant la démonstration de Date sur la "simplicité" du modèle relationnel, on peut mettre en évidence la simplicité de la structure des données, la simplicité des opérateurs, l'indépendance entre les données et les applications.
Par exemple, la sélection de l'ensemble de lignes d'une table dont les valeurs répondent à un critère donné se traduit par une seule opération, en utilisant l'opérateur "select...from...where..." L'utilisateur disposera donc en relationnel d'un langage de manipulation des données simple, d'apprentissage facile.
Un SGBD est un outil logiciel qui permet, selon un modèle de base de données particulier, de gérer ces données en offrant les fonctionnalités suivantes :
L'utilisateur final n'accédera généralement qu'au langage de manipulation des données, soit directement en utilisant le langage standard SQL, soit au travers d'outils plus évolués offerts par le SGBD lui-même ou développés par l'administrateur. Classiquement, une base de données peut être représentée en trois niveaux :
R=(D1 X D2 X D3) est la relation R définie sur les domaines D1, D2, D3. La réalisation (l'extension) de cette relation est représentée
par une table à 3 colonnes D1, D2, D3, dont les lignes seront concrétisées
par les différentes valeurs possibles prises dans les domaines D1,
D2, D3 :
Le modèle relationnel consiste à représenter par une relation, ou table, chaque type d'objet ou entité du monde réel, en prenant pour colonnes les constituants caractéristiques de l'objet. Chaque colonne de la table a un identificateur, ou nom de colonne, ou "attribut", qui représente un des constituants de l'entité. Par exemple, pour modéliser l'entité "personne", on prendra comme constituants son nom, son prénom, sa date de naissance, sa ville, son département et on définira la table "personne" avec ses colonnes : personne (nom , prénom , date-naissance , ville , département). Les domaines correspondant aux identificateurs de colonnes peuvent être définis par les ensembles de valeurs suivants : nom : chaîne de 1 à 30 caractères alphabétiques prénom : chaîne de 1 à 30 caractères alphabétiques date-naissance : ensemble des dates depuis le 1er janvier 1900 jusqu'au 1er janvier 1994 ville : ensemble des noms de villes (ou chaîne de 1 à 40 caractères) département : entier compris entre 1 et 98
Les noms de colonnes (columns en anglais), constituants de l'objet, sont encore appelés attributs (attributes en anglais). Les lignes de la tables, ou rangées, (rows en anglais), sont encore appelées n-uplets (ou tuples en anglais), selon la terminologie relationnelle. Pour définir une table du modèle, il faut :
prenom char(30), date-naissance date, ville char(40), departement integer); Par exemple, en plus des données de personnes que nous avions prévues ci-dessus, nous pouvons avoir à gérer également des données relatives au transport, par exemple le ou les véhicules que possède chaque personne. Faut-il ajouter à la relation individus les attributs correspondant aux véhicules ou faut-il en faire une autre relation ? Ce type de choix peut et doit être mené selon les règles de normalisation établies pour le modèle relationnel. Ces règles ont pour but de constituer un modèle évitant les redondances inutiles de données et facilitant la gestion, la mise à jour des données. Elles définissent des "formes normales", dont nous allons voir les 4 premiers niveaux. Le respect de cette règle est obligatoire dans un SGBD réellement relationnel : les colonnes d'une table ne peuvent être multivaluées. Donc, pour représenter les voitures appartenant aux personnes, il n'est pas possible de constituer une colonne "type de voiture" dans la table personne, le nombre de voitures possédées pouvant être variable. Nous devrons créer une deuxième table "voiture-possédée" où le type sera une colonne à valeur simple. Pour que le modèle ait un sens, il faudra bien entendu tenir compte de l'association entre la voiture et son propriétaire et garder, dans la table "voiture-possédée" les colonnes faisant référence à la personne propriétaire. exemple : personne( nom, prénom, date-naissance, ville, departement) voiture-possédée( nom, prénom, type, date-achat, marque, couleur) Ceci introduit un concept essentiel du modèle relationnel : la clé d'une relation. Toute relation, ou table, en relationnel strict, doit comporter, parmi l'ensemble de ses colonnes, un sous-ensemble formant une clé unique : dire, par exemple, que le couple (c1, c2) est la clé de la relation R signifie que les valeurs des autres colonnes c3,...cn de R sont déterminées de façon unique par la valeur du couple (c1, c2). Dans la table "personne", on pourrait dire que le couple (nom, prénom) forme la clé, à condition que l'on ne puisse avoir deux personnes ayant même nom et même prénom. C'est cette clé de la table personne qui doit être rappelée dans la table "voiture-possédée" pour faire l'association entre les deux tables. On dit que le couple (nom, prénom) dans la table "voiture-possédée" est une clé étrangère (foreign key en anglais), liée à la clé primaire (nom, prénom) de la table "personne". Dans la table "voiture-possédée", la clé primaire peut être constituée par les trois colonnes nom, prénom, type (à condition qu'une personne ne puisse posséder plus d'une voiture d'un type donné). Il est parfois nécessaire de redéfinir la structure d'une table pour assurer qu'elle contient une clé primaire : par exemple, dans le cas de la table "personne", il est sans doute préférable d'avoir une colonne d'identification de la personne dont on est sûr qu'elle est unique, par exemple son code INSEE, pour éviter le cas où nom et prénom ne seraient pas discriminants. De même, on peut ajouter une colonne "rang" à la table "voiture-possédée" pour être sûr de constituer une clé unique. Ce qui donne le schéma suivant : personne (insee, nom, prenom, date-naissance, ville, departement) voiture-possédée (insee, rang, type, date-achat, marque, couleur) nb : les colonnes clés sont soulignées. La désignation de la clé de la relation, ou clé primaire est obligatoire dans certains SGBD relationnels. Elle ne l'est pas dans INGRES mais la définition de cette clé doit être présente à l'esprit de celui qui conçoit le modèle car elle conditionne, comme on va le voir, la normalisation des tables. Les étapes suivantes de normalisation des relations vont consister en une décomposition des tables de façon à respecter certaines règles de dépendances entre les colonnes clés et les colonnes non clés, ce qui permettra d'éliminer les redondances de données et les problèmes de mise à jour.
Une relation est en deuxième forme normale si et seulement si :
commande (nom-fournisseur, adresse-fournisseur, article, quantité, prix) La clé primaire est constituée par le couple (nom-fournisseur, article). Or, l'adresse du fournisseur ne dépend que de son nom. Cette table n'est donc pas en 2FN (2ème forme normale) et devra être décomposée en deux tables : fournisseur (nom-fournisseur, adresse) commande (nom-fournisseur, article, quantité, prix) On évite ainsi des redondances et des problèmes de mise à jour si l'adresse du fournisseur change.
Pour respecter la troisième forme normale, il faudra décomposer cette relation en deux nouvelles relations : voiture-possédée (insee, rang, type, date-achat, couleur) marque-voiture (type, marque) On voit là encore l'intérêt de la normalisation pour les mises à jour et l'élimination des redondances : on peut mettre à jour les voitures possédées sans toucher à la relation marque-voiture, la marque de voiture n'est pas répétée dans chaque ligne de voiture-possédée ayant même type.
Supposons que, pour un même type de voiture, le modèle existe en plusieurs couleurs, rouge, vert, bleu, et en plusieurs versions : normale, décapotable, break. Par exemple : un type R18 a un modèle en version normale et un modèle en version break, un type AX a un modèle en version normale et un modèle en version décapotable; le type R18 existe en couleur rouge et bleu, le type AX existe en couleur rouge et vert. On pourrait constituer une table "choix_modèle" de la façon suivante : choix_modèle( type, couleur, version) La table choix-modèle contiendrait les lignes suivantes :
Pour une valeur de type, on a toutes les valeurs possibles de couleur et, pour chacune de ces valeurs, toutes les valeurs possibles de version, mais couleur et version sont indépendantes entre elles : on dit que l'on a une dépendance multivaluée entre la colonne "type" et la colonne "couleur" et une dépendance multivaluée entre la colonne "type" et la colonne "version". On voit l'inconvénient de cette forme puisque, si l'on supprime une valeur possible de la colonne "version", par exemple décapotable pour le type AX, il faut supprimer toutes les lignes où apparaissent AX et décapotable. Pour éviter ce genre de problèmes, il faut passer à la quatrième forme normale, qui se définit théoriquement ainsi : Une relation est en quatrième forme normale si et seulement si les seules dépendances multivaluées élémentaires sont celles dans lesquelles une clé détermine la valeur d'une colonne. Ici, les colonnes sur lesquelles portent des dépendances multivaluées font partie de la clé, donc la table n'est pas en 4FN et il faut la décomposer en deux tables : choix_couleur( type, couleur) choix_version( type, version)
Toutefois, il arrive que l'on ne poursuive pas au maximum la normalisation du modèle car elle peut dégrader les performances à l'interrogation. En effet, la décomposition d'une table en plusieurs autres peut conduire à scruter plusieurs tables pour une recherche donnée (on dit faire une jointure), ce qui peut être pénalisant en temps de recherche.
- la contrainte de clé primaire d'une relation implique la non duplication des lignes, c'est-à-dire que chaque objet du monde réel peut être enregistré sans ambiguïté par une seule ligne, un seul "tuple"; - la contrainte de domaine restreint les valeurs possibles d'une colonne à un ensemble de valeurs prédéfinies; - on peut ajouter à une colonne la contrainte de non-nullité qui implique que cette colonne ne peut pas avoir de valeur nulle, c'est-à-dire ne peut pas être indéfinie ou inutilisable; - la contrainte d'intégrité référentielle spécifie les liens qui doivent exister entre deux tables, par la correspondance d'une ou plusieurs colonnes communes à ces deux tables, cette ou ces colonnes constituant ce qui est appelée une clé étrangère. Avec INGRES, la contrainte de clé primaire n'est pas obligatoire et n'a pas à être donnée lors de la création de la table, même s'il est bon de l'avoir définie dans la conception du modèle car elle est nécessaire au processus de normalisation. Les contraintes de domaine et de non-nullité sont gérées en langage SQL, lors de la création de la table. Le domaine est défini par le type de la colonne, soit dans INGRES
:
Une autre contrainte d'intégrité sur les données peut être ajoutée pour spécifier une condition que doit respecter la valeur d'une colonne. Ce qui signifiera qu'une mise à jour de la colonne ne respectant pas cette contrainte sera rejetée. exemple : La contrainte d'intégrité référentielle est, par exemple, de dire qu'une ligne d'une table voiture-possédée ne peut exister si la personne à laquelle elle se rapporte n'existe pas. Avec INGRES et son extension base de connaissances, on peut définir des règles qui sont des mécanismes par lesquels on invoque une procédure spécifique à chaque fois qu'une condition est vraie : par exemple, on fera en sorte de supprimer toutes les lignes de voiture-possédée se référant à une personne si cette personne est enlevée de la base : execute procedure del_voitures (insee = old.insee); Une opération relationnelle conduit à construire une nouvelle relation (ou table), résultat d'une opération sur une ou plusieurs relations (ou tables) opérandes. L'algèbre relationnelle comprend les opérations classiques de la théorie des ensembles : union, intersection, différence. exemple : R= étudiant ( nom, prenom) S= études (bac, filière) R
S
T = R * S
exemple : la projection de R sur le nom donne une table ne contenant
que la colonne "nom"; les doubles, s'ils existent, sont éliminés.
exemple : la restriction de R pour la condition "prénom de plus de 4 lettres" donne les 2 premières lignes. R
exemple : le produit cartésien vu ci-dessus donne toutes les possibilités d'études pour chacun des étudiants; supposons maintenant que les étudiants enregistrés aient passé leur bac et que l'on ait l'attribut "bac" dans la relation "étudiants", ce qui donnerait : R2
On peut créer la table T2 donnant les filières possibles pour chaque étudiant en fonction du bac, en faisant la jointure de la table R2 avec la table S, sur la propriété : le bac de l'étudiant est le même que le bac de la table "études". Donc, du produit cartésien précédent, on ne garde que les lignes suivantes : T2
La jointure interne est la jointure classique vue ci-dessus : on combine toutes les lignes de R à toutes les lignes de S pour lesquelles la valeur de l'attribut "bac" est identique dans les deux tables. Si la table R contenaient des lignes dont la valeur de l'attribut "bac" n'existait pas dans la table S, ces lignes n'apparaîtraient pas dans la jointure interne. De même si la table S contenaient des lignes dont la valeur de l'attribut "bac" n'existait pas dans la table R, ces lignes n'apparaîtraient pas dans la jointure interne. exemple :
résultat de la jointure interne :
La jointure externe donne une table résultat comprenant des lignes qui ne sont pas en correspondance entre les deux tables origines. Elle est dite "externe à gauche" si on prend toutes les lignes
de la table de gauche de l'opération de jointure, par exemple T3-gauche
= R3*S3
Elle est dite "externe à droite" si on prend toutes les lignes
de la table de droite de l'opération de jointure, par exemple T3-droite
= R3*S3
Elle est dite "externe complète" si on prend toutes les lignes
des deux tables de l'opération de jointure, par exemple T3-externe-complète
= R3*S3
avec tri des résultats : select * from table_t where qualification order by col_1, col_2...; avec expression = attribut_i op valeur conj est un opérateur de connexion ( and, or) op est un opérateur de comparaison ( =, <, >, <>, != , <=, >= ) * est une convention pour sélectionner toutes les colonnes. I-5-3-3 la jointure (par défaut elle est interne) : from table_1 t1, table_2 t2 where t1.col_i = t2.col_j ; qualification multi-colonnes select col_1,... from table_1 where col_i in (select col_j from table2) On peut également envisager tout type d'opérateur de comparaison entre les colonnes mises en jointure. On peut aussi faire une jointure sur plus de deux colonnes appartenant aux deux tables. Enfin, on est parfois amené à faire la jointure d'une table sur elle-même (auto-jointure). from table_1 t1 left join table_2 t2 on t1.col_i = t2.col_j ; from table_1 t1 right join table_2 t2 on t1.col_i = t2.col_j ; from table_1 t1 full join table_2 t2 on t1.col_i = t2.col_j ; exemple : union select nom from employe
exemple : on fait le calcul des moyennes de salaires, par catégorie : group by categorie create table create integrity create view ... Toutes ces fonctions sont développées dans le chapitre SQL.
études ( bac, filière) sélection des étudiants ayant un bac A : where r.bac = s.bac ; vehicules ( insee, rang, type, age) modele ( type, marque, puissance) sélection des individus ayant plus d'un véhicule : from individus i, vehicules v where i.insee = v.insee and v.rang > 1 ; from vehicules v , modele m where v.rang = 2 and v.type = m.type ; from vehicules v, modele m, individus i where v.insee = i.insee and i.departement = "78" and v.type = m.type ; (il s'agit ici de faire une auto-jointure de la relation "vehicules") from vehicules v1, vehicules v2 where v1.insee = v2.insee and v1.rang = 1 and v2.rang = 2 ; |
.