Seu design atual é chamado de arcos exclusivos onde o
sets
table tem duas chaves estrangeiras e precisa que exatamente uma delas seja não nula. Essa é uma maneira de implementar associações polimórficas, pois uma determinada chave estrangeira pode referenciar apenas uma tabela de destino. Outra solução é fazer uma "supertabela" comum que ambos os
users
e schools
referências e, em seguida, use isso como pai 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
Você pode pensar nisso como análogo a uma interface na modelagem OO:
interface SetOwner { ... }
class User implements SetOwner { ... }
class School implements SetOwner { ... }
class Set {
SetOwner owner;
}
Re seus comentários:
Deixe a tabela SetOwners gerar valores de id. Você precisa inserir em SetOwners antes de poder inserir em Usuários ou Escolas. Portanto, faça com que os IDs em Usuários e Escolas não auto-incremento; basta usar o valor que foi gerado por SetOwners:
INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');
Dessa forma, nenhum valor de id fornecido será usado para uma escola e um usuário.
Você certamente pode fazer isso. Na verdade, pode haver outras colunas que sejam comuns a Usuários e Escolas, e você pode colocar essas colunas na supertabela SetOwners. Isso entra na herança de tabela de classe de Martin Fowler padronizar.
Você precisa fazer uma associação. Se você estiver consultando a partir de um determinado conjunto e souber que ele pertence a um usuário (não a uma escola), você pode pular ingressar em SetOwners e ingressar diretamente em Users. As junções não precisam necessariamente passar por chaves estrangeiras.
SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ...
Se você não sabe se um determinado conjunto pertence a um usuário ou uma escola, você teria que fazer uma junção externa para ambos:
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 ...
Você sabe que o SetOwner_id deve corresponder a uma ou outra tabela, Usuários ou Escolas, mas não a ambas.