Dit nuværende design kaldes eksklusive buer hvor sets
tabellen har to fremmednøgler, og skal præcis én af dem være ikke-nul. Dette er en måde at implementere polymorfe associationer på, da en given fremmednøgle kun kan referere til én måltabel.
En anden løsning er at lave en fælles "supertable", som begge users
og schools
referencer, og brug det derefter som overordnet for 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
Du kan tænke på dette som analogt med en grænseflade i OO-modellering:
interface SetOwner { ... }
class User implements SetOwner { ... }
class School implements SetOwner { ... }
class Set {
SetOwner owner;
}
Vedrørende dine kommentarer:
Lad SetOwners-tabellen generere id-værdier. Du skal indsætte i SetOwners, før du kan indsætte i enten Users eller Schools. Så gør id'erne i Brugere og Skoler ikke automatisk stigning; bare brug den værdi, der blev genereret af SetOwners:
INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');
På denne måde vil ingen given id-værdi blive brugt for både en skole og en bruger.
Du kan helt sikkert gøre dette. Faktisk kan der være andre kolonner, der er fælles for både brugere og skoler, og du kan placere disse kolonner i supertabellen SetOwners. Dette kommer ind i Martin Fowlers Klassebordsarv mønster.
Du skal deltage. Hvis du forespørger fra et givet sæt, og du ved, at det tilhører en bruger (ikke en skole), kan du springe over at deltage i SetOwners og slutte sig direkte til brugere. Joins behøver ikke nødvendigvis at ske med fremmednøgler.
SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ...
Hvis du ikke ved, om et givet sæt tilhører en bruger eller en skole, skal du lave en ekstern joinforbindelse til begge:
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 ...
Du ved, at SetOwner_id skal matche den ene eller den anden tabel, Brugere eller Skoler, men ikke begge.