多对多(连接)表中是否需要唯一ID列?


22

从EF开始一些项目,但是我对联接表和键等有一些疑问。可以说我有一个应用程序表和一个权限表。应用程序具有许多权限,每个许可权可以属于多个应用程序(多对多)。

现在,“应用程序”和“权限”表很简单:

Applications
--------------
PK  ApplicationID
    Name

Permissions
--------------
PK  PermissionID
    Name

但是做联接表的最佳方法是什么?我有两个选择:

ApplicationPermissions
-----------------------
PK  ApplicationPermissionID
CU  ApplicationID
CU  PermissionID

要么

ApplicationPermissions
-----------------------
CPK ApplicationID
CPK PermissionID

PK = Primary Key
CPK = Composite Primary Key
CU = Composite Unique Index

您曾经被一种方法折磨过吗?严格优先吗?我发现很多“差异”都会被我的存储库模式抽象化(例如,我几乎永远不会创建一个完整的权限对象并将其添加到应用程序中,而是通过ID或唯一名称或东西),但我想我正在寻找恐怖故事,一种或另一种方式。

Answers:


20

我相信您的意思是“连接”表,而不是“联接”表。

不需要联结表具有其自己的ID字段。您将无需加入或过滤此类ID。您将仅联接或过滤要映射的表的ID。联结表上的ID浪费了磁盘空间。

因此,“最佳”选项是避免使用ID。通常,联结表将具有2个覆盖索引。每个覆盖索引都使用映射的ID之一作为主要排序字段。

但是,“最好”的目标并不长远。具有冗余ID字段是一个非常小的问题。对于少量浪费的磁盘,您不会有任何恐怖故事。该ID不会“窃取”聚集的索引,因为无论如何您都不想聚集在映射的组合上。

如果您的框架希望所有表都具有一个ID,那么就去吧。如果您的团队的数据库标准规定所有表都必须具有ID,则应使用它。如果没有,那就避免它。


2
好吧,您已经说过,添加ID只是次要的让步,很容易被潜在的好处所克服,所以在我看来(鉴于在每个表中都有唯一的ID或多或少是大多数DBMS和ORM的最佳做法)您应该主张将ID作为“最佳”或“默认”选项,而不是不使用。
罗伯特·哈维

4
“您永远不需要加入或查询这样的ID”-在技术情况下说“从不”正在促使这种事情发生。话说,有是连接表时候,你会加入(是的,我听说它被称为“加入”表多了一个“结”表)尚未第四表,因为加入了实体实际上一个自己的业务对象。
Jesse C. Slicer 2013年

4
@RobertHarvey。对于实体,ID是一个好习惯。但是联结更多地是许多关系的实现细节,而不是一个实体。但是正如Jesse C.slider指出的那样,在某些情况下,可以将联结视为业务实体。
mike30 2013年

1
“浪费磁盘空间。” -我认为某些引擎(InnoDB?)会自己创建一个(内部)主键,如果您自己没有创建一个-那么您实际上可能不会因为没有一个而获得磁盘空间。
亚历克斯

@Alex。您在映射的ID上放置了一个复合PK。
mike30

11

多年来,我养成了给每个表“ TableName”一个自动生成的主键“ TableNameID”的习惯,没有任何例外,即使对于联结表也不例外。我可以说我从不后悔,因为在创建通用代码时,它使很多事情变得更容易,这些通用代码对“所有表”或“某些表”或“多个不同表的许多行”起作用。

例如,如果有人要求您在文件或内存中存储不同表的某些行(或对这些表的引用)(例如,出于日志记录目的),则在事先知道只需要存储一个表时非常方便表名和一个整数ID,而您不必处理任何“特殊情况”。

另一件事,当您从合并的PK开始时,您可能会在某些时候遇到合并的外键的需要(因为您可能会想在ApplicationPermissions表中添加FK引用)。然后,下一个要求可能是使该FK与其他属性或外键一起具有唯一性-这将导致总体上增加复杂性。当然,对于大多数现代数据库系统来说,这是不可能的,但是统一的解决方案通常会使程序员的工作变得更加轻松。

最后,类似SELECT ... FROM TABLE WHERE TableNameID IN (id1,id2,...)这样的语句可以将单列作为主键很好地工作,但是到目前为止,我还从未见过SQL方言可以通过组合键来完成。如果您事先知道您将永远不需要这样的查询,那很好,但是如果明天您收到了使用此类SQL最容易解决的要求,请不要感到惊讶。

当然,当您希望ApplicationPermissions表包含数亿行时,则应考虑避免使用诸如ApplicationPermissionsID


尽管我最终没有选择您的答案。我喜欢它的各个方面。感谢您的想法(支持)。
solidau 2013年

6

尽管Mike的回答很好,但这是我是否添加单独的ID字段的原因。

  1. 如果联结表/联结表包含 ID以外的字段,请考虑使用单独的ID字段。这往往表明它是一流的实体。

  2. 如果API或任何现有逻辑倾向于使用单个字段来检索/编辑实体,请考虑使用单独的ID字段。这可以帮助其他人在更大的项目中遵循您的代码。

  3. 如果没有特殊好处(KISS),请不要使用它。EF知道如何处理这种类型的表,并且当其他人试图理解这种类型的关系时,有时可能会错过复合唯一约束。另外,在规范化时,我尝试使用唯一定义元组的最小键。在第二个示例中,您实际上有2个单独的候选主键。


-5
table Person
   Id int identity(1,1) not null primary key
   ...other fields go here...
table Address
   Id int identity(1,1) not null primary key
   ...other fields go here...
table PersonAddress
   Id int identity(1,1) not null primary key
   PersonId int not null
   AddressId int not null

请记住,创建两个索引和外键PersonIdAddressId

无论别人认为是“更好”还是“应该”,这都是允许数据库正常运行的最简单,最简单的方法。


1
我认为这种方法的一个问题是架构允许两PersonAddress行具有相同的PersonIdAddressId值。
2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.