Dar*_*g8r 113 sql-server relational-database
我的数据库中有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
)
Run Code Online (Sandbox Code Playgroud)
用户属于多个组.这是通过多对多关系完成的,但在这种情况下无关紧要.票证可以由组或用户通过dbo.Ticket.Owner字段拥有.
什么是最正确的方式描述故障单和可选的用户或组之间的这种关系?
我想我应该在票证表中添加一个标志,说明拥有它的类型.
Nat*_*erl 139
您有几个选项,所有选项都有"正确性"和易用性.一如既往,正确的设计取决于您的需求.
您可以在Ticket,OwnedByUserId和OwnedByGroupId中创建两个列,并且每个表都有可为空的Fks.
您可以创建M:M参考表,同时启用ticket:user和ticket:组关系.也许将来您可能希望允许多个用户或组拥有单个票证?此设计不强制票证必须仅由单个实体拥有.
您可以为每个用户创建一个默认组,并使票证仅由真实组或用户的默认组拥有.
或者(我的选择)建模一个实体,它充当用户和组的基础,并拥有该实体拥有的票证.
下面是使用您发布的架构的粗略示例:
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 primary key,
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 primary key,
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 primary key,
[Owner] int NOT NULL references dbo.Party(PartyId),
[Subject] varchar(50) NULL
)
Run Code Online (Sandbox Code Playgroud)
And*_*y M 28
@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
)
);
Run Code Online (Sandbox Code Playgroud)
如您所见,该Ticket
表有两列,OwnerGroup
并且这两列OwnerUser
都是可以为空的外键.(其他两个表中的相应列相应地成为主键.)CK_Ticket_GroupUser
检查约束确保两个外键列中只有一个包含引用(另一个是NULL,这就是为什么两者都必须可为空).
(Ticket.ID
对于这个特定的实现,主键不是必需的,但在这样的表中有一个肯定不会有害.)
小智 5
另一种方法是创建一个关联表,其中包含每个潜在资源类型的列。在您的示例中,两个现有所有者类型中的每一个都有自己的表(这意味着您有一些可以引用的内容)。如果情况总是如此,你可以有这样的东西:
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_ID int NOT NULL,
Subject varchar(50) NULL
)
CREATE TABLE dbo.Owner
(
ID int NOT NULL,
User_ID int NULL,
Group_ID int NULL,
{{AdditionalEntity_ID}} int NOT NULL
)
Run Code Online (Sandbox Code Playgroud)
使用此解决方案,您将在向数据库添加新实体时继续添加新列,并且删除并重新创建@Nathan Skerl 显示的外键约束模式。该解决方案与@Nathan Skerl 非常相似,但看起来不同(取决于偏好)。
如果您不打算为每个新所有者类型创建一个新表,那么最好为每个潜在所有者包含一个owner_type而不是外键列:
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_ID int NOT NULL,
Owner_Type string NOT NULL, -- In our example, this would be "User" or "Group"
Subject varchar(50) NULL
)
Run Code Online (Sandbox Code Playgroud)
通过上述方法,您可以添加任意数量的所有者类型。Owner_ID 没有外键约束,但将用作对其他表的引用。缺点是您必须查看表才能了解所有者键入的内容,因为根据架构,它并不是立即显而易见的。仅当您事先不知道所有者类型并且它们不会链接到其他表时,我才会建议这样做。如果您事先知道所有者类型,我会采用像@Nathan Skerl 这样的解决方案。
抱歉,如果我弄错了一些 SQL,我只是把它们放在一起。