How to I design a database constraint so two entities can only have a many to many relationship if two field values within them match?

风流意气都作罢 提交于 2019-12-08 00:21:25

问题


I have a database with four tables as follows:

Addressbook
--------------------
id
more fields

Contact
---------------------
id
addressbook id
more fields

Group
---------------------
id
addressbook id
more fields

Group to Contact
---------------------
Composite key
Group id
Contact id


My relationships are one to many for addressbook > contact, one to many for addressbook > group and many to many between contact and groups.

So in summary, I have an addressbook. Contacts and groups can be stored within it and they cannot be stored in more than one addressbook. Furthermore as many contacts that are needed can be added to as many groups as are needed.

My question now poses as follows. I wish to add the constraint that a contact can only be a member of a group if both of them have the same addressbook id.

As I am not a database person this is boggling my brain. Does this mean I have designed my table structure wrong? Or does this mean that I have to add a check somewhere before inserting into the group to contact table? This seems wrong to me because I would want it to be impossible for SQL queries to link contacts to groups if they do not have the same id.


回答1:


You should be able to accomplish this by adding a addressbook_id column to your Group to Contact bridge table, then using a compound foreign key to both the Contacts and Groups tables.

In PostgreSQL (but easily adaptable to any DB, or at least any DB that supports compound FKs):

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)
)

By using the same addressbook_id column in both constraints, you are of course enforcing that they are the same in both referenced tables.




回答2:


OK - the Many to Many is governed by the GroupToContact table.

So the constraints are between Group and GroupToContact and between Contact and GroupToContact (GTC)

Namely

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

And

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

So you will need to add AddressBookId to GroupToContact table

One further note - you should not define any relationship between Contact and Group directly - instead you just define the OneToMany relationships each has with the GroupToContact table.




回答3:


As BonyT suggestion:

  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)



回答4:


As A CHECK Constraint can't include sub-queries. You could create a trigger that checks that the group and contact have the same addressbookid and generate an error if they do not.

Although a database trigger defined to enforce an integrity rule does not check the data already in the table, I would recommended that you use a trigger only when the integrity rule cannot be enforced by an integrity constraint.

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

Note:(This is from memory so probably not syntactically correct)




回答5:


In your E-R (Entity-Relationship) model, the entities Group and Contact are (or should be) "dependent entities", which is to say that the existence of a Group or Contact is predicated upon that of 1 or more other entities, in this case AddressBook, that contributes to the identity of the dependent entity. The primary key of a dependent entity is composite and includes foreign keys to the entity(ies) upon which it is dependent.

The primary key of both Contact and Group include the primary key of the AddressBook to which they belong. Once you do that, everything falls into place:

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 ) ,
)

Cheers.



来源:https://stackoverflow.com/questions/6622262/how-to-i-design-a-database-constraint-so-two-entities-can-only-have-a-many-to-ma

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!