Внешний ключ для нескольких таблиц



У меня есть 3 таблицы в моей базе данных.



CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)

CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)

CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner int NOT NULL,
Subject varchar(50) NULL
)


пользователи принадлежат к нескольким группам. Это делается через отношения "многие ко многим", но в данном случае не имеет значения. Билет может принадлежать либо группе, либо пользователю, через dbo.Билет.Поле Владелец.



что будет САМЫЙ ПРАВИЛЬНЫЙ как описать эту связь между билетом и необязательно пользователем или группой?



Я думаю, что я должен добавить флаг в таблице билета это говорит о том, какой тип владеет им.

712   3  

3 ответов:

У вас есть несколько вариантов, все они различаются по "правильности" и простоте использования. Как всегда, правильный дизайн зависит от ваших потребностей.

  • вы можете просто создать два столбца в Ticket, OwnedByUserId и OwnedByGroupId, и иметь nullable Fks для каждой таблицы.

  • вы можете создать справочные таблицы M:M, включающие отношения ticket:user и ticket:group. Возможно, в будущем вы захотите, чтобы один билет принадлежал нескольким пользователям или группы? Этот дизайн не требует, чтобы билет должны принадлежать только одному лицу.

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

  • или (мой выбор) моделируйте сущность, которая действует как база для пользователей и групп, и имеет владельца билетов этой сущностью.

вот пример грубого использования вашего опубликованного схема:

create table dbo.PartyType
(   
    PartyTypeId tinyint primary key,
    PartyTypeName varchar(10)
)

insert into dbo.PartyType
    values(1, 'User'), (2, 'Group');


create table dbo.Party
(
    PartyId int identity(1,1) primary key,
    PartyTypeid tinyint references dbo.PartyType(PartyTypeId),
    unique (PartyId, PartyTypeId)
)

CREATE TABLE dbo.[Group]
(
    ID int NOT NULL,
    Name varchar(50) NOT NULL,
    PartyTypeId as cast(2 as tinyint) persisted,
    foreign key (ID, PartyTypeId) references Party(PartyId, PartyTypeID)
)  

CREATE TABLE dbo.[User]
(
    ID int NOT NULL,
    Name varchar(50) NOT NULL,
    PartyTypeId as cast(1 as tinyint) persisted,
    foreign key (ID, PartyTypeId) references Party(PartyID, PartyTypeID)
)

CREATE TABLE dbo.Ticket
(
    ID int NOT NULL,
    [Owner] int NOT NULL references dbo.Party(PartyId),
    [Subject] varchar(50) NULL
)

первый вариант @Nathan Skerl's list-это то, что было реализовано в проекте, с которым я когда-то работал, где аналогичная связь была установлена между тремя таблицами. (Один из них ссылался на двух других, по одному за раз.)

таким образом, ссылочная таблица имела два столбца внешнего ключа, а также имела ограничение, гарантирующее, что точно одна таблица (не обе, не ни одна) ссылается на одну строку.

вот как это может выглядеть при нанесении на таблицы:

CREATE TABLE dbo.[Group]
(
    ID int NOT NULL CONSTRAINT PK_Group PRIMARY KEY,
    Name varchar(50) NOT NULL
);

CREATE TABLE dbo.[User]
(
    ID int NOT NULL CONSTRAINT PK_User PRIMARY KEY,
    Name varchar(50) NOT NULL
);

CREATE TABLE dbo.Ticket
(
    ID int NOT NULL CONSTRAINT PK_Ticket PRIMARY KEY,
    OwnerGroup int NULL
      CONSTRAINT FK_Ticket_Group FOREIGN KEY REFERENCES dbo.[Group] (ID),
    OwnerUser int NULL
      CONSTRAINT FK_Ticket_User  FOREIGN KEY REFERENCES dbo.[User]  (ID),
    Subject varchar(50) NULL,
    CONSTRAINT CK_Ticket_GroupUser CHECK (
      CASE WHEN OwnerGroup IS NULL THEN 0 ELSE 1 END +
      CASE WHEN OwnerUser  IS NULL THEN 0 ELSE 1 END = 1
    )
);

как вы можете видеть,Ticket таблица имеет две колонки OwnerGroup и OwnerUser, оба из которых являются внешними ключами от null. (Соответствующие столбцы в двух других таблицах являются первичными ключами соответственно.) В CK_Ticket_GroupUser check constraint гарантирует, что только один из двух столбцов внешнего ключа содержит ссылку (другой-NULL, поэтому оба должны быть nullable).

(первичный ключ на Ticket.ID не надо для этой конкретной реализации, но это определенно не повредит иметь его в таком столе, как этот.)

CREATE TABLE dbo.OwnerType
(
    ID int NOT NULL,
    Name varchar(50) NULL
)

insert into OwnerType (Name) values ('User');
insert into OwnerType (Name) values ('Group');

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

Comments

    Ничего не найдено.