每个表都应该有一个主键吗?

Posted

技术标签:

【中文标题】每个表都应该有一个主键吗?【英文标题】:Should each and every table have a primary key? 【发布时间】:2010-10-24 19:40:34 【问题描述】:

我正在创建一个数据库表,但没有为其分配逻辑主键。

【问题讨论】:

您能否提供有关该表的更多详细信息?不过,答案可能是“是”。 是的,每个表都应该有主键。 【参考方案1】:

简短回答:

长答案:

您需要您的表可以加入某事 如果您希望对表进行聚类,则需要某种主键。 如果您的表设计不需要主键,请重新考虑您的设计:很可能,您遗漏了一些东西。为什么要保留相同的记录?

mysql 中,如果您没有明确指定主键,InnoDB 存储引擎总是会创建一个主键,从而产生一个您无权访问的额外列。

请注意,主键可以是复合的。

如果您有一个多对多链接表,您可以在链接中涉及的所有字段上创建主键。因此,您可以确保没有两条或多条描述一个链接的记录。

除了逻辑一致性问题之外,大多数 RDBMS 引擎都会受益于将这些字段包含在唯一索引中。

并且由于任何主键都涉及创建唯一索引,因此您应该声明它并获得逻辑一致性和性能。

请参阅我的博客中的这篇文章,了解为什么应该始终为唯一数据创建唯一索引:

Making an index UNIQUE

P.S.有些非常非常的特殊情况不需要主键。

出于性能原因,它们大多包含没有任何索引的日志表。

【讨论】:

@annakata:他们应该有一个复合主键 “而且由于任何 PRIMARY KEY 都涉及创建唯一索引”对于 Oracle 来说是不正确的。可以使用非唯一索引来强制执行主键。事实上,有时要求唯一和 PK 约束使用非唯一索引。 只是对“为什么要保留相同的记录?”这个反问句发表评论。请注意,仅添加 PK 并不能确保没有重复。通常 PK 对用户是不可见的,因此重要的是在可见字段中,其中可能包含重复数据。根据您的设计,这可能是可取的,也可能不是。 键与可连接性无关。集群参数取决于您使用的 DBMS,并混合了逻辑和物理考虑。 @JonHeggland 是对的,缺少键并不会阻止您加入表(当然,通常希望有某种索引可以加快加入速度)。【参考方案2】:

最好有一个主键。这样它就可以满足first normal form 并允许您继续沿着database normalization 路径前进。

正如其他人所说,没有主键有一些原因,但如果有主键,大多数都不会受到伤害

【讨论】:

@PaulSuart 数据不必总是采用正常形式。事实上,当数据变得庞大时,它不应该保持其正常形式,否则对于执行表连接等查询的访问数据将非常缓慢。正常形式是一种“理想化”并且实际上只有在数据预计不会增长时才有可能巨大的。【参考方案3】:

不同意建议的答案。简短的回答是:

主键的目的是唯一标识表上的一行,以便与另一个表形成关系。传统上,自动递增的整数值用于此目的,但也有一些变化。

但在某些情况下,例如记录时间序列数据,根本不需要此类键的存在,只会占用内存。使一行独一无二只是......不是必需的!

一个小例子: 表 A:日志数据

Columns:  DateAndTime, UserId, AttribA, AttribB, AttribC etc...

不需要主键。

表 B:用户

Columns: Id, FirstName, LastName etc. 

需要主键 (Id) 才能用作 LogData 表的“外键”。

【讨论】:

【参考方案4】:

除了极少数情况(可能是多对多关系表,或者你临时用来批量加载大量数据的表),我会这么说:

如果没有主键,就不是表!

马克

【讨论】:

严格来说这句话是错误的。表格可以是由您的查询语言创建的“查看表格”。 RDBMS 由关系而不是表组成。那句话应该说:“如果它没有主键,那就不是关系!”。 或者,“如果没有候选键,那么它就不是关系表”。但是请参阅非常罕见的情况,即可以有一个不代表关系的表。 为什么多对多表没有主键?您可以创建一个单独的主键,然后为外键的代理创建一个唯一索引。我认为最好在每个表上都有一个主键。即使在批量加载表上,您也可能希望单独标识不包含正在导入的数据的主键,因为它可以帮助您识别 ETL 过程中的重复记录。在我看来,每个表仍然应该有一个主键,即使它是多一点存储。由视图创建的表是表的子集,而不是表本身。 在多对多关系表中,您可以创建一个由关系的两个 ID 组成的复合主键。【参考方案5】:

