Du modèle ER au schéma de base de données
La normalisation a une réputation d'exercice académique, méritée à force d'être enseignée comme cinq règles numérotées plutôt que comme une question posée sans cesse : ce fait a-t-il sa place ici ?
11 min de lectureER - crow's foot3 sur 3
La réponse courte
- La troisième forme normale en une phrase : chaque colonne non clé dépend de la clé, de toute la clé, et de rien d'autre que la clé.
- Une clé de substitution survit au métier qui change d'avis sur ce qui identifie une chose, ce qui finit toujours par arriver.
- Ne dénormalisez qu'après avoir mesuré le chemin de lecture. Avant la mesure, vous échangez une garantie de correction contre un gain non confirmé.
- Passer d'ER à SQL est mécanique : entité vers table, un-à-plusieurs vers une clé étrangère du côté plusieurs, plusieurs-à-plusieurs vers une table à part.
01La table par laquelle tout le monde commence#
Ce n'est pas un homme de paille : c'est la forme qu'a un tableur, et c'est par un tableur que commencent la plupart des schémas. Il vaut la peine d'être précis sur ce qui cloche, parce que "elle n'est pas normalisée" n'explique rien :
Ce qui suit est de la modélisation de schéma de base de données au sens ordinaire du terme : décider quelles sont les tables, quelle colonne identifie chaque ligne, et où un fait a le droit d'habiter. C'est l'étape entre un diagramme ER et un DDL qu'une base acceptera, et elle mérite d'être faite sur papier, parce que chacune de ces décisions coûte cher à défaire une fois qu'il y a des données dans la table.
- Vous ne pouvez pas stocker un client qui n'a rien commandé. Le client n'existe que sous forme de colonnes sur une commande.
- Corriger un e-mail oblige à mettre à jour toutes les lignes où ce client apparaît - et en oublier une laisse la base avec deux réponses différentes.
- Supprimer la dernière commande supprime le client. De l'information disparaît comme effet de bord de quelque chose qui n'a rien à voir.
product_namescontient une liste. Désormais toute requête qui veut un seul produit analyse une chaîne, et aucun index ne peut aider.totalpeut contredire les lignes dont il est censé être la somme.
Ces cinq-là ont des noms - anomalies d'insertion, de mise à jour et de suppression, un groupe répétitif et une valeur dérivée - et la normalisation n'est rien d'autre que la procédure qui les supprime.
02Choisir les clés d'abord#
Tout le reste en dépend, donc cela vient avant les tables. Une clé primaire doit être unique, jamais nulle et ne jamais changer. C'est la troisième condition qui élimine la plupart des candidats.
| Élément | Notation | Ce que cela signifie |
|---|---|---|
| Clé de substitution | bigint ou uuid | Générée, sans signification, stable pour toujours. Le choix par défaut. Un entier séquentiel est compact et se prête bien à l'indexation ; un UUID peut être généré par le client et ne divulgue aucun volume. |
| Clé naturelle | ISBN, IBAN, code pays | Porteuse de sens, et sûre uniquement lorsqu'un organisme de normalisation garantit qu'elle ne changera pas. Seule une courte liste qualifie vraiment. |
| Clé composite | deux colonnes ou plus | Le bon choix pour les tables de jonction, où la paire de clés étrangères est à la fois l'identité et la contrainte d'unicité que vous vouliez de toute façon. |
| Clé métier | numéro de commande, SKU | Destinée aux humains, elle doit être unique et devrait être une contrainte UNIQUE plutôt que la clé primaire - pour pouvoir être corrigée quand on y découvre une faute de frappe. |
03La normalisation, en trois étapes#
Il existe six formes normales et vous en avez besoin de trois. Chacune est une question unique posée à une table, et la réponse "non" vous dit quelle colonne déplacer où.
| Élément | Notation | Ce que cela signifie |
|---|---|---|
| Première forme normale | aucun groupe répétitif | Chaque colonne contient une seule valeur. Pas de listes séparées par des virgules, pas de product_1 / product_2 / product_3. La partie qui se répète part dans sa propre table. |
| Deuxième forme normale | aucune dépendance partielle | Chaque colonne non clé dépend de toute la clé. Ce n'est un problème qu'avec les clés composites : un nom de produit sur une table (order_id, product_id) dépend de la moitié de la clé, il appartient donc à products. |
| Troisième forme normale | aucune dépendance transitive | Aucune colonne non clé ne dépend d'une autre colonne non clé. Un customer_email sur une commande dépend du client, pas de la commande. Déplacez-le vers customers. |
Le vieux résumé reste le meilleur : chaque colonne non clé dépend de la clé, de toute la clé, et de rien d'autre que la clé.
Faites passer la table plate par ces trois questions. La 1FN éclate product_names et quantities dans une table order_lines, une ligne par produit. La 2FN remarque que le nom et le prix d'un produit ne dépendent que du produit, d'où l'apparition de products. La 3FN remarque que le nom et l'e-mail du client dépendent du client et non de la commande, d'où l'apparition de customers. Quatre tables, c'est-à-dire exactement le diagramme d'ouverture - et aucune étape n'a demandé de jugement, seulement la question.
04Valeurs dérivées et dénormalisation#
La colonne total de la table plate pose un problème différent des quatre autres : elle n'est pas mal placée, elle est redondante. Elle se calcule à partir des lignes de commande, elle peut donc les contredire, et finira par le faire.
Trois réponses légitimes, par ordre de préférence : la calculer dans la requête ; la calculer dans une vue ; la stocker et laisser la base l'entretenir - colonne générée, vue matérialisée ou déclencheur. Ce qui n'est pas légitime, c'est de la stocker en comptant sur le code applicatif pour penser à la mettre à jour : c'est la version qui produit des factures qui ne tombent pas juste.
À utiliser quand
- Un chemin de lecture mesuré est trop lent et la jointure en est la cause démontrée
- La valeur est un instantané, pas une dérivation - le prix au moment de la commande
- Une table de reporting alimentée par une tâche planifiée, nommée clairement comme telle
- La base peut entretenir la copie elle-même, elle ne peut donc pas dériver
Préférer autre chose quand
- Ce sera peut-être lent plus tard - mesurez d'abord, ce n'est généralement pas le cas
- Le code applicatif doit penser à garder la copie à jour
- Le doublon est aussi la source de vérité pour autre chose
- Vous dénormalisez le schéma transactionnel pour alimenter un tableau de bord
05Modéliser l'héritage#
Un modèle ER sait exprimer un surtype avec des sous-types ; une base relationnelle n'a pas cette construction, donc le modèle physique doit choisir l'une de trois dispositions. Les trois sont largement utilisées et le choix est un vrai arbitrage.
| Élément | Notation | Ce que cela signifie |
|---|---|---|
| Table unique | une table, une colonne de type | Les colonnes de tous les sous-types dans une seule table, la plupart nulles. Requêtes simples, pas de jointures - et la base ne peut pas imposer "un paiement par chèque doit avoir un code guichet". |
| Table par sous-type | une table chacun, clé partagée | Une table parente avec les colonnes communes et une table fille par sous-type, reliée par la clé. Les contraintes fonctionnent vraiment ; toute requête joint. |
| Table par type concret | tables entièrement séparées | Aucune table partagée. Rapide et net par sous-type - et interroger "tous les paiements" demande une union qui grandit à chaque sous-type ajouté. |
Règle grossière : peu de sous-types aux colonnes largement communes plaident pour la table unique ; beaucoup de sous-types aux colonnes divergentes et aux contraintes réelles plaident pour une table par sous-type ; et des sous-types jamais interrogés ensemble plaident pour des tables séparées. Le diagramme de classes du même domaine aura généralement choisi son héritage sans affronter rien de tout cela, et c'est pourquoi les deux modèles ont le droit de diverger ici.
06Arriver au DDL#
Un modèle ER logique se transpose en DDL presque mécaniquement, ce qui est la récompense de l'avoir dessiné correctement :
| Élément | Notation | Ce que cela signifie |
|---|---|---|
| Entité | CREATE TABLE | Une table. Nom de table au pluriel, nom d'entité au singulier - choisissez une convention et tenez-la. |
| Clé primaire | PRIMARY KEY | Implique not null et unique, et crée l'index. |
| Relation | REFERENCES | Une clé étrangère du côté plusieurs. Ajoutez l'index vous-même : la plupart des bases n'en créent pas pour une clé étrangère, et son absence fait ramper les suppressions. |
| Extrémité obligatoire | NOT NULL | La barre intérieure du pied-de-corbeau est exactement cette contrainte. |
| Un-à-un | UNIQUE sur la clé étrangère | Sinon c'est un un-à-plusieurs qui n'a qu'une seule ligne pour l'instant. |
| Entité de jonction | PRIMARY KEY composite | Les deux clés étrangères ensemble. C'est ce qui empêche d'enregistrer deux fois la même paire. |
Deux choses que le diagramme ne dit pas et que le DDL doit trancher : ce qui se passe à la suppression (CASCADE pour une relation identifiante, RESTRICT pour presque tout le reste), et quelles colonnes reçoivent des index au-delà des clés. Les deux sont des décisions de comportement et de charge, pas de modèle, et les deux méritent d'être écrites à côté du schéma plutôt que découvertes plus tard dans un journal de requêtes lentes.
07Erreurs courantes#
- Normaliser au-delà de l'utile. La 3FN est la destination d'un schéma transactionnel. Éclater une table parce qu'une colonne pourrait se répéter un jour n'apporte rien et coûte une jointure pour toujours.
- Pas de clés étrangères, "pour la performance". Le coût est une lecture d'index à l'écriture ; le bénéfice est que les lignes orphelines deviennent impossibles. Presque jamais le bon arbitrage.
- Des colonnes nullables à la place d'une table manquante. Six colonnes renseignées pour un seul type de ligne, c'est un sous-type qui réclame sa table.
statusen texte libre. Contraignez-le - contrainte de vérification, énumération ou table de référence - sinon il contiendrashipped,ShippedetSHIPPEDavant un an.- Des horodatages sans fuseau horaire. Justes exactement une fois, dans un seul bureau, jusqu'au premier déménagement de serveur.
- Le diagramme abandonné après la première migration. Un diagramme de schéma en désaccord avec la base est pire que pas de diagramme du tout, parce que les gens lui font confiance.
Si les symboles de cardinalité des diagrammes ci-dessus demandent un décodage, ils sont traités dans la notation pied-de-corbeau ; la forme du modèle lui-même est dans les diagrammes ER.
En une ligne chacun
- 01Choisissez les clés avant les tables : uniques, non nulles, et qui ne changent jamais.
- 02Chaque colonne non clé dépend de la clé, de toute la clé, et de rien d'autre que la clé.
- 03La 1FN éclate les groupes répétitifs, la 2FN corrige les clés partielles, la 3FN déplace les faits mal placés.
- 04Dénormalisez sur mesure, et seulement là où la base entretient la copie.
- 05Un prix historique n'est pas un doublon : c'est un autre fait.
- 06Le pied-de-corbeau se transpose en DDL presque mécaniquement ; indexez vos clés étrangères.
08Questions fréquentes#
Qu'est-ce que la troisième forme normale ?
Une table est en troisième forme normale quand chaque colonne non clé dépend de la clé, de toute la clé, et de rien d'autre que la clé. En pratique : pas de groupes répétitifs, aucune colonne dépendant d'une partie seulement d'une clé composite, et aucune colonne dépendant d'une autre colonne non clé.
Quelle différence entre clé de substitution et clé naturelle ?
Une clé naturelle est une donnée qui identifie déjà la ligne, comme un ISBN. Une clé de substitution est une valeur générée sans signification, entier ou UUID. Les substituts restent stables quand le métier change d'avis sur ce qui identifie une chose, ce qui finit toujours par arriver.
Quand la dénormalisation se justifie-t-elle ?
Quand vous avez mesuré un chemin de lecture que la normalisation a rendu trop lent, et que vous pouvez vivre avec la valeur dupliquée qui se périme. Dénormaliser avant d'avoir cette mesure échange une garantie de correction contre un gain de performance dont vous n'avez pas confirmé l'existence.
Comment implémenter l'héritage dans un schéma relationnel ?
Trois options : une table pour toute la hiérarchie, avec une colonne discriminante et beaucoup de colonnes nullables ; une table par classe concrète, avec les colonnes communes répétées ; ou une table par classe, jointes sur une clé partagée. Le bon choix dépend de la fréquence des requêtes traversant la hiérarchie, comparée à l'écart entre les sous-classes.
Comment transformer un diagramme ER en SQL ?
Chaque entité devient une table, chaque attribut une colonne, et l'attribut identifiant la clé primaire. Les relations un-à-plusieurs deviennent une clé étrangère du côté plusieurs, et les relations plusieurs-à-plusieurs une table à part entière. Ajoutez ensuite les contraintes not null et unique qu'impliquent les cardinalités du diagramme.
Dans cette série
- 01Diagrammes ER
- 02Notation pied de corbeau
- 03Conception de schéma
À lire aussi
Fondamentaux
Référence de notation
Diagrammes de structure
Diagrammes de structure