你喜欢你的主键?

在我们团队的一次相当热烈的讨论中,我想到了大多数人喜欢的主键。 我们有以下几组 –

  1. Int / BigInt是自动增加的主键。
  2. 至less应该有3列组成主键。
  3. Id,GUID和人类可读的行标识符都应该被区别对待。

什么是最好的PK方法? 如果你能certificate你的观点,那将是非常棒的。 上面有没有更好的方法?

编辑:任何人都有一个简单的样本/algorithm来生成可伸缩的行的人类可读标识符?

如果您打算偶尔连接的应用程序在数据库之间进行任何同步,那么您应该使用GUID作为主键。 debugging是一种痛苦,所以除了这种情况之外,我倾向于坚持自动增量的整数。

自动增量整数应该是你的默认值, 使用它们应该是合理的。

我没有看到一个答案指出(我认为是)真正的基本点 – 即主键是保证你不会在同一个现实世界的实体中获得两个条目(如在数据库中build模)。 这个观察有助于确定什么是好的,什么是主键的不好的select。

例如,在(美国)州名和代码表中,名称或代码可以是主键 – 它们构成两个不同的候选键,并且其中的一个(通常是较短的代码)被选作首要的关键。 在函数依赖理论中(并且join依赖关系 – 从1NF到5NF),它是关键而不是主键的候选键。

举一个反例,人名通常是主键的一个不好的select。 有很多人以“John Smith”或其他相似的名字命名; 即使考虑到中间名(记住:不是每个人都有一个 – 例如,我没有),有很多的重复的余地。 因此,人们不使用名字作为主键。 他们发明了人造钥匙,例如社会安全号码(SSN)或雇员号码,并用它们来指定个人。

一个理想的主键是短暂的,独特的,令人难忘的,自然的。 在这些特征中,唯一性是强制性的; 其余的必须弯曲给现实世界的数据的限制。

因此,在确定给定表的主键时,必须查看该表所代表的内容。 表中的一组或多组列值唯一标识表中的每一行? 那些是候选键。 现在,如果每个候选键由4列或5列组成,那么你可能会认为这些键太笨重,无法做出一个好的主键(主要是因为缺点)。 在这种情况下,您可能会引入一个代理键 – 一个人工生成的数字。 代理键通常(但不总是)简单的32位整数就足够了。 然后,您将此代理键指定为主键。

但是,您仍然必须确保其他候选键(对于替代键也是候选键以及所选主键)都被保留为唯一标识符 – 通常通过对这些列集放置唯一的约束。

有时候,人们很难确定哪一行是唯一的,但是应该有这样的事情,因为简单地重复一条信息并不会使事情变得更为真实。 如果你不小心,并得到两个(或更多)行声称存储相同的信息,然后你需要更新的信息,有危险(尤其是如果你使用游标),你会更新一行而不是每行,所以行不同步,没有人知道哪一行包含正确的信息。

这在某些方面是相当强硬的观点。

在需要的时候使用GUID没有特别的问题,但是它们往往很大 (如16-64字节),而且经常使用它们。 通常一个完美的4字节值就足够了。 在4字节值就足够的情况下使用GUID会浪费磁盘空间,甚至会减慢对数据的索引访问,因为每个索引页面的值较less,所以索引将更深,需要读取更多的页面才能访问信息。

这只是一个宗教问题,因为人们寻求一个普遍正确的答案。 事实上,你的团队和这个SO线程performance出如此多的分歧应该是一个线索,有充分的理由使用你所描述的所有解决scheme,在不同的情况下。

  • 当表中没有其他属性或属性集合适合唯一标识行时,替代键是有用的。
  • 如果可能的话,自然键是优选的,以使表更易于阅读。 自然键也允许从属表中的外键包含实际值而不是代理ID。 例如,当你需要存储state (CA,TX,NY)时,你可以使用char(2)自然键而不是int。
  • 在适当的地方使用复合主键。 当存在完美的复合关键字时,不要不必要地添加“ id ”代理关键字(在多对多表格中尤其如此)。 每个表格中的三列键的使命是绝对的废话。
  • 当您需要保留多个站点的唯一性时,GUID是一种解决scheme。 如果您需要主键中的值是唯一的,但是没有sorting或连续,那么它们也很方便。
  • INT与BIGINT:一个表对于主键需要一个64位范围是不常见的,但是随着64位硬件的可用性的增加,它不应该成为一个负担,并且可以保证你不会溢出。 INT当然更小,所以如果空间有限,它可以带来一点优势。

我喜欢The Database Programmer博客作为这种信息的来源。

主键为3列? 我想说,列应该有业务规则要求适当的唯一约束,但我仍然有一个单独的代理键。 复合键意味着业务逻辑进入密钥。 如果逻辑改变了,你的整个架构就被搞砸了。

