Настоящият ви дизайн се нарича изключителни дъги където sets
таблицата има два външни ключа и трябва точно един от тях да е ненулев. Това е един от начините за прилагане на полиморфни асоциации, тъй като даден външен ключ може да препраща само към една целева таблица.
Друго решение е да се направи обща "супертаблица", която и двамата users
и schools
препратки и след това го използвайте като родител на 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
Можете да мислите за това като за аналог на интерфейс в OO моделиране:
interface SetOwner { ... }
class User implements SetOwner { ... }
class School implements SetOwner { ... }
class Set {
SetOwner owner;
}
Отново вашите коментари:
Нека таблицата SetOwners генерира стойности на идентификатори. Трябва да вмъкнете в SetOwners, преди да можете да вмъкнете в потребители или училища. Така че направете идентификаторите в Users and Schools не автоматично увеличаване; просто използвайте стойността, която е генерирана от SetOwners:
INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');
По този начин няма да се използва дадена стойност на идентификатор както за училище, така и за потребител.
Със сигурност можете да направите това. Всъщност може да има други колони, които са общи както за потребители, така и за училища, и можете да поставите тези колони в супертаблицата SetOwners. Това влиза в наследяване на таблица на класове на Мартин Фаулър модел.
Трябва да направите присъединяване. Ако правите заявка от даден набор и знаете, че принадлежи на потребител (не на училище), можете да пропуснете присъединяването към SetOwners и да се присъедините директно към потребители. Не е задължително присъединяванията да стават чрез външни ключове.
SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ...
Ако не знаете дали даден набор принадлежи на потребител или на училище, ще трябва да направите външно присъединяване към двете:
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 ...
Знаете, че SetOwner_id трябва да съвпада с една или друга таблица, потребители или училища, но не и двете.