几乎任何时候我创建了一个没有主键的表,以为我不需要主键,我最终返回并添加了一个。现在,我什至使用自动生成的身份字段创建连接表,并将其用作主键。

【讨论】:

连接表是一个主键 - 一个复合键,由被连接的两条记录的 PK 组成。例如。创建表 PersonOrder (PersonId int, OrderId int, PRIMARY KEY(PersonId, OrderId))。 是的,但是如果链接表还有第三个属性,比如说“OrderDate”。您是否也将其添加到复合键中?恕我直言,否 - 因为它可以进一步简化并且不提供主键应具有的不可简化特征。【参考方案6】:

添加就行了,不添加会后悔的(选择、删除、链接等)

【讨论】:

【参考方案7】:

您是否需要将此表连接到其他表?您是否需要一种唯一标识记录的方法?如果答案是肯定的,您需要一个主键。假设您的数据类似于客户表,其中包含作为客户的人员的姓名。可能没有自然密钥,因为您需要地址、电子邮件、电话号码等来确定这个 Sally Smith 是否与那个 Sally Smith 不同,并且您将把该信息存储在相关表中,因为这个人可以有多个电话,addesses 、电子邮件等。假设 Sally Smith 与 John Jones 结婚并成为 Sally Jones。如果您的桌子上没有人造钥匙,那么当您更新名称时,您只需将 7 个 Sally Smiths 更改为 Sally Jones,即使其中只有一个结婚并更改了她的名字。当然,在这种情况下,如果没有人工钥匙,你怎么知道哪个 Sally Smith 住在芝加哥,哪个住在洛杉矶?

您说您没有自然键,因此您也没有任何字段组合可以使其唯一,这使得人工键至关重要。

我发现任何时候我没有自然密钥,人工密钥是维护数据完整性的绝对必要条件。如果您确实有一个自然键,则可以将其用作键字段。但就个人而言,除非自然键是一个字段,否则我仍然更喜欢人工键和自然键上的唯一索引。不放进去以后会后悔的。

【讨论】:

【参考方案8】:

在每张桌子上都有一个 PK 是一种很好的做法,但这不是必须的。您很可能需要一个唯一索引和/或聚集索引(是否为 PK),具体取决于您的需要。

查看联机丛书中的主键和聚集索引部分(适用于 SQL Server)

"PRIMARY KEY 约束标识具有唯一标识表中行的值的列或列集。表中的任何两行不能具有相同的主键值。您不能为任何列输入 NULL在主键中。我们建议使用小的整数列作为主键。每个表都应该有一个主键。符合主键值的列或列组合称为候选键。"

但也请检查一下:http://www.aisintl.com/case/primary_and_foreign_key.html

【讨论】:

也检查一下,sql-server-performance.com/2006/primary-key-index-clustered 该页面非常愚蠢。首先,出于性能原因需要主键。通过阅读他的页面,我了解到向书表添加 ID 是没有用的,因为书的文本是独一无二的;显然,这家伙从未使用过数据库。但他在理解他所批评的内容方面也存在问题。页面写道 1)一个 PK 值引用一行 2)您可以通过任何一组列连接 2 个表。没有矛盾。令人惊讶的是,学术文章作者不了解关系理论的基本原理。 “首先,出于性能原因需要一个主键”这是不正确的,PK 不会直接影响性能。没有 PK 可能会导致许多问题(识别一行、加入等),但性能不是其中之一。当您在表上创建 PK 时,SQL 服务器会创建一个唯一聚集索引,该索引会影响性能而不是 PK 本身。作为一个真实的例子,我的表在日期列上有一个聚集索引,在 GUID 字段上有一个 PK,因为我的行应该在表中的日期列上进行物理排序,因为所有查询都有一个日期范围(在我的情况下)。跨度> 聚集索引是主键的一种形式,由 SQL Server 和其他几个 DBMS 创建。您确定使用它是个好主意吗?例如,在 MySQL 中,它不是出于几个未记录的原因。 请记住,在 InnoDB 中,GUID 不是 PK 的最佳类型。所有索引都包含对 PK 的引用,因此 PK 越大,所有其他索引也越大。【参考方案9】:

