在设计表时,我养成了一个习惯,即有一个唯一的列,并将其作为主键。这可以通过三种方式实现,具体取决于需求:

自动递增的标识整数列。 唯一标识符(GUID) 短字符(x)或整数(或其他相对较小的数字类型)列,可作为行标识符列

数字3将用于相当小的查找,主要是读取表,这些表可能有一个唯一的静态长度字符串代码,或一个数值,如年或其他数字。

在大多数情况下,所有其他表都有一个自动递增的整数或唯一标识符主键。

问题:-)

我最近开始使用一些数据库,这些数据库没有一致的行标识符,而且主键目前聚集在各个列之间。一些例子:

datetime /字符 datetime /整数 datetime / varchar 字符/ nvarchar / nvarchar

这有有效的理由吗?我总是为这些情况定义一个标识符或唯一标识符列。

此外,还有许多根本没有主键的表。如果有的话,合理的理由是什么?

我试图理解为什么桌子被设计成这样,对我来说,它似乎是一个很大的混乱,但也许有很好的理由。

第三个问题在某种程度上帮助我解析答案:在使用多个列组成复合主键的情况下,与代理/人工键相比,这种方法是否有特定的优势?我主要考虑的是性能、维护、管理等方面。


我怀疑原始数据结构的设计者需要Steven A. Lowe的卷起报纸疗法。

顺便说一句,guid作为主键可能会影响性能。我不推荐。


表应该一直有一个主键。如果没有,它应该是一个自动递增字段。

有时人们会省略主键,因为他们要传输大量数据,这可能会减慢(取决于数据库)进程。但是,它应该加在它之后。

一些关于链接表的评论,这是正确的,这是一个例外,但是字段应该是FK以保持完整性,并且在某些情况下,如果链接中的重复没有被授权,这些字段也可以是主键…但是要保持简单的形式,因为异常在编程中经常出现,所以应该提供主键来保持数据的完整性。


如果有天然钥匙,通常是最好的。因此,如果datetime/char唯一地标识了行,并且这两部分对行都有意义,那就太好了。

如果只有datetime是有意义的,并且只是附加了char以使其唯一,那么您不妨使用一个identify字段。


您应该使用由多个字段组成的“复合”或“复合”主键。

这是一个完全可以接受的解决方案,点击这里了解更多信息:)


我也总是使用数字ID列。在oracle中,我使用数字(18,0)没有真正的原因高于数字(12,0)(或任何int而不是long),也许我只是不想担心在db中获得数十亿行!

我还包括了一个用于基本跟踪的已创建和修改的列(类型时间戳),在这里它似乎很有用。

我不介意在其他列的组合上设置唯一的约束,但我非常喜欢我的id、创建和修改的基线需求。


我们做了很多连接,复合主键已经成为性能的累赘。简单的int或long即使引入第二个候选键也可以解决许多问题,但是在一个字段上连接比在三个字段上连接要容易得多,也更容易理解。


我总是使用自动编号或标识字段。

我曾经为一个客户工作,他使用SSN作为主键,然后由于HIPAA法规被迫更改为“MemberID”,这在更新相关表中的外键时引起了大量问题。坚持一致的标识列标准帮助我在所有项目中避免了类似的问题。


从不同的字段中创建主键是没有问题的,这是一个自然键。

您可以使用Identity列(与候选字段上的唯一索引相关联)来创建代理键。

这是一个古老的讨论。在大多数情况下,我更喜欢代理键。

但是没有钥匙是没有理由的。

再保险:编辑

是的,关于这一点有很多争议:D

我没有看到任何明显的优势自然键,除了他们是自然的选择。你总是会用Name、SocialNumber或者类似的东西来思考,而不是idPerson。

代理键可以解决自然键存在的一些问题(例如传播更改)。

当你习惯了代理,它似乎更干净,更易于管理。

但最终,你会发现这只是一个品味或心态的问题。有些人用自然的钥匙能“更好地思考”,而其他人则不然。


自然键和人工键是数据库社区中的一种宗教争论——请参阅本文及其链接的其他文章。我既不赞成一直使用人工钥匙,也不赞成永远不使用。我会根据具体情况做出决定,例如:

美国各州:我会使用state_code(德克萨斯州的'TX'等),而不是德克萨斯州的state_id=1 员工:我通常会创建一个人工的employee_id,因为很难找到其他任何工作。SSN或同等的工作,但可能会有问题,如新加入谁还没有提供他/她的SSN。 员工薪资历史:(employee_id, start_date)。我不会创建一个人工的employee_salary_history_id。它能起到什么作用(除了“愚蠢的一致性”)

无论在哪里使用人工键,都应该始终在自然键上声明唯一的约束。例如,如果你必须使用state_id,但是你最好在state_code上声明一个唯一的约束,否则你最终肯定会得到:

state_id    state_code   state_name
137         TX           Texas
...         ...          ...
249         TX           Texas

所有表都应该有一个主键。否则,您所拥有的就是一个HEAP——在某些情况下,这可能就是您想要的(当数据通过服务代理复制到另一个数据库或表时,会产生大量插入负载)。

