Objectif : traduire le schéma E/A obtenu précédemment en un ensemble de tables prêtes à être créées dans la base.
Dans une première partie on cherche à obtenir une représentation intermédiaire : le schéma relationnel.
Ensuite, celui-ci sert de support à l'implémentation physique.
Pour chaque entité, on commence par rechercher les références à des clés d'autres entités, indiquées par des dièses #.
Exemple :
clients(numéro_client, nom_client, prénom_client, adresse_client)
commandes(numéro_comd, date_comd, #numéro_client (non vide))
Quelques contraintes peuvent être spécifiées sur les attributs, comme "unique" ou "not null" (non vide ci-dessus).
Intégrité référentielle : Le SGBD vérifie à chaque opération que les clés étrangères ne prennent que des valeurs autorisées.
Notations : on dit qu'une association binaire (entre deux entités ou réflexive) est de type :
Remarque :
Un schéma relationnel ne peut faire la différence entre 0,n et 1,n.
Par contre, il peut la faire entre 0,1 et 1,1 (cf. règles 2 et 4 à suivre).
Règle 1 : toute entité devient une table dans laquelle les attributs deviennent les colonnes. L'identifiant de l'entité constitue alors la clé primaire de la table.
Règle 2 : une association binaire de type 1 : n disparaît, au profit d'une clé étrangère dans la table côté 0,1 ou 1,1 qui référence la clé primaire de l'autre table. Cette clé étrangère ne peut pas recevoir la valeur vide si la cardinalité est 1,1.
fournisseurs(
num_four, nom_contact, num_tel_contact)
livraisons(
num_livr, date_livr, nom_livr, #num_four (non vide))
Règle 3 : une association binaire de type n : m devient une table supplémentaire (table de jointure, table d'association ...) dont la clé primaire est composée de deux clés étrangères (qui référencent les deux clés primaires des deux tables en association). Les attributs de l'association deviennent des colonnes de cette nouvelle table.
Règle 4 : une association binaire de type 1 : 1 est traduite comme une association binaire de type 1 : n, sauf que la clé étrangère se voit imposer une contrainte d'unicité en plus d'une éventuelle contrainte de non vacuité (cette contrainte d'unicité impose à la colonne correspondante de ne prendre que des valeurs distinctes).
Règle 5 : une association non binaire est traduite par une table supplémentaire dont la clé primaire est composée d'autant de clés étrangères que d'entités en association. Les attributs de l'association deviennent des colonnes de cette nouvelle table.
Objectifs :
Il existe six formes normales graduelles, visant chacune une robustesse accrue et des économies de mémoire ; en pratique on se contente souvent des trois premières.
En général plus une table est normalisée, moins les performances (en temps d'exécution) sont bonnes. C'est pourquoi on préfère parfois dénormaliser a posteriori certaines parties d'une base pour améliorer ces performances.
Une relation (schéma de table) est en 1NF si aucun de ses attributs ne peut prendre de valeur qui soit elle-même un ensemble.
Cependant, PostGreSQL (et d'autres SGBDR) permet la création de types composés, pouvant être utilisés comme attributs.
Exemples :
Nom | Instrument | Adresse |
---|---|---|
Albert | Piano | 12 rue des lilas |
Albert | Clavecin | 12 rue des lilas |
Andrea | Clarinette | 23 rue des mimosas |
Andrea | Hautbois | 23 rue des mimosas |
Andrea | Flûte | 23 rue des mimosas |
Antoine | Basson | 34 rue des tulipes |
Solution :
Nom | Instrument |
---|---|
Albert | Piano |
Albert | Clavecin |
Andrea | Clarinette |
Andrea | Hautbois |
Andrea | Flûte |
Antoine | Basson |
Nom | Adresse |
---|---|
Albert | 12 rue des lilas |
Andrea | 23 rue des mimosas |
Antoine | 34 rue des tulipes |
CREATE TABLE marins(nom, date_de_naissance, PRIMARY KEY(nom));
CREATE TABLE embarquements(marin, date_embarquement, bateau, tonnage)
PRIMARY KEY(marin, date_embarquement);
Est-ce en 2NF ? En 3NF ? Si non, quel est le problème ?
Solution : comme précédemment,
CREATE TABLE marins(nom, date_de_naissance, PRIMARY KEY(nom));
CREATE TABLE embarquements(marin, date_embarquement, bateau)
PRIMARY KEY(marin, date_embarquement);
CREATE TABLE bateaux(bateau_ID, tonnage, PRIMARY KEY(bateau_ID));
Une relation $R$ est en forme normale de Boyce-Codd (BCNF) si et seulement si pour chaque dépendance fonctionnelle non triviale $A \rightarrow B$ qui doit être vérifiée par $R$, $A$ est une surclé de $R$.
DF triviale : $A \rightarrow B$ avec $B \subseteq A$
Surclé : ensemble d'attributs noté $X$ tel que $\forall Y \in R, X \rightarrow Y$
Course | Book | Lecturer |
---|---|---|
AHA | Silberschatz | John D |
AHA | Nederpelt | John D |
AHA | Silberschatz | William M |
AHA | Nederpelt | William M |
AHA | Silberschatz | Christian G |
AHA | Nederpelt | Christian G |
OSO | Silberschatz | John D |
OSO | Silberschatz | William M |
Une relation est en 4NF si les seules DM (dépendances multivariées) sont celles dans lesquelles une clé multidétermine un attribut.
Définition : $X$ "multidétermine" $Y$ si pour chaque valeur (n-uplet) de $X$ l'ensemble des valeurs (n-uplets) possibles pour $Y$ est restreint, et que cet ensemble est indépendant des autres attributs.
Remarque : une DF est un cas particulier de DM.
Une dépendance de jointure (DJ) sur une relation R est la donnée d'un n-uplet $X_1,..., X_n$ de sous-ensembles d'attributs de R vérifiant $$R = \pi_{X_1}(R) \bowtie \pi_{X_2}(R) \bowtie \dots \bowtie \pi_{X_n}(R)$$
Définition : une relation R est en 5NF si toutes les DJ sont impliquées par les clés.
Un modèle physique de données est l'implémentation particulière du modèle logique de données par un logiciel.
La traduction d'un MLD conduit à un MPD qui précise notamment le stockage de chaque donnée à travers son type et sa taille (en octets).
Si nécessaire, cette traduction est l'occasion d'un certain nombre de libertés prises par rapport aux règles de normalisation afin d'optimiser les performances du système.
Possibilités :
Exemple clients/achats/produits (association n : m).
Obtenir la liste des achats avec les nom des clients et produits nécessite deux jointures : opération coûteuse.
Attention cependant : la mise à jour des noms demandera plus de travail sous cette forme (maintenance plus complexe).
Un schéma E/A se traduit quasi automatiquement en un schéma relationnel, décrivant directement la structure des tables.
Il peut être nécessaire de dénormaliser certaines parties de la base si l'on constate des problèmes de performance.
Note 1 : un certain nombre de situations ne sont pas modélisables par la méthode décrite jusqu'alors. Des informations complémentaires intéressantes se trouvent par exemple sur ce fil, dans la FAQ Merise MCD ou encore dans la FAQ Merise MLD.
Note 2 : face à une base de données existante mais mal conçue, on peut suivre les étapes inverses (normalisation, obtention et re-conception du schéma E/A).
CREATE TABLE my_first_table (
first_column text,
second_column integer );
Suppression :
DROP TABLE my_first_table;
CREATE TABLE Livres (
Auteur varchar(64),
Titre text,
Date date,
ISBN integer );
DROP TABLE Livres;
column-def :
Exemple :
ALTER TABLE Livres RENAME TO Bouquins;
ALTER TABLE Bouquins ADD COLUMN nb_pages integer;
Workaround (from sqlite3 FAQ)
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t_backup(Auteur,Titre,ISBN);
INSERT INTO t_backup SELECT Auteur,Titre,ISBN FROM Bouquins;
DROP TABLE Bouquins;
CREATE TABLE Livres(Auteur,Titre,ISBN DEFAULT 0);
INSERT INTO Livres SELECT * FROM t_backup;
DROP TABLE t_backup;
COMMIT;
Most SQL database engines uses static, rigid typing. With static typing, the datatype of a value is determined by its container - the particular column in which the value is stored.
SQLite uses a more general dynamic type system. In SQLite,
the datatype of a value is associated with the value itself,
not with its container.
Illustration :
INSERT INTO Livres (ISBN) VALUES (42); -- Ok : type integer
INSERT INTO Livres (ISBN) VALUES ("13"); -- Ok : converti
INSERT INTO Livres (ISBN) VALUES ("hello world!"); -- Ok : text
Each stored value has one of the following storage classes:
A storage class represents several
datatypes
-- Indiquer seulement certains champs
INSERT INTO Livres (Auteur) VALUES ('Fred Vargas');
INSERT INTO Livres (Auteur,Titre) VALUES
('Alain Damasio', 'La Horde du Contrevent');
-- Plusieurs lignes en une commande
INSERT INTO Livres (Auteur, Titre) VALUES
('Haruki Murakami','La Course au mouton sauvage')
('Haruki Murakami','La Fin des temps')
('Haruki Murakami','La Ballade de l''impossible')
SELECT * FROM Livres; Fred Vargas|| Alain Damasio|La Horde du Contrevent|0 Haruki Murakami|La Course au mouton sauvage|0 Haruki Murakami|La Fin des temps|0 Haruki Murakami|La Ballade de l'impossible|0
.mode csv
.import my_file.csv my_table
-- Vérification
.schema my_table
SELECT * FROM my_table;
Note : If the table already exists, the sqlite3 tool uses all the rows, including the first row, in the CSV file as the actual data to import. Therefore, you should delete the first row of the CSV file.
Exemples
UPDATE Livres SET ISBN = 32 WHERE ISBN IS NULL;
UPDATE Livres SET ISBN = 42 WHERE Auteur = 'Alain Damasio';
SELECT * FROM Livres; Fred Vargas||32 Alain Damasio|La Horde du Contrevent|42 ...
Exemples
DELETE FROM Livres WHERE ISBN = 32;
DELETE FROM Livres WHERE Titre = 'La Fin des temps';
SELECT * FROM Livres; Alain Damasio|La Horde du Contrevent|42 Haruki Murakami|La Course au mouton sauvage|0 Haruki Murakami|La Ballade de l'impossible|0
Note : DELETE FROM Livres;
supprime les données mais pas la table
Un SGBD est dit transactionnel s'il vérifie les propriétés
Les SGBDR usuels ont ces propriétés – y compris SQLite ☺
(Contre-)Exemple imaginaire avec la table
CREATE TABLE acidtest (
A INTEGER, B INTEGER CHECK (A + B = 100));
$T_1$ transfert 10 de A à B, et $T_2$ transfert 5 de B à A.
Possible exécution simultanée :
Si panne entre 3] et 4] :
$\Rightarrow$ Échec A, C, I (mais pas D).