为了让它成为未来的证明,你真的应该这样做。如果你想复制它,你需要一个。如果你想把它加入另一张桌子,你的生活(以及明年必须维护它的可怜傻瓜的生活)会容易得多。

【讨论】:

我不相信这是必要的,但是“这样做是因为否则以后有人将不得不处理后果”足以让我在这样做时犯错。如果看起来值得尝试缩小它,我总是可以稍后删除该列......【参考方案10】:

我知道,为了在 .NET 中使用 gridview 的某些功能,您需要一个主键,以便 gridview 知道哪一行需要更新/删除。一般的做法应该是有一个主键或主键簇。我个人更喜欢前者。

【讨论】:

【参考方案11】:

我的职责是维护离岸开发团队创建的应用程序。现在我在应用程序中遇到了各种问题,因为原始数据库模式在某些表上不包含 PRIMARY KEYS。所以请不要因为你糟糕的设计而让其他人受苦。在表上设置主键始终是个好主意。

【讨论】:

【参考方案12】:

聚会迟到了,但我想加两分钱:

每个表都应该有一个主键吗?

如果您在谈论“关系 Albegra”,答案是。以这种方式对数据建模需要实体和表具有主键。关系代数的问题(除了它有 20 种不同的、不匹配的风格之外),它只存在于纸面上。您无法使用关系代数构建现实世界的应用程序。

现在,如果您谈论的是来自真实世界应用程序的数据库,它们部分/大部分都遵循关系代数,充分利用它并忽略它的其他部分。此外,现在数据库引擎提供了大量的非关系功能(现在是 2020 年)。所以在这种情况下,答案是。无论如何,我 99.9% 的真实世界表都有主键,但也有合理的例外。恰当的例子:事件/日志表(多个索引,但看不到一个键)。

归根结底,在遵循实体/关系模型的事务性应用程序中,几乎(如果不是)所有表都有主键是很有意义的。如果您决定跳过表的主键,请确保您有充分的理由这样做,并准备为您的决定辩护。

【讨论】:

【参考方案13】:

我总是有一个主键,即使一开始我还没有考虑到它的目的。有几次我最终需要在一个没有 PK 的表中进行 PK,以后再放入它总是比较麻烦。我认为总是包含一个有更多好处。

【讨论】:

【参考方案14】:

如果您使用的是 Hibernate,则无法创建没有主键的实体。如果您使用的是使用普通 sql/ddl 脚本创建的现有数据库,并且没有添加主键,则此问题可能会产生问题

【讨论】:

【参考方案15】:

简而言之,没有。但是,您需要记住,某些客户端访问 CRUD 操作需要它。为了将来打样,我倾向于始终使用主键。

【讨论】:

【参考方案16】:

我想找一个像这样官方的东西 - 15.6.2.1 Clustered and Secondary Indexes - MySQL。

如果表没有 PRIMARY KEY 或合适的 UNIQUE 索引,InnoDB 会在包含行 ID 值的合成列上内部生成一个名为 GEN_CLUST_INDEX 的隐藏聚集索引。这些行按 InnoDB 分配给此类表中的行的 ID 排序。行 ID 是一个 6 字节的字段,随着新行的插入而单调增加。因此,按行 ID 排序的行在物理上是按插入顺序排列的。

那么,为什么不自己创建主键或类似的东西呢?此外,ORM 无法识别这个隐藏的 ID,这意味着您不能在代码中使用 ID。

【讨论】:

以上是关于每个表都应该有一个主键吗?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server CE 中的每个表都必须有一个主键吗?

多对多表应该有一个主键吗?

SQL中的每一张表都必须设有主键吗

SQL中的每一张表都必须设有主键吗

我应该为 Realm 中的每个实体定义主键吗?

一个表可以有多个主键吗?