对于行数较少的查找表,可以使用3 CHAR代码作为主键,因为这比INT占用的空间更少,但性能差异可以忽略不计。除此之外,我总是使用INT,除非您有一个引用表,它可能有一个由相关表的外键组成的复合主键。


如果你真的想阅读关于这个古老争论的所有内容,可以在Stack Overflow上搜索“自然键”。你应该能拿到几页结果。


我遵循一些规则:

Primary keys should be as small as necessary. Prefer a numeric type because numeric types are stored in a much more compact format than character formats. This is because most primary keys will be foreign keys in another table as well as used in multiple indexes. The smaller your key, the smaller the index, the less pages in the cache you will use. Primary keys should never change. Updating a primary key should always be out of the question. This is because it is most likely to be used in multiple indexes and used as a foreign key. Updating a single primary key could cause of ripple effect of changes. Do NOT use "your problem primary key" as your logic model primary key. For example passport number, social security number, or employee contract number as these "natural keys" can change in real world situations. Make sure to add UNIQUE constraints for these where necessary to enforce consistency.

关于代理键和自然键,我参考了上面的规则。如果自然键很小并且永远不会改变,则可以将其用作主键。如果自然键很大或可能改变,我使用代理键。如果没有主键,我仍然会创建一个代理键,因为经验表明,您总是会向模式添加表,并希望在适当的位置放置一个主键。


对我来说,自然键和人工键的区别在于数据库中需要多少业务逻辑。社会安全号码(SSN)就是一个很好的例子。

“我的数据库中的每个客户都将而且必须有SSN。”搞定,把它设为主键,搞定。只要记住,当你的业务规则改变时,你就完蛋了。

我自己不喜欢自然键,因为我有改变业务规则的经验。但是如果您确定它不会改变,那么它可能会阻止一些关键的连接。


我寻找自然主键,并尽可能地使用它们。

如果找不到自然的键,我更喜欢GUID而不是INT++,因为SQL Server使用树,总是在树的末尾添加键是不好的。

在多对多耦合的表上,我使用外键的复合主键。

因为我很幸运使用SQL Server,我可以用分析器和查询分析器研究执行计划和统计数据,并很容易地发现我的键是如何执行的。


GUID可以用作主键,但是您需要创建正确类型的GUID,这样它才能正常运行。

您需要生成COMB guid。一篇关于它和性能统计的好文章是 guid作为主键的代价。

此外,在SQL中构建COMB guid的一些代码是在Uniqueidentifier vs identity(存档)中。


这只是对一些经常被忽视的东西的额外评论。有时不使用单个代理键作为主键对子表有好处。假设我们有一种设计,允许您在一个数据库中运行多个公司(可能是一个托管解决方案,或者其他什么)。

假设我们有这些表和列:

Company:
  CompanyId   (primary key)

CostCenter:
  CompanyId   (primary key, foreign key to Company)
  CostCentre  (primary key)

CostElement
  CompanyId   (primary key, foreign key to Company)
  CostElement (primary key)

Invoice:
  InvoiceId    (primary key)
  CompanyId    (primary key, in foreign key to CostCentre, in foreign key to CostElement)
  CostCentre   (in foreign key to CostCentre)
  CostElement  (in foreign key to CostElement)

以防最后一点说不通,发票。CompanyId是两个外键的一部分,一个指向CostCentre表,另一个指向CostElement表。主键是(InvoiceId, CompanyId)。

在这个模型中,不可能搞砸并引用来自一个公司的CostElement和来自另一个公司的CostCentre。如果在CostElement和CostCentre表上使用一个代理键作为主键,并且在Invoice表中没有外键关系,那么它就是。

搞砸的机会越少越好。


我将坦率地说明我对自然键的偏好——在可能的情况下使用它们,因为它们将使您的数据库管理工作更加容易。我在公司建立了一个标准,所有的表格都有以下列:

行ID (GUID) 创造者(字符串;有一个默认的当前用户名(SUSER_SNAME()在T-SQL)) 创建(DateTime) 时间戳

行ID在每个表上都有一个唯一的键,并且在任何情况下都是每行自动生成的(并且权限阻止任何人编辑它),并且合理地保证在所有表和数据库中是唯一的。如果任何ORM系统都需要一个ID密钥,那么可以使用这个密钥。

同时,如果可能的话,实际的PK是一个自然的关键。我的内部规则是这样的:

人-使用代理键,例如INT。如果它是内部的,那么Active Directory用户GUID是一个可以接受的选择 查找表(例如StatusCodes) -使用短的CHAR代码;它比int更容易记住,在许多情况下,纸张表格和用户也会使用它来简洁(例如,Status = "E"表示"过期","A"表示"已批准","NADIS"表示"样品中未检测到石棉")。 链接表- fk的组合(例如EventId, AttendeeId)

因此,理想情况下,您最终会得到一个自然的、人类可读的和难忘的PK,以及一个orm友好的每个表一个id的GUID。

警告:我维护的数据库倾向于100,000条记录,而不是数百万或数十亿条记录,所以如果您有使用大型系统的经验,这不利于我的建议,请随意忽略我!