我喜欢我的独特。

我总是用代理键去。 代理键(通常是标识列,自动增量或GUID)是数据本身中不存在键的代理键。 另一方面,一个自然的关键是它本身唯一地识别该行。 据我所知,生活中几乎没有真正的自然钥匙。 在美国,甚至连SSN都不是一个天然的关键。 复合主键是一个等待发生的灾难。 你不能编辑任何数据(这是任何自然键,组合键的主要缺点),但更糟糕的是,使用组合键,现在你必须永久保存关键数据到每个相关的表。 什么巨大的浪费。

现在,为了select代理键,我坚持使用标识列(我主要在MS SQL Server中工作)。 GUID太大,Microsoftbuild议不要将它们用作PK。 如果你有多台服务器,你所要做的就是增加10或20,或者你认为你需要同步/扩展到的最大数量的服务器,并且在每个后续的服务器上为每个表添加种子,你永远不会有数据冲突。

当然,由于增量,我把身份列设置为BigInt(否则称为long [64位])。

做一点math运算,即使你增加了100,你的表格中仍然可以有92,233,720,368,547,758(> 92 quadrillion)的行。

我认为在“主要”键中使用“主要”这个词是真正意义上的误导。

首先,使用“键”是一个属性或一组属性的定义,该属性或属性集合在表中必须是唯一的,

那么,拥有任何密钥都会带来几个相互矛盾的目的。

  1. 将连接条件用作与此父表有关系的子表中的一个或多个logging。 (在这些子表中显式或隐式地定义一个外键)
  2. (相关)确保子logging必须在父标签中具有父logging; e(子表FK必须在父表中以Key存在)
  3. 增加需要快速查找表中特定logging/行的查询的性能。

  4. 通过防止代表相同逻辑实体的重复行被插入到表中来确保数据的一致性。 (这通常被称为“自然”键,应该由相对不变的表(实体)属性组成)。

