Il tuo progetto attuale si chiama archi esclusivi dove il sets
table ha due chiavi esterne e ne necessita esattamente una per non essere nulla. Questo è un modo per implementare le associazioni polimorfiche, poiché una determinata chiave esterna può fare riferimento a una sola tabella di destinazione.
Un'altra soluzione è creare una "supertabella" comune che sia users
e schools
riferimenti, quindi usalo come genitore di 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
Puoi pensare a questo come a un'interfaccia nella modellazione OO:
interface SetOwner { ... }
class User implements SetOwner { ... }
class School implements SetOwner { ... }
class Set {
SetOwner owner;
}
Re i tuoi commenti:
Lascia che la tabella SetOwners generi valori id. Devi inserire in SetOwners prima di poterlo inserire in Utenti o Scuole. Quindi imposta gli ID in Utenti e Scuole non auto-incremento; usa semplicemente il valore che è stato generato da SetOwners:
INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');
In questo modo nessun valore id specificato verrà utilizzato sia per una scuola che per un utente.
Puoi certamente farlo. In effetti, potrebbero esserci altre colonne comuni sia agli utenti che alle scuole e potresti inserire queste colonne nella supertable SetOwners. Questo entra nella Ereditarietà della tabella delle classi di Martin Fowler modello.
Devi fare un join. Se stai eseguendo una query da un determinato Set e sai che appartiene a un utente (non a una scuola), puoi saltare l'accesso a SetOwners e unirti direttamente agli Utenti. I join non devono necessariamente passare da chiavi esterne.
SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ...
Se non sai se un determinato set appartiene a un Utente o a una Scuola, dovresti eseguire un'unione esterna per entrambi:
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 ...
Sai che SetOwner_id deve corrispondere all'una o all'altra tabella, Utenti o Scuole, ma non a entrambe.