Twój obecny projekt nazywa się ekskluzywnymi łukami gdzie sets
table ma dwa klucze obce i dokładnie jeden z nich musi mieć wartość inną niż null. Jest to jeden ze sposobów implementacji asocjacji polimorficznych, ponieważ dany klucz obcy może odwoływać się tylko do jednej tabeli docelowej.
Innym rozwiązaniem jest stworzenie wspólnego „supertable”, aby obaj users
i schools
referencje, a następnie użyj go jako rodzica 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
Można to potraktować jako analogiczne do interfejsu w modelowaniu OO:
interface SetOwner { ... }
class User implements SetOwner { ... }
class School implements SetOwner { ... }
class Set {
SetOwner owner;
}
Przypomnij swoje komentarze:
Niech tabela SetOwners generuje wartości id. Musisz wstawić do SetOwners, zanim będziesz mógł wstawić do Użytkowników lub Szkół. Dlatego nie umieszczaj identyfikatora w Użytkownicy i Szkoły nie autoinkrementacja; po prostu użyj wartości wygenerowanej przez SetOwners:
INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');
W ten sposób żadna podana wartość identyfikatora nie będzie używana zarówno dla szkoły, jak i użytkownika.
Z pewnością możesz to zrobić. W rzeczywistości mogą istnieć inne kolumny, które są wspólne zarówno dla użytkowników, jak i szkół, i można je umieścić w supertabeli SetOwners. To trafia do Dziedziczenia tabeli klas Martina Fowlera wzór.
Musisz dołączyć. Jeśli wysyłasz zapytania z danego Zestawu i wiesz, że należy on do użytkownika (nie do szkoły), możesz pominąć dołączanie do SetOwners i dołączyć bezpośrednio do Użytkowników. Połączenia niekoniecznie muszą odbywać się za pomocą kluczy obcych.
SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ...
Jeśli nie wiesz, czy dany zestaw należy do Użytkownika, czy do Szkoły, musisz wykonać zewnętrzne połączenie do obu:
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 ...
Wiesz, że SetOwner_id musi pasować do jednej lub drugiej tabeli, Użytkownicy lub Szkoły, ale nie do obu.