很显然,任何无意义的非自然键(如GUID或自动生成的整数都完全不能满足#4。

但是,对于许多(大多数)表格,通常可以提供#4的完全自然的键通常由多个属性组成,并且过于宽泛或过于宽泛,因此将其用于#1,#2或#3的目的会导致不可接受的性能结果。

答案很简单。 同时使用。 对其他子表中的所有联接和FK使用一个简单的自动生成整数键,但是要确保每个需要数据一致性的表(很less有表都没有)有一个替代的自然唯一键,这将防止插入不一致的数据行。另外,如果你总是拥有这两者,那么所有反对使用自然键(如果它改变了呢?我必须改变它被引用为FK的每一个地方)的反对意见,因为你不使用它。 ..你只是在一个表中使用它,它是一个PK,以避免不一致的duplciate数据…

至于GUID,要非常小心地使用它们,因为在索引中可以使用索引可以索引碎片。 用于创build它们的最常用algorithm将guid的“随机”部分放在最重要的位位置上,这样就增加了对新增行添加常规索引碎片整理/重新索引的要求。

你永远不应该做的一件事是使用智能钥匙。 这是一个关键的地方,关于logging的信息编码在密钥本身,它最终会咬你。

我工作了一个地方,主键是帐户ID,这是一个字母和数字的组合。 我不记得任何细节,但是,例如,那些具有某种types的账户将在600个范围内,而另一个types的账户从400个开始。这很好,直到客户决定要求工作types。 或者改变了他们所做的工作types。

另一个地方,使用树中的位置作为logging的主键。 所以会有如下logging。

 Cat1.subcatA.record1 Cat1.subcatA.record2 Cat1.subcatB.record1 Cat2.subcatA.record1 

当然,顾客想要的第一件事就是在树上移动物品。 整套软件在发生之前就已经死亡了。

请,请,如果您正在编写我必须维护的代码,请不要使用智能密钥!

有点偏离主题,但我感到不得不与

如果您的主键是GUID, 请不要将其设置为聚簇索引 。 由于GUID是非顺序的,所以在几乎每一次插入过程中,数据都会重新排列在磁盘上。 (Yuck。)如果使用GUID作为主键,它们应该是非聚簇索引。

我是作为主键自动增量的粉丝。 我深深地知道这是一个cop-out,但它确实可以很容易地按照添加的顺序对数据进行sorting(ORDER BY ID DESC,f'r instance)。

3列对人类的parsing听起来非常苛刻。

这就是权衡 – 你需要多less关系能力,而不是让人们对这个问题的理解(相对于存储过程或编程接口)。

自动增量是为我们人类。 🙁

一般来说,这取决于。

我个人喜欢autoincrement整数。

但是,我可以告诉你的一件事就是永远不要把其他来源的数据当作你的钥匙。 我发誓,每当我做完了,它回来咬我。 那么,永远不会!

应该有至less3个组成主键的列。

我不明白这一点。

你是在谈论一个“自然的钥匙”,例如“出生的名字和date”? 如果一个自然键存在,那么自然键可能是理想的,但是大多数自然键的候选者不是唯一的(几个同名的人)或者不是恒定的(某人可以改变他们的名字)。

Int / BigInt是自动增加的主键。

我更喜欢Guid。 自动增量的一个潜在问题是数据库实例(例如“销售数据库”)指定的值(例如“订单ID”)…如果不能完全工作(而是开始需要复合键)您需要合并由多个数据库实例创build的数据(例如,各个销售办事处都有自己的数据库)。

自动增加列。 我能够使我的代码无缝地与SQL Server或Oracle一起工作,一个使用通过我的DAL使用序列的身份,我不能更快乐。 我同意,如果您正在进行复制或发送数据以便稍后进行处理,则GUID有时是必需的。

RE GUID的

注意这是否真的是一个真正的真正的大数据库,大量的负载和快速的访问。

在我上一份工作中,我们拥有1亿到5亿条logging的数据库,我们的数据库人员强烈反对GUID和一个适当大小的十进制数。 他们觉得(在Oracle下)stringGuid-vs-十进制值的内部存储空间的大小差异会在查找中产生非常显着的差异。 (更大的键=更深的树遍历)

GUID的随机性也显着降低了索引页面的填充因子 – 这大大增加了撕裂和磁盘I / O。

我一直使用一个代理键 – 一个自动增量整数,称为“ID”。 即使在另一种select是显而易见的时候,我也可以看到很多原因:

  • 一致性
  • 数据独立(唯一的,不被格式的改变所破坏)
  • 人类可读

…没有明智的理由不要:

  • 联接中的歧义? – 别名表是一个更好的做法,恕我直言
  • 最佳表格? – 每个条目删除一个字节是不成熟的优化,恕我直言
  • 每桌决定? – 不再一致
  • 缩放问题? – 呃? 为什么?
  • 分层数据结构? – 这是denormalising,整个宗教的其他主题。 只要说我在理论上是在几个情况下的粉丝,但从来没有在实践中:)

反对我没有想到或遇到的理智的原因总是欢迎…

这是一个经典的“视情况而定”。 每个项目都没有一个正确的答案。 我喜欢不同的情况。 这取决于我是否使用ORM以及它支持什么。 这取决于整体架构(分布式还是非分布式等)。 只要select一个你认为可以工作的选项,然后继续争取制表符和空格。

我倾向于使用选项#1或#3,具体取决于大小,连接的人数以及是否为多个数据库服务器情况。

选项#2对我来说没有多大意义。 如果三者中的任何一个不足以识别独特的logging,那么有可能(不经过额外的机器)两个logging在所有三列中显示相同的值。 如果你想强制三者的任何组合的唯一性,那么只需为它们添加一个索引。

我只使用自动增量int或GUID。 99%的时间我使用自动增量整数。 这正是我第一次学习数据库时所学的知识,从来没有遇到过不使用它的理由(尽pipe我知道为什么GUID会更好)。

我喜欢自动增量整数,因为它有助于可读性。 例如,我可以说“看一下logging129383”,这对于某个人来说很容易find。 用一个几乎不可能做的GUID。

通过一个基本的定义,答案是什么构成了一个好的主要关键,很大程度上留给了宗教,打破了房间的论点 如果你有什么东西,并且会一直映射到一个单独的行,那么它将作为一个主键正常工作。 过去这一点,还有其他的考虑:

  • 主键定义是不是太复杂? 是否避免为了遵循“最佳实践”而引入不必要的复杂性?
  • 是否有更好的可能的主键,将需要较less的数据库处理开销(即INTEGER与VARCHAR等)?
  • 我绝对肯定我的主键的唯一性和定义不变性不会改变吗?

这最后一个可能是吸引大多数人使用像GUIDs或自递增整数列的东西,因为依靠地址,电话号码,名字/姓氏等东西,只是不削减它。 对于我所能想到的唯一不变的是SSN,但是对那些永远都是独一无二的人来说,我甚至不能确定。

希望这有助于增加一些清晰度…

我接近主键(我觉得是最好的)的方式是避免有一个“默认”的方法。 这意味着,而不是只是一个自动递增的整数,并调用它的一天,我看着这个问题,并说:“有一列或一组将永远unqiue,不会改变? 如果答案是肯定的,那我就采取这种做法。

几乎总是整数。

除了更小/更快处理之外,他们还有其他很好的理由。 你宁愿写下“404040”还是“3463b5a2-a02b-4fd4-aa0f-1d3c0450026c”?

只有一点相关,但是最近我有一个小的分类表(主要是那些代表ENUM的分类表),我将把主键设为char(3)或char(4)。 然后我使这些主键代表查找值。

例如,我有一个内部销售代理的报价系统。 我们有“成本类别”,每个报价行项目分配… …所以我有一个名为“tCostCategories”types查找表,其中主键是“MTL”,“SVC”,“TRV”,“税” 'ODC'。 查找表中的其他列存储更多细节,例如代码的正常英语含义,“材料”,“服务”,“旅行”,“税”,“其他直接成本”等等。

这非常好,因为它不会使用比int更多的空间,并且在查看源数据时,不必链接查找表就知道该值是多less。 例如,一个报价行可能如下所示:

1部分号码$ 40 MTL
2 OtherPartNumber $ 29.99 SVC
3 PartNumber2 $ 150 TRV

使用int来表示类别,然后在所有行上连接1,2,3都是非常容易的 – 您的数据就在您的面前,而且性能不会受到任何影响(不是我“真的testing过了。)

至于真正的问题去…我喜欢RowGUID uniqueidentifiers。 我不是100%,但是并不是所有的行都有内部的RowGuid? 如果是这样,那么使用RowGuid实际上会占用更less的空间(或者其他任何事情)。我所知道的是,如果在GreatPlains中使用M $足够好,那么对我来说就足够了。 (我应该鸭?)

哦,我使用GUID的另一个原因 – 我使用分层数据结构。 也就是说,我有一个表“公司”和一个表“供应商”,主键匹配。 但是我也有一个“制造商”也是从公司inheritance的。 供应商和制造商通用的字段不会出现在这些表中 – 它们出现在公司中。 在这个设置中,使用int比Guids更痛苦。 至less,你不能使用身份主键。

只要我能信任它,我就喜欢自然的钥匙。 为了使用对主题专家有意义的密钥,我愿意付出一个小的性价比价格。

对于描述实体的表格,应该有一个简单的自然键,以与人们的主题相同的方式来标识个体实例。 如果主题没有可靠的标识符为其中一个实体,那么我会诉诸代理键。

对于描述关系的表,我使用了一个复合键,其中每个组件引用一个参与关系的实体,因此在实体表中是一行。 同样,使用复合密钥的性能受到的影响通常很小。

正如其他人所指出的,“主键”这个词有点误导人。 在关系数据模型中,使用的术语是“候选键”。 一个表可能有几个候选键。 从逻辑上讲,每一个都和另一个一样好。 select其中之一作为“主要”,并通过该关键字进行全部引用仅仅是devise师可以做出的select。

Guids.period.

In the event that you need to scale out or you need to assign the primary key by alternate means they will be your friend. You can add indexes for everything else.


update to clarify my statement.

I've worked on a lot of different kinds of sites. From small single server deals to large ones backed with multiple DB and web servers. There have certainly been apps that would have been just fine with auto incrementing ints as primary keys. However, those don't fit the model of how I do things.

When using a GUID you can generate the ID anywhere. It could be generated by a remote server, your web app, within the database itself or even within multiple databases in a multimaster situation.

On the other hand, an auto incremented INT can only be safely generated within the primary database. Again, this might be okay if you have an application that will be intimately tied to that one backing DB server and scaling out is not something you are concerned with.

Sure, usage of GUIDs mean you have to have nightly reindexing processes. However, if you are using anything other than an auto incremented INT you should do that anyway. Heck, even with an INT as the primary it's likely you have other indexes that need regenerated to deal with fragmentation. Therefore, using GUIDs doesn't exactly add another problem because those tasks need to be performed regardless.

If you take a look at the larger apps out there you will notice something important: they all use Base64 encoded GUIDs as the keys. The reason for this is simple, usage of GUIDs enables you to scale out easily whereas there can be a lot of hoops to jump through when attempting to scale out INTs.

Our latest app goes through a period of heavy inserts that lasts for about a month. After that 90+% of the queries are all selects for reporting. To increase capacity I can bring up additional DB servers during this large insert period; and later easily merge those into a single DB for reporting. Attempting to do that with INTs would be an absolute nightmare.

Quite frankly, any time you cluster a database or setup replication the DB server is going to demand that you have GUIDs on the table anyway. So, if you think that your system might need to grow then pick the one that's good.

This is a complex subject whether you realized it or not. Might fall under the section on this StackOverflow FAQ.

What kind of questions should I not ask here?

Avoid asking questions that are subjective, argumentative, or require extended discussion. This is a place for questions that can be answered!

This has been debated for years and will continue to be debated for years. The only hints of consensus I have seen is that the answers are somewhat predictable depending on if you are asking a OO guy (GUIDs are the only way to go!), a data modeler (Natural keys are the only way to go!), or a performance oriented DBA (INTs are the only way to go!).