我避免使用自然键的原因很简单——人为错误。虽然通常可以使用自然的唯一标识符(SSN、VIN、Account Number等),但它们需要人工正确输入。如果您使用ssn作为主键,有人在数据输入期间调换了几个数字,并且没有立即发现错误,那么您将面临更改主键的问题。

我的主键都是由数据库程序在后台处理的,用户永远不会知道它们。


主键有什么特别之处?

模式中表的用途是什么?表中键的作用是什么?主键有什么特别之处?围绕主键的讨论似乎忽略了一点,即主键是表的一部分,而表是模式的一部分。对表和表关系最有利的应该驱动所使用的键。

Tables (and table relationships) contain facts about information you wish to record. These facts should be self-contained, meaningful, easily understood, and non-contradictory. From a design perspective, other tables added or removed from a schema should not impact on the table in question. There must be a purpose for storing the data related only to the information itself. Understanding what is stored in a table should not require undergoing a scientific research project. No fact stored for the same purpose should be stored more than once. Keys are a whole or part of the information being recorded which is unique, and the primary key is the specially designated key that is to be the primary access point to the table (i.e. it should be chosen for data consistency and usage, not just insert performance).

ASIDE: The unfortunately side effect of most databases being designed and developed by application programmers (which I am sometimes) is that what is best for the application or application framework often drives the primary key choice for tables. This leads to integer and GUID keys (as these are simple to use for application frameworks) and monolithic table designs (as these reduce the number of application framework objects needed to represent the data in memory). These application driven database design decisions lead to significant data consistency problems when used at scale. Application frameworks designed in this manner naturally lead to table at a time designs. “Partial records” are created in tables and data filled in over time. Multi-table interaction is avoided or when used causes inconsistent data when the application functions improperly. These designs lead to data that is meaningless (or difficult to understand), data spread over tables (you have to look at other tables to make sense of the current table), and duplicated data.

It was said that primary keys should be as small as necessary. I would says that keys should be only as large as necessary. Randomly adding meaningless fields to a table should be avoided. It is even worse to make a key out of a randomly added meaningless field, especially when it destroys the join dependency from another table to the non-primary key. This is only reasonable if there are no good candidate keys in the table, but this occurrence is surely a sign of a poor schema design if used for all tables.

It was also said that primary keys should never change as updating a primary key should always be out of the question. But update is the same as delete followed by insert. By this logic, you should never delete a record from a table with one key and then add another record with a second key. Adding the surrogate primary key does not remove the fact that the other key in the table exists. Updating a non-primary key of a table can destroy the meaning of the data if other tables have a dependency on that meaning through a surrogate key (e.g. a status table with a surrogate key having the status description changed from ‘Processed’ to ‘Cancelled’ would definitely corrupt the data). What should always be out of the question is destroying data meaning.

Having said this, I am grateful for the many poorly designed databases that exist in businesses today (meaningless-surrogate-keyed-data-corrupted-1NF behemoths), because that means there is an endless amount of work for people that understand proper database design. But on the sad side, it does sometimes make me feel like Sisyphus, but I bet he had one heck of a 401k (before the crash). Stay away from blogs and websites for important database design questions. If you are designing databases, look up CJ Date. You can also reference Celko for SQL Server, but only if you hold your nose first. On the Oracle side, reference Tom Kyte.


除了这些好答案,我想分享一篇我刚读过的好文章,伟大的主键辩论。

这里引用几点:

开发人员在为每个表选择主键时必须应用一些规则:

主键必须唯一地标识每条记录。 记录的主键值不能为空。 在创建记录时,主键-值必须存在。 主键必须保持稳定—您不能更改主键字段。 主键必须紧凑且包含尽可能少的属性。 主键值不能更改。

自然键(倾向于)打破规则。代理键符合规则。(你最好通读那篇文章,它值得你花时间!)


以下是我拥有25年以上开发经验后得出的经验法则。

所有表都应该有一个单列主键auto 增量。 将它包含在任何意味着可更新的视图中 主键在应用程序上下文中不应该有任何意义。这意味着它不应该是SKU、帐号、员工id或对应用程序有意义的任何其他信息。它只是一个与实体相关联的唯一键。

主键由数据库用于优化目的,应用程序除了用于标识特定实体或与特定实体相关外,不应该使用主键。

始终使用单一值主键使得执行upsert非常简单。

Favor multiple indices on single columns over multi-column indices. For example, if you have a two column key, favor creating an index on each column over creating a two column index. If we create a multi-column key on firstname + lastname, we can't do indexed lookups on lastname without providing a firstname as well. Having indices on both columns allows the optimizer to perform indexed lookups on either or both columns regardless of how they are expressed in your WHERE clause. If your tables are massive, explore partitioning the table into segments based on the most prominent search criteria. If you have a table that has a significant number of Id fields in it, consider removing all except the primary key to a single table which has an id (PK), an org_id (FK to original table) and an id_type column. Create indices for all columns on the new table and relate it to the original table. In this manner, you can now perform indexed lookups of any number of ids using only a single index.