2011-10-21 73 views
79

我的数据库中有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.Ticket.Owner字段拥有。

什么是最正确方式描述票证和可选的用户或组之间的这种关系?

我想我应该在票据表中添加一个标志,说明什么类型拥有它。

+0

在我看来,每票由一组拥有。只是用户是一个组。 @ nathan-skerl模型中有哪些选择4。如果您使用Guids作为键,整个事情也可以很好地工作 – GraemeMiller

回答

99

您有几个选项,所有选项都“正确”和易用性各不相同。一如既往,正确的设计取决于您的需求。

  • 你可以简单地在票务,OwnedByUserId和OwnedByGroupId创建两列,并有可空FKS每个表。

  • 您可以创建启用票证:用户和票证:组关系的M:M参考表。也许将来你会希望允许一张票被多个用户或组所拥有?这种设计不强制要求门票必须仅由单个实体拥有。

  • 您可以为每个用户创建一个默认组,并让票据由真实组或用户的默认组拥有。

  • 或(我的选择)模拟一个实体,该实体充当用户和组的基础,并且该实体拥有门票所有者。

继承人使用您的发布模式的粗略例如:

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

您的解决方案帮助了我很多,谢谢 – mxasim

+4

选项编号为4的+1选项。 – Catchops

+2

对用户/组工单的查询会是什么样子?谢谢。 – paulkon

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

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

我认为这将是最常用的方式来表示你想要的而不是使用一个标志。

24

@Nathan Skerl的列表中的第一个选项是什么一个项目,我曾经与其中之间建立类似的关系是合作开始实施三张桌子。 (其中一个引用了另外两个,一次一个)。

因此,引用表有两个外键列,并且它还有一个约束来保证只有一个表(不是两个,都不是两个)被引用由单排。

这里是当应用到你的表可能的样子:

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表有两列,OwnerGroupOwnerUser,这两者都是空的外键。 (其他两个表中的相应列相应地成为主键。)CK_Ticket_GroupUser检查约束确保两个外键列中只有一个包含引用(另一个为NULL,这就是为什么两者都必须为空)。

(上Ticket.ID主键是没有必要为这个特定的实现,但它绝对不会伤害有一个表中的这个样子。)

+0

这也是我们在我们的软件中所具有的,如果您尝试创建通用数据访问框架,我会避免这种情况。这种设计会增加应用层的复杂性。 –

+0

我真的很陌生,所以如果这是错误的,请纠正我的错误,但是当您非常自信地认为您只需要两张机票的所有者类型时,此设计似乎是一种使用方法。如果引入了第三个机票所有者类型,那么您必须在表格中添加第三个可为空的外键列。 – Shadoninja

+0

@Shadoninja:你没有错。事实上,我认为这是一个完全公平的方式。对于这种解决方案,我总体上可以接受,但在考虑选项时,我肯定不会首先考虑 - 正是因为你列出的原因。 –