Как спроектировать ограничение базы данных, чтобы две сущности могли иметь отношение «многие ко многим», только если в них совпадают два значения поля? - PullRequest
2 голосов
/ 08 июля 2011

У меня есть база данных с четырьмя таблицами:

Addressbook
--------------------
id
другие поля

Контакт
---------------------
ID
ID адресной книги
больше полей

* Группа 1016 *
---------------------
ID
ID адресной книги
другие поля

Группа для контакта
---------------------
Композитный ключ
Идентификатор группы
Контактный номер


Мои отношения - один ко многим для адресной книги> контакт, один ко многим для адресной книги> группа и многие ко многим между контактом и группами.

Итак, в общем, у меня есть адресная книга. Контакты и группы могут быть сохранены в нем, и они не могут быть сохранены в более чем одной адресной книге. Кроме того, столько нужных контактов можно добавить в любое количество групп.

Мой вопрос теперь выглядит следующим образом. Я хочу добавить ограничение, что контакт может быть членом группы, только если они оба имеют одинаковый идентификатор адресной книги .

Поскольку я не работаю в базе данных, это поражает мой мозг. Означает ли это, что я неправильно спроектировал структуру таблицы? Или это означает, что мне нужно добавить чек где-нибудь перед тем, как вставить в группу список контактов? Мне кажется, что это неправильно, потому что я бы хотел, чтобы в SQL-запросах было невозможно связать контакты с группами, если они не имеют одинаковый идентификатор.

Ответы [ 5 ]

2 голосов
/ 08 июля 2011

Этого можно добиться, добавив столбец addressbook_id в таблицу мостов Group to Contact , а затем используя составной внешний ключ для обоих Contacts и Группы таблиц.

В PostgreSQL (но легко адаптируется к любой БД или, по крайней мере, к любой БД, поддерживающей составные FK):

CREATE TABLE group_to_contact (
    contact_id          INT,
    group_id            INT,
    addressbook_id      INT,
    CONSTRAINT contact_fk FOREIGN KEY (contact_id,addressbook_id)
        REFERENCES contacts(id,addressbook_id),
    CONSTRAINT groups_fk FOREIGN KEY (group_id,addressbook_id)
        REFERENCES groups(id,addressbook_id)
)

Используяодин и тот же столбец addressbook_id в обоих ограничениях, вы, конечно, обязываетесь к тому, чтобы они были одинаковыми в обеих ссылочных таблицах.

2 голосов
/ 08 июля 2011

ОК - «Многие ко многим» управляется таблицей GroupToContact.

Таким образом, ограничения существуют между Group и GroupToContact и между Contact и GroupToContact (GTC)

А именно

 [Group].groupId = GTC.GroupId AND [Group].AddressBookid = GTC.AddressBookId

И

Contact.ContactId = GTC.ContactID AND Contact.AddressBookId = GTC.AddressBookId

Так что вам нужно будет добавить AddressBookId в таблицу GroupToContact

Еще одно замечание - вы не должны определять какие-либо отношения между Контактом и Группой напрямую - вместо этого вы просто определяете отношения OneToMany, которые каждое имеет с таблицей GroupToContact.

1 голос
/ 08 июля 2011

В вашей модели ER (Entity-Relationship) сущности Group и Contact являются (или должны быть) "зависимыми сущностями", то есть существованием Группа или Контакт основывается на 1 или более других объектах, в данном случае Адресная книга , что способствует идентификации объектазависимая сущность.Первичный ключ зависимого объекта является составным и включает в себя внешние ключи к объекту (ам), от которого он зависит.

Первичный ключ и Contact, и Group включает первичный ключ адресной книги, к которой они принадлежат,Как только вы это сделаете, все станет на свои места:

create table Address
(
  id int not null ,
  ... ,

  primary key (id) ,
)

create table Contact
(
  address_book_id int not null ,
  id              int not null ,
  ... ,

  primary key ( address_book_id , id ) ,
  foreign key ( address_book_id      ) references AddressBook ( id ) ,
)

create table Group
(
  address_book_id int not null ,
  id              int not null ,
  ... ,

  primary key ( address_book_id , id ) ,
  foreign key ( address_book_id      ) references AddressBook( id ) ,
)

create table GroupContact
(
  address_book_id int not null ,
  contact_id      int not null ,
  group_id        int not null ,

  primary key ( address_book_id , contact_id , group_id ) ,
  foreign key ( address_book_id , contact_id ) references Contact ( address_book_id , id ) ,
  foreign key ( address_book_id , group_id   ) references Group   ( address_book_id , id ) ,
)

Приветствия.

1 голос
/ 08 июля 2011

Как предложение BonyT:

  Addressbook    
---------------  
*id*  
...more fields  
PRIMARY KEY (id)      

  Contact    
-----------  
*id*  
addressbook_id   
...more fields  
PRIMARY KEY (id)  
FOREIGN KEY (addressbook_id)  
    REFERENCES Addressbook(id)

  Group  
---------
*id*  
addressbook_id  
...more fields  
PRIMARY KEY (id)  
FOREIGN KEY (addressbook_id)
    REFERENCES Addressbook(id)

  Group to Contact    
--------------------   
*group_id*  
*contact_id*  
addressbook_id  
PRIMARY KEY (group_id, contact_id)  
FOREIGN KEY (addressbook_id, contact_id)  
    REFERENCES Contact(addressbook, id)
FOREIGN KEY (addressbook_id, group_id)  
    REFERENCES Group(addressbook, id)
1 голос
/ 08 июля 2011

Поскольку ограничение CHECK не может включать подзапросы.Вы можете создать триггер, который проверяет, что группа и контакт имеют одинаковый адресной книги, и генерировать ошибку, если они этого не делают.

Хотя триггер базы данных, определенный для применения правила целостности, не проверяет данные, уже находящиеся в таблице, Я бы порекомендовал использовать триггер только в том случае, если правило целостности не может быть применено ограничением целостности.

CREATE TRIGGER tr_Group_to_Contact_InsertOrUpdate on Group_to_Contact
FOR INSERT, UPDATE AS
IF (SELECT Count(*) FROM inserted i 
 INNER JOIN Group g   ON i.groupid= g.groupid AND a.addressbookid=i.addressbookid
 INNER JOIN Address a ON a.addressbookid=I.addressbookid AND a.addressd=i.addressid) = 0
BEGIN
    RAISERROR('Address Book Mismatch', 16, 1)
    rollback tran
END

Примечание: (Это из памяти, поэтому, вероятно, синтаксически неверно)

...