Disons que j'ai le diagramme ER suivant:
Maintenant, si je représentais la relation à l'aide d'une clé étrangère de School
dans Student
, je pourrais avoir des valeurs de NULL
(car il n'est pas nécessaire d'appartenir à un Student
un School
), par exemple:
Donc, la bonne façon (sur la base de ce que j'ai lu) est de créer une table d'intersection pour représenter la relation, par exemple:
De cette façon, aucune valeur NULL
ne peut être présente dans la table School_has_Student
.
Mais quels sont les inconvénients de l'utilisation d'une clé étrangère nullable au lieu de créer une table d'intersection?
Modifier:
J'ai choisi par erreur (school_id
, student_id
) pour être la clé primaire du School_has_Student
table, qui a rendu la relation plusieurs-à-plusieurs. La clé primaire correcte aurait dû être student_id
:
Les deux modèles représentent des relations différentes.
En utilisant une table de jointure, vous modélisez une relation plusieurs-à-plusieurs.
En utilisant une simple clé étrangère, vous modélisez une relation un-à-plusieurs.
L'inconvénient d'une clé étrangère annulable est de ne pas pouvoir modéliser la relation en plusieurs à plusieurs, si c'est ce que vous essayez d'accomplir.
En fonction de votre modification de la question, vous divisez efficacement la table des élèves en deux tables avec la même clé. Je vois généralement cela sur des tables qui ont beaucoup trop de champs, donc quelqu'un les divise en deux pour être plus faciles à gérer (je l'appelle mettre du rouge à lèvres sur un cochon).
En fractionnant la table des élèves, vous rendez la deuxième table facultative car il n'est pas nécessaire qu'un enregistrement existe dans la deuxième table. Ce qui est très similaire à un champ qui n'a pas besoin d'être défini car il peut être nul.
Si vous souhaitez une relation un-à-plusieurs, il est préférable d'utiliser une seule table et de permettre que l'ID de l'école soit nul dans la table des étudiants. Il n'y a aucune raison d'éviter les valeurs NULL dans les champs, même pour une clé étrangère. Cela signifie que la relation étrangère est facultative: les développeurs et les administrateurs de base de données le comprennent clairement, et le moteur de base de données sous-jacent devrait certainement fonctionner correctement.
Si vous vous inquiétez des jointures, ne vous inquiétez pas. Il existe une sémantique bien définie sur la façon dont les jointures fonctionnent avec les champs nuls. En utilisant une seule table, vous pouvez joindre deux tables au lieu de trois.
Vous avez écrit dans un commentaire ci-dessus:
le livre "Fundamentals of Database Systems" dit [...] qu'il est recommandé d'utiliser une table d'intersection s'il y a beaucoup de valeurs NULL dans la colonne de clé étrangère (par exemple: si 98% des employés ne gère pas de département)
Lorsqu'il y a beaucoup de valeurs NULL dans la colonne de clé étrangère, vos programmes devront traiter cette colonne, pour la plupart vide, pour chaque enregistrement qu'ils traitent. La colonne occupera probablement un peu d'espace disque même si dans 98% de tous les cas elle est vide, interroger la relation signifie interroger cette colonne qui vous donne plus de trafic réseau, et si vous utilisez un ORM qui vous génère des classes à partir de vos tables, de vos programmes aura également besoin de plus d'espace côté client que nécessaire. L'utilisation d'une table d'intersection évite cela, il n'y aura que des enregistrements de lien nécessaires où la clé étrangère équivalente ne serait pas NULL sinon.
En revanche, si vous n'avez pas seulement quelques valeurs NULL, disons que 50% ou plus de relations ne sont pas NULL, l'utilisation d'une table d'intersection vous donne l'effet inverse - plus d'espace disque, une complexité plus élevée entraînant plus de trafic réseau, etc.
Ainsi, l'utilisation d'une table d'intersection n'est qu'une forme d'optimisation, uniquement judicieuse pour un cas spécifique, et en particulier de nos jours, où l'espace disque et la mémoire sont devenus moins chers, beaucoup moins souvent nécessaires. Notez que "Fundamentals of Database Systems" a été écrit à l'origine il y a plus de 20 ans (j'ai trouvé une référence à la deuxième édition de 1994), et je suppose que cette recommandation était déjà là à l'époque. Avant 1994, l'optimisation de l'espace était probablement beaucoup plus importante qu'aujourd'hui, car le stockage de masse était encore plus cher et les ordinateurs et les réseaux étaient beaucoup plus lents qu'aujourd'hui.
En guise de remarque secondaire à un commentaire pointilleux: la déclaration ci-dessus tente simplement d'anticiper ce que l'auteur de "Fundamentals of Database Systems" avait à l'esprit avec sa recommandation, je suppose qu'il faisait une déclaration générale approximative, valide pour la plupart des systèmes. Dans certaines bases de données, il existe d'autres optimisations possibles comme des "colonnes éparses" qui rendent l'utilisation d'une table d'intersection encore plus obsolète.
Alors, ne vous méprenez pas sur cette recommandation. Le livre ne vous dit pas de préférer les tables d'intersection pour {0,1}:n
les relations en général, ou - comme vous l'avez écrit - que c'est la "bonne façon". Utilisez des optimisations comme celle-ci qui ne compliqueront vos programmes que lorsque vous en aurez vraiment besoin.
Le modèle conceptuel ressemblera à ceci, ce qui est très peu orthodoxe pour le moins:
Le modèle physique ressemblera à ceci, ce qui est déroutant pour le moins (les gens penseront que c'est M: M à moins qu'ils ne voient de près):
Ma suggestion:
Si vous avez de nombreuses colonnes (FK ou autres) qui ne s'appliquent pas à la plupart des étudiants, séparez les tables en tables de rôle avec des rouleaux 1: 1. Mais ce n'est pas parce qu'ils sont FK, c'est parce que les colonnes ne s'appliquent pas à la plupart des lignes.
Sinon , les FK annulables sont une partie normale d'une base de données et les tables de jointure sont généralement pour les rouleaux M: M.
Les utilisations courantes des rouleaux 1: 1 sont pour les tables de rôle ayant des colonnes qui s'appliquent uniquement si l'entité est d'un certain type, et l'extraction de colonnes BLOB pour les performances ou considérations de stockage. Avoder des valeurs nulles dans les FK n'est pas une utilisation courante pour cela.
En plus d'autres réponses, je voudrais souligner qu'une valeur nulle pour la clé étrangère est ambiguë. Est-ce que ça veut dire que:
1) L'école de l'élève (le cas échéant) est inconnue (il s'agit de la signification standard de "nul" - la valeur est inconnue)
2) On sait si l'élève a ou non une école, et il n'y en a pas
Si vous utilisez la signification standard de null, comment représenteriez-vous "l'élève n'a pas d'école" dans votre modèle de clé étrangère. Dans ce cas, vous devrez probablement créer une entrée "pas d'école", avec son propre identifiant dans la table de l'école. (Pas idéal)
Les tables de base de données ont cette belle chose appelée contraintes. Il est donc très facile de créer une table d'intersection qui permet à seulement 1 de chaque élève d'apparaître dans la table mais à de nombreuses écoles dans cette table. Vous donnant efficacement un
La théorie est sympa mais à la fin vous allez modéliser votre base de données d'après les questions que vous posez.
Si vous voulez poser des questions souvent avec la question: "quels élèves sont dans mon école" voulez-vous vraiment interroger la table entière des élèves ou avoir une table d'intersection facile.
Dans les bases de données: optimisez pour les questions que vous posez.
Il existe un cas d'utilisation où l'utilisation d'une troisième table peut réellement avoir un sens. L'exemple peut sembler purement hypothétique, mais j'espère qu'il illustre bien mon propos. Supposons que vous ajoutiez plus de colonnes à la table students
et à un moment donné, vous décidez d'appliquer l'unicité aux enregistrements via un index composite sur plusieurs colonnes. Il est très probable que vous deviez inclure le school_id
colonne aussi, et ici les choses commencent à devenir désordonnées. En raison de la conception de SQL, l'insertion de plusieurs enregistrements identiques où school_id
est NULL
sera possible. Il est parfaitement logique d'un point de vue technique, mais est contre-intuitif et peut conduire à des résultats inattendus. D'un autre côté, il est facile d'imposer l'unicité sur la table d'intersection.
J'ai dû modéliser une telle relation "facultative" récemment, où l'exigence d'une contrainte d'unicité était due à une colonne d'horodatage. Laisser la clé étrangère nullable dans la table conduit soudainement à la possibilité d'insérer des enregistrements avec le même horodatage (supposons que c'est un enregistrement par défaut, défini sur des enregistrements qui n'ont pas encore été audités/approuvés) - et la seule issue était de supprimer colonne nullable.
Donc, comme vous pouvez le voir, c'est un cas assez spécifique, et comme d'autres l'ont noté, la plupart du temps, vous seriez parfaitement d'accord avec toutes les valeurs NULL
. Cela dépend vraiment des exigences spécifiques de votre modèle.
En plus des nombreuses bonnes suggestions déjà soumises, personnellement, je ne suis pas fan des clés étrangères à moins qu'elles ne soient vraiment nécessaires. Il y a d'abord la relation M: M à laquelle vous faites référence. De plus, l'appel d'une clé étrangère, et donc l'extraction de ces données de table dans vos requêtes, introduit plus de complexité et, selon la taille de la table, des performances plus lentes. Comme d'autres l'ont dit, les champs FK annulables peuvent être non pris en charge et peuvent créer des problèmes d'intégrité des données.
Si vous définissez un état où l'école étudiante est inconnue ou vide, la valeur NULL ne différenciera pas ces conditions. (encore une fois, nous sommes de retour à l'intégrité des données.) La suggestion de table de rôle par Tulains est élégante et permet des valeurs nulles proprement.