Внешний ключ для нескольких таблиц
У меня есть 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.Билет.Поле Владелец.
что будет САМЫЙ ПРАВИЛЬНЫЙ как описать эту связь между билетом и необязательно пользователем или группой?
Я думаю, что я должен добавить флаг в таблице билета это говорит о том, какой тип владеет им.
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_GroupUsercheck 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