MySQL: два отношения n: 1, но не оба одновременно - PullRequest
5 голосов
/ 30 июня 2010

Извините за заголовок, это сложно объяснить.

Мне нужна модель данных, подобная этой: alt text

Как видите, набор может принадлежать как пользователюили школа.Моя проблема: разрешается принадлежать только пользователю ИЛИ школе.Но никогда оба одновременно.

Как я могу решить эту проблему?

Ответы [ 3 ]

6 голосов
/ 30 июня 2010

Ваш текущий дизайн называется исключительные дуги , где таблица 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

Вы можете думать об этом как об интерфейсе в моделировании ОО:

interface SetOwner { ... }

class User implements SetOwner { ... }

class School implements SetOwner { ... }

class Set {
  SetOwner owner;
}

Ваши комментарии:

Итак, таблица SetOwner содержит и идентификаторы пользователей, и идентификаторы SchoolID, верно? Это означало бы, что у меня не будет одинакового идентификатора для пользователя и школы. Как я могу обеспечить это?

Пусть таблица SetOwners генерирует значения идентификаторов. Вы должны вставить в SetOwners, прежде чем вы сможете вставить в пользователей или школ. Поэтому сделайте идентификаторы в «Пользователи и школы» , а не с автоматическим увеличением; просто используйте значение, сгенерированное SetOwners:

INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');

Таким образом, никакое заданное значение идентификатора не будет использоваться ни для школы, ни для пользователя.

Если я хочу получить тип владельца для набора, нужен ли мне атрибут ownerType в моей таблице SetOwner?

Вы, конечно, можете это сделать. На самом деле, могут быть другие столбцы, которые являются общими как для пользователей, так и для школ, и вы можете поместить эти столбцы в суперпопулярные 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 должен соответствовать одной или другой таблице, Пользователи или Школы, но не обоим.

2 голосов
/ 30 июня 2010

С оговоркой: не совсем понятно, что такое набор в вашей модели данных.

Я подвергаю сомнению вашу модель данных.

Почему Пользователь -> Наборы и Школа -> Наборы должны указывать на одну и ту же таблицу. Это не более нормализовано, если это действительно независимые наборы данных. То, что они имеют некоторые общие черты в отслеживаемых столбцах, не означает, что они должны храниться в одной таблице. Они логически одно и то же?

Просто создайте отдельные таблицы для школьных наборов и пользовательских наборов. Это также упростит обратные запросы, поскольку вам не нужно проверять наличие нулевых отношений в таблице множеств, чтобы узнать, действительно ли это набор пользователей или SchoolSet.

1 голос
/ 30 июня 2010

Вам придется использовать триггер для принудительного применения этого бизнес-правила, поскольку MySQL имеет, но не применяет ограничения CHECK (на любом движке).

...