Uw huidige ontwerp heet exclusieve bogen waar de sets
tabel heeft twee externe sleutels en er moet er precies één niet-null zijn. Dit is een manier om polymorfe associaties te implementeren, aangezien een gegeven externe sleutel slechts naar één doeltabel kan verwijzen.
Een andere oplossing is om een gemeenschappelijke "supertable" te maken die zowel users
en schools
referenties, en gebruik dat dan als de ouder van 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
Je kunt dit zien als analoog aan een interface in OO-modellering:
interface SetOwner { ... }
class User implements SetOwner { ... }
class School implements SetOwner { ... }
class Set {
SetOwner owner;
}
Over uw opmerkingen:
Laat de SetOwners-tabel id-waarden genereren. U moet in SetOwners invoegen voordat u kunt invoegen in Gebruikers of Scholen. Dus maak de id's in Gebruikers en Scholen niet automatisch ophogen; gebruik gewoon de waarde die is gegenereerd door SetOwners:
INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');
Op deze manier wordt er geen bepaalde ID-waarde gebruikt voor zowel een school als een gebruiker.
Dit kun je zeker doen. Er kunnen zelfs andere kolommen zijn die zowel voor gebruikers als voor scholen gelden, en u kunt deze kolommen in de supertabel SetOwners plaatsen. Dit komt in Martin Fowler's Class Table Inheritance patroon.
Je moet een join doen. Als u een vraag stelt vanuit een bepaalde Set en u weet dat deze van een gebruiker is (niet van een school), kunt u de deelname aan SetOwners overslaan en rechtstreeks deelnemen aan Gebruikers. Joins hoeven niet per se met externe sleutels te gaan.
SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ...
Als je niet weet of een bepaalde set toebehoort aan een gebruiker of een school, moet je een outer join doen voor beide:
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 ...
U weet dat de SetOwner_id moet overeenkomen met de ene of de andere tabel, Gebruikers of Scholen, maar niet beide.