Votre conception actuelle s'appelle arcs exclusifs où le sets
table a deux clés étrangères et a besoin d'exactement l'une d'entre elles pour être non nulle. C'est une façon d'implémenter des associations polymorphes, puisqu'une clé étrangère donnée ne peut référencer qu'une seule table cible.
Une autre solution consiste à créer une "supertable" commune que les deux users
et schools
références, puis utilisez-les comme parent de sets
.
create table set_owner
create table users
PK is also FK --> set_owner
create table schools
PK is also FK --> set_owner
create table sets
FK --> set_owner
Vous pouvez considérer cela comme analogue à une interface en modélisation OO :
interface SetOwner { ... }
class User implements SetOwner { ... }
class School implements SetOwner { ... }
class Set {
SetOwner owner;
}
Vos commentaires :
Laissez la table SetOwners générer des valeurs d'ID. Vous devez insérer dans SetOwners avant de pouvoir insérer dans Users ou Schools. Alors faites en sorte que les identifiants dans Utilisateurs et Écoles ne soient pas auto-incrémentation ; utilisez simplement la valeur qui a été générée par SetOwners :
INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');
De cette façon, aucune valeur d'identifiant donnée ne sera utilisée à la fois pour une école et un utilisateur.
Vous pouvez certainement le faire. En fait, il peut y avoir d'autres colonnes communes aux utilisateurs et aux écoles, et vous pouvez placer ces colonnes dans la supertable SetOwners. Cela entre dans l'héritage de table de classe de Martin Fowler motif.
Vous devez faire une jointure. Si vous interrogez à partir d'un ensemble donné et que vous savez qu'il appartient à un utilisateur (pas à une école), vous pouvez ignorer la connexion à SetOwners et rejoindre directement les utilisateurs. Les jointures ne doivent pas nécessairement passer par des clés étrangères.
SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ...
Si vous ne savez pas si un ensemble donné appartient à un utilisateur ou à une école, vous devrez faire une jointure externe aux deux :
SELECT COALESCE(u.name, sc.name) AS name
FROM Sets s
LEFT OUTER JOIN Users u ON s.SetOwner_id = u.id
LEFT OUTER JOIN Schools sc ON s.SetOwner_id = sc.id
WHERE ...
Vous savez que SetOwner_id doit correspondre à l'une ou l'autre table, Utilisateurs ou Écoles, mais pas aux deux.