Sql 设计多个表之一的外键
我有三个表:Sql 设计多个表之一的外键,sql,sql-server,database,database-design,entity-framework-core,shared-primary-key,class-table-inheritance,Sql,Sql Server,Database,Database Design,Entity Framework Core,Shared Primary Key,Class Table Inheritance,我有三个表:客户、提供商和位置。我需要创建第四个名为Contacts的表 我希望有任意数量的联系人与客户、提供商和位置表中的任何行相关联,因此我最终得到了类似的结果 CREATE TABLE [dbo].[Contacts] ( [Id] INT IDENTITY (1, 1) NOT NULL, [CustomerId] INT NULL, [ProviderId] INT NULL,
客户
、提供商
和位置
。我需要创建第四个名为Contacts
的表
我希望有任意数量的联系人
与客户
、提供商
和位置
表中的任何行相关联,因此我最终得到了类似的结果
CREATE TABLE [dbo].[Contacts] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[CustomerId] INT NULL,
[ProviderId] INT NULL,
[LocationId] INT NULL,
[Name] NVARCHAR (80) NULL,
[Email] NVARCHAR (80) NULL,
[Phone] NVARCHAR (80) NULL,
[Title] NVARCHAR (80) NULL,
[Address] NVARCHAR (120) NULL,
);
我觉得这个不太优雅。除了有未使用的列之外,我可能还应该添加一个约束,以确保CustomerId
、ProviderId
和LocationId
中的一个不为NULL
另一种方法是创建多对多联接表。这将不需要任何未使用的列。但这似乎仍然是一种浪费,因为任何联系人都不会涉及多家公司
有人知道任何更巧妙的解决方案吗?另一种方法是恢复关系,并为联系人可能涉及的每个实体创建一个映射表,如:
CREATE TABLE [dbo].[Contacts] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (80) NULL,
[Email] NVARCHAR (80) NULL,
[Phone] NVARCHAR (80) NULL,
[Title] NVARCHAR (80) NULL,
[Address] NVARCHAR (120) NULL
);
CREATE TABLE [dbo].[ContactCustomers] (
[ContactId] INT NOT NULL REFERENCES Contacts([ContactId]),
[CustomerId] INT NOT NULL REFERENCES Customers([CustomerId]),
PRIMARY KEY([ContactId], [CustomerId])
);
CREATE TABLE [dbo].[ContactProviders] (
[ContactId] INT NOT NULL REFERENCES Contacts([ContactId]),
[ProviderId] INT NOT NULL REFERENCES Providers([ProviderId]),
PRIMARY KEY([ContactId], [ProviderId])
);
CREATE TABLE [dbo].[ContactLocations] (
[ContactId] INT NOT NULL REFERENCES Contacts([ContactId]),
[LocationId] INT NOT NULL REFERENCES Locations([LocationId]),
PRIMARY KEY([ContactId], [LocationId])
);
这在关系方面为您提供了充分的灵活性,同时保持
联系人表专注于其主要目的:存储与该实体相关的数据。除了@GMB的建议外,另一种选择是简单地拥有不同的联系人表。仅仅因为地点和客户都有联系人,而且他们最初具有相同的属性,并不意味着他们必须存储在同一个表中
如果您不打算查询所有联系人,那么像这样单独存储它们会更有效。三者的模式可能会随着时间的推移而发生分歧
如果您想在联系人类型上编写通用逻辑,它们甚至可以在应用程序中共享一个NotMapped的Contact超类
乙二醇
联系人是对客户、提供商和地点的概括。这里有一些技巧你可能会发现很有用。和。这似乎与我的第二个多对多联接表想法相同,尽管它实际上不是多对多关系。当然有可能,如果没有我所希望的那么高效和简洁的话。正如@DaleK所建议的,没有一个真正的灵巧的方法来解决这个问题。我决定采用这种方法,主要是因为通过约束更容易确保数据的有效性。我不认为这是基于观点的。问题基本上是“此方案的合理备选方案是什么?”,这是一个有效的技术问题。@DavidBrowne Microsoft但有多种备选方案,并且所有备选方案都要权衡,因此最终取决于设计决策。@JonathanWood网站就是这样工作的。。。为什么有一个选项可以关闭“基于意见的”。@philipxy:这并不是真正解决同一件事,尽管将PK作为FK的中间表的想法肯定是可以采取的一种方法。我同意这既有趣又有用。。。但是你已经有了两个答案(还有更多的可能性),这两个答案都完全解决了问题,那么你将如何选择一个答案呢?无论如何,我没有投票决定结束,我只是指出了可能性,因为我已经看到很多这样的问题结束了——因为没有一个答案。是的,这会奏效,而且在某些方面很简单。但就优雅或效率而言,它的得分并不高。只要你不需要查询所有类型的联系人,它是最有效的。GMB的解决方案需要一个额外的连接来获取联系人,以及contacts.Id上的主键,这两者都可以在这里省略,因为联系人的Id是集群主键的尾随键。每个实体的联系人将一起存储在聚集索引中,因此从CustomerContacts中选择*,其中CustomerId=@CustomerId
将花费4-5个逻辑IO。句号。@DavidBrowne微软:我同意你说的。但效率也可能与表示数据所需的表和列的数量有关。我不仅仅是在考虑性能。@JonathanWood从另一个角度来看,这是唯一一种使相应的联系人条目归其“家长”所有的设计,因此可以根据特定家长自由添加、删除或修改。也会随父项自动删除。在公认的方法中,联系人是单独的实体,可以在客户、提供商和地点之间共享。它们中没有一个“拥有”它,因此无法自动删除它,修改将影响其他人。我并不是说这是不好的,这只是一个不同于原来的行为(限制是只设置了一个FK)。@IvanStoev:是的,我可以看到。级联删除无法完全实现,孤立联系人肯定是可能的。
CREATE TABLE [dbo].[CustomerContacts] (
[CustomerId] INT not null references Customer on delete cascade,
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (80) NULL,
[Email] NVARCHAR (80) NULL,
[Phone] NVARCHAR (80) NULL,
[Title] NVARCHAR (80) NULL,
[Address] NVARCHAR (120) NULL,
constraint pk_CustomerContacts primary key (CustomerId,Id)
);
CREATE TABLE [dbo].[ProviderContacts] (
[ProviderId] INT not null references Provider on delete cascade,
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (80) NULL,
[Email] NVARCHAR (80) NULL,
[Phone] NVARCHAR (80) NULL,
[Title] NVARCHAR (80) NULL,
[Address] NVARCHAR (120) NULL,
constraint pk_ProviderContacts primary key (ProviderId,Id)
);
CREATE TABLE [dbo].[LocationContacts] (
[LocationId] INT not null references Location on delete cascade,
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (80) NULL,
[Email] NVARCHAR (80) NULL,
[Phone] NVARCHAR (80) NULL,
[Title] NVARCHAR (80) NULL,
[Address] NVARCHAR (120) NULL,
constraint pk_LocationContacts primary key (LocationId,Id)
);