在我的数据库中,我倾向于养成对我创建的每个表使用名称为id的自动递增整数主键的习惯,以便对任何特定行进行唯一查找。

是这算是个坏主意吗?这样做有什么弊端吗?有时我会有多个索引,例如id, profile_id, subscriptions,其中id是唯一标识符,profile_id链接到id表的外部Profile,等等。或者在某些情况下,您不想添加一个字段?

评论

看一下德国坦克问题,举一个简单的自动递增标识符是一个问题的例子。当然,这仅在您在公共场所使用ID时才重要。

@ArukaJ关键是它泄漏了有关系统的某些信息。例如,假设数据库包含用户撰写的帖子,每个帖子都有一个顺序ID。假设您发布了四个帖子,每个帖子都有一个ID:凌晨4点(20),凌晨5点(25),晚上8点(100)和晚上9点(200)。通过查看ID,您可以看到在凌晨4点到凌晨5点之间仅添加了5条帖子,而在晚上8点至晚上9点之间添加了100条帖子。如果您想抽出时间进行拒绝服务攻击,那可能是有价值的信息。

对于每个抱怨“德国坦克问题”的人。...如果阻止某人访问数据的唯一原因是您的URL中的键,那么您面临的问题比GUID和Auto INT更大。
@MatthewWhited不仅仅是交换URL中的参数。假设您使用一个站点,并且在时间t创建资产100,在时间t + 60创建资产120。如果您能以混淆的形式看到这两个ID(分别为100和120),那么现在您知道存在的资产总数,以及大致的创建速度。这是信息泄漏。这不是纯粹的假设。

“总是...是一种好习惯吗?” No.

#1 楼

保证唯一的行标识符绝对不是一个坏主意。我猜我不应该说永远不会-但在绝大多数情况下,让我们一起讨论它是个好主意。对于我来说,不使用它从来就不足为奇了。

评论


我就是做这个的。大多数人都使用“ id”或“ tablename_id”(例如user_id)。该参数通常不是是否需要该列,而是使用哪种命名方式。

–GrandmasterB
16年8月15日在20:50

我个人认为表名称应包含其余内容。 TableName.id与TableName.TableName_id相对,因为该ID还要引用什么?如果我在表中还有另一个id字段,那么如果它引用其他表,我将为其加上一个表名作为前缀

– AJJ
16年8月15日在20:51

@ArukaJ,您提到您正在使用SQLite。这实际上是一个特例,因为它总是使此类专栏“在幕后”。因此,您甚至不使用任何多余的空间,因为无论您是否想要,都可以得到一个空间。另外,SQLite的rowid始终是64位整数。如果我对它的理解是正确的,那么如果定义自动递增的行,它将是内部行ID的别名。因此,您可能总是会这样做!参见sqlite.org/autoinc.html

–GrandmasterB
16年8月15日在21:28



我可以想到的一个例外是,如果您有一个以其他方式生成的唯一标识符,在这种情况下,应将其作为主键,而自动递增的ID是多余的。

–HamHamJ
16年8月15日在21:39

@GrandmasterB:当前版本的SQLite允许创建WITHOUT ROWID表(使用显式PRIMARY KEY)作为优化。但是否则,INTEGER PRIMARY KEY列是rowid的别名。

– dan04
16年8月15日在23:29

#2 楼

TL; DR:如果您还没有识别每行的独特方法,请使用UUID代替自动递增。
我不同意之前的所有答案。在所有表中添加自动递增字段的原因很多,这有很多原因。如果您的表中没有明显的键,那么自动递增字段似乎是个好主意。毕竟,您不想select * from blog where body = '[10000 character string]'。您宁愿select * from blog where id = 42。我认为在大多数情况下,您真正​​想要的是唯一的标识符。不是顺序的唯一标识符。您可能想改用通用唯一标识符。
大多数数据库中都有函数来生成随机唯一标识符(mysql中为uuid,mssql中为postgres。newid)。这些使您可以随时将数据生成到不同计算机上的多个数据库中,并且它们之间没有网络连接,并且合并数据时冲突为零。这样一来,您可以更轻松地设置多个服务器甚至是数据中心,例如使用微服务。如果有https://example.com/user/1263,也可能还有https://example.com/user/1262。这可以允许在用户配置文件页面中自动进行安全利用。
在很多情况下,uuid列无用甚至有害。假设您有一个社交网络。有一个users表和一个friends表。朋友表包含两个用户ID列和一个自动递增字段。您希望35成为朋友,因此您将3,5插入数据库中。数据库将添加一个自动增量ID并存储1,3,5。以某种方式,用户3再次单击“添加朋友”按钮。您再次将3,5插入数据库中,数据库将添加一个自动增量ID并插入2,3,5。但是现在35彼此成为朋友了!那是在浪费空间,如果您考虑一下,自动增量列也是如此。您需要查看ab是不是朋友,只需选择带有这两个值的行即可。它们一起是唯一的行标识符。 (您可能希望编写一些逻辑以确保对3,55,3进行重复数据删除。)网址缩短器),您真正想使用的是随机生成的唯一ID。

评论


UUID的问题在于,它们占用大多数表的太多空间。为每个表使用正确的唯一标识符。

–斯蒂芬
16年8月16日在6:37

关于唯一性的整个段落都是没有意义的-可以使用或不使用主键来强制执行唯一性。此外,从理论上讲,UUID更好,但是在调试/执行DBA任务或进行其他任何“无法抵御攻击”的事情时,都很难使用UUID。

–user44761
16年8月16日在7:28



UUID更好的另一种情况是:实现幂等PUT操作,以便您可以安全地重试请求,而不会引入重复的行。

– yurez
16年8月16日在8:21

在“ URL猜测”点上,具有唯一的ID(顺序的ID或其他ID)并不意味着将该ID暴露给应用程序的用户。

–戴夫·谢罗曼(Dave Sherohman)
16年8月16日在12:16

纯粹从数据库的角度来看,这个答案是完全错误的。使用UUID而不是自动递增整数会使索引增长得太快,从而对性能和内存消耗产生不利影响。如果您是从Web服务或Web应用程序的角度进行交谈,则无论如何数据库和前端之间都应该有一层。别的都是不好的设计。使用数据作为主键甚至更糟。主键只能在数据层上使用,不能在其他地方使用。

–醉酒的代码猴子
16年8月19日在22:31



#3 楼

自动增量键具有大多数优点。业务规则。
当在两个数据库之间传输数据时,尤其是当数据位于多个表(即主表/明细表)中时,由于序列在数据库之间未同步,因此它不是直截了当的,因此您必须首先使用业务密钥作为匹配项来创建等效表,以了解源数据库中的哪个ID与目标数据库中的哪个ID相对应。但是,当从/向隔离表传输数据时,这应该不是问题。
许多企业具有临时的,图形的,点击式和拖放式报告工具。由于自动递增ID是没有意义的,因此这类用户将很难理解“应用”之外的数据。
如果您不小心修改了业务密钥,那么您将永远无法恢复该行,因为您不再有一些东西供人类识别。曾经在BitCoin平台上引起过故障。
当PK应该仅由两个外部ID组成时,一些设计者将ID添加到两个表之间的联接表中。显然,如果联接表在三个或更多表之间,则使用自动增量ID是有意义的,但是当将其应用于FK组合以执行业务规则时,则必须添加唯一键。

这里Wikipedia文章部分介绍了代理密钥的缺点。

评论


指责替代密钥上的mt.gox漏洞似乎很可疑。问题在于它们将所有字段都包括在其复合键中,甚至包括可变/易碎字段。

– CodesInChaos
16年8月16日在8:51

使用自动增量键的“社交”缺点是,有时“业务”会假定绝不存在任何差距和要求,以便知道插入失败(事务回滚)时丢失行发生了什么情况。

–瑞克·赖克(Rick Ryker)
16年8月17日在14:51

另一个缺点是,如果系统变得如此庞大以至于必须分片数据库,则无法再使用自动增量来生成全局唯一密钥。当您达到这一点时,您可能有很多依赖于该假设的代码。还有其他产生唯一标识符的方法,如果对数据库进行分片,该标识符将继续起作用。

–卡巴斯德
16年8月17日在20:37

@Voo无法保证您选择的数据库支持该功能。尝试实现比数据库本身更高的层意味着您将失去SQL会给您的某些保证。最后,如果您具有分布式系统,则ID的任何集中分配都会增加延迟。

–卡巴斯德
16年8月20日在0:11

@Voo当然,无论系统的规模如何,都不应对自动递增ID的性质做出太多假设。如果您只有一个数据库,则会按顺序分配它们,但不能保证按顺序提交它们。序列中可能会有缺口,因为并非所有事务都已提交。

–卡巴斯德
16年8月20日在0:22

#4 楼

恰恰相反,不,您不必总是拥有数字AutoInc PK。

如果仔细分析数据,通常会识别数据中的自然键。当数据对业务具有内在含义时,通常就是这种情况。有时,PK是来自古代系统的人工制品,业务用户将其用作第二语言来描述其系统的属性。例如,我已经看到车辆VIN号码用作车队管理系统中“车辆”表的主键。不要创建第二个毫无意义的主键;

有时您可以使用AutoInc PK来产生客户有意义的价值,例如政策编号。将起始值设置为合理的值,并应用有关前导零等的业务规则。这可能是“两全其美”的方法。

当少量相对静态的值时,请使用值对系统用户有意义。当您可以使用L,C,H(其中L,H和C代表保险“政策类型”上下文中的人寿,汽车和房屋)时,为什么要使用1,2,3,或者回到VIN示例,如何使用“ TO” “对于丰田?所有Toyata汽车的VIN都以“ TO”开头。这是用户要记住的一件事,使他们不太可能引入编程和用户错误,甚至可以作为管理报告中完整描述的有用替代方法,从而使报告更简单

对此的进一步发展可能是“一座桥梁太远了”,我一般不建议这样做,但为了完整起见我将其包括在内,您可能会发现很好用。即,使用描述作为主键。对于快速变化的数据,这是令人讨厌的。对于“一直”报告的非常静态的数据,也许不是。只需提及它,就可以坐在那里。

我确实使用AutoInc PK,我只是动脑筋,首先寻找更好的替代品。数据库设计艺术使有意义的事情可以快速查询。有太多的连接会阻止此情况。与汽车类似,一辆汽车有0..n个附件,每个附件都可以在许多汽车上找到。为此,您可以创建一个Car_Accessory表,其中包含Car和Accessory的PK以及有关链接日期等的其他相关信息。

(通常)不需要该表上的AutoInc PK -只能通过“告诉我这辆车上有什么附件”汽车或从附件“告诉我这辆车上有什么附件”来访问它

评论


>所有Toyata汽车的VIN都以“ TO”开头,这是不正确的。如果是日本制造,则以“ JT”开头。美国制造的丰田车具有完全不同的VINsen.wikibooks.org/wiki/…

– Monty Harder
16年8月16日15:37



不要创建第二个毫无意义的主键;这很浪费,可能会导致错误。但是,如果您为记录建立唯一性的方法是6列的组合,那么始终将所有6列连接起来很容易出错。数据自然具有PK,但是最好使用id列和这6列的唯一约束。

–布拉德
16年8月16日在17:51

我承认其中一些建议对我来说有点过头。是的,务实是可以的,但我无法数出某人多久发誓自己的长子一生中,某些属性不在领域中保持独特的地位。好吧,通常情况良好,直到上线后的第二个星期才出现,而第一个副本出现了。 ;)使用“描述”作为PK距离还很遥远。

– AnoE
16年8月16日在20:27



@Brad,我的回答中没有地方提倡使用六元素复合键。如果您有一个,则它可能是代理主键(并且应该被强制执行),但是您不会在链接到它的每个表中重击6个字段。我的信息是关于实用主义的,而不是盲目地做某事,因为象牙塔学者写的是一些理论性的东西。

– mcottle
16年8月17日在2:27

真正令人惊讶的是,几乎没有真正的“自然键”。 SSN的?不,他们可以改变。很少见,但有可能发生。用户名?不。最终,有人将有有效的商业理由进行更改。 VIN通常是教科书中的一个例子,但没有很多其他例子。给定街道名称,即使家庭住址也可以更改。

–埃里克·冯肯布施(Erik Funkenbusch)
16年8月17日在21:08

#5 楼

许多表已经具有自然的唯一ID。不要在这些表上添加另一个唯一的id列(自动递增或其他方式)。请使用自然的唯一ID。如果添加另一个唯一ID,则您的数据实际上就具有冗余(重复或依赖性)。这违反了规范化的原则。一个唯一的ID依赖于另一个ID以确保准确性。这意味着它们必须在管理这些行的每个系统中始终保持完全同步。这只是数据完整性中的另一个脆弱性,您实际上并不想长期进行管理和验证。唯一的ID列会给出(有时甚至会降低性能)。作为IT的一般规则,请避免像瘟疫一样造成冗余!建议您在任何地方抵抗它。这是恶心。并注意报价。一切都应该尽可能简单,但不要简单。即使自然的ID看起来不太整洁,也没有两个ID足以满足需要。

评论


如果绝对保证永不更改,则不应该只使用“自然” ID作为主键吗?例如,您不应该将驾驶执照号码用作主键,因为如果某人获得了新的驾驶执照,则不仅需要更新该表,还需要更新任何带有引用该表的外键的表!

– ekolis
16年8月19日在19:17

驾驶执照编号不符合自然唯一标识的原因有很多。首先,其中一些来自其他数据,例如出生日期和姓名。不能保证它们在各州之间是唯一的。以您的例子为例,当一个人重新签发具有相同编号但可能延长的有效期的许可证时,会发生什么?他们拥有具有相同编号的不同许可证。自然ID仍必须满足主键的基本属性。驾照号码(至少在美国)在这方面存在一些缺陷。

–布拉德·托马斯(Brad Thomas)
16年8月19日在19:59



好的,我想我当时误解了自然ID的定义。我认为这仅仅是业务规则定义的ID,实际上是否可以保证它是不可变的。

– ekolis
16年8月20日在13:06

#6 楼

在较大的系统上,ID是一致性增强器,请在几乎任何地方使用它。在这种情况下,不建议使用单独的主键,因为它们在底行很昂贵(请阅读原因)。

每个规则都有一个例外,因此您可能不需要在用于暂存表的整数自动增量ID导出/导入以及类似的单向表或临时表。您还希望在分布式系统上使用GUID代替ID。

这里的许多答案都建议应采用现有的唯一密钥。好吧,即使它有150个字符?我认为不是。在那里,他们可以为每个表提供单独的方法。

但是,如果您的ERP具有400多个表,那么在任何地方(上述情况除外)都具有整数自动增量ID是很有意义的。即使存在其他唯一字段并确保其唯一性,您也不必依赖它们。 br />在大多数情况下,您可以使用JOIN表,而无需检查键是什么。
您可以使通用代码例程与整数自动增量列配合使用。以前仅通过引用现有表的ID即可预见的插件。它们从一开始就已经存在,无需额外添加它们。

在大型系统上,有必要忽略这些主键的次要优点,并在大多数情况下始终使用整数自动增量ID。使用现有的唯一字段作为主键可能会为每条记录节省一些字节,但是在当今的数据库引擎中,额外的存储或索引时间不会造成任何问题。实际上,您浪费了更多的金钱和资源,浪费了开发人员/维护人员的时间。当今的软件应针对程序员的时间和精力进行优化-具有一致ID的哪种方法可以更好地实现。

评论


根据个人经验,我完全同意您的回答的后半部分。与快速而紧凑的索引相比,您将需要全局唯一键的次数要少得多,而且使用频率要少得多。如果确实需要一个,请创建一个具有自动生成的ID和UUID列的GlobalEntities表。然后,将ExGlobalEntityId外键添加到“客户”表中。或使用某些值的哈希值。

–醉酒的代码猴子
16年8月21日在7:40



#7 楼

多余的设计不是一个好习惯。即-当不需要一个主键时,总是具有自动递增int主键是不明智的做法。文章表–具有int主键id和一个名为title的varchar列。

您还有一个充满文章类别的表格– id int主键varchar name

“文章”表中的一行的id为5,title“如何用黄油煮鹅”。您想要将该文章与“类别”表中的以下行链接:“禽”(id:20),“鹅”(id:12),“烹饪”(id:2),“黄油”(id:9) 。

现在,您有2个表格:文章和类别。您如何创建两者之间的关系?

您可能有一个包含3列的表:
id(主键),article_id(外键),category_id(外键)。但是现在您有了类似的东西:

| id | a_id | c_id | 
| 1  |  5   |   20 | 
| 2  |  5   |   12 | 
| 3  |  5   |    2 | 


更好的解决方案是拥有一个由2列组成的主键。

这可以通过执行以下操作来实现:主键的UUID。

UUID的定义是唯一的,它完成与使用唯一整数相同的操作。与整数相比,它们还具有自己的优点(和缺点)。例如,对于UUID,您知道您要引用的唯一字符串指向特定的数据记录。如果您没有1个中央数据库,或者应用程序具有脱机创建数据记录的能力(然后稍后将它们上传到数据库),则此功能很有用。最后,您无需将主键视为事物。您需要将它们视为它们执行的功能。为什么需要主键?为了能够使用将来不会更改的字段从表中唯一标识特定的数据集。您是否需要一个名为id的特定列来执行此操作,还是可以将此唯一标识基于其他(不可变的)数据?

评论


定义两个表之间的关系(通常是多对多关系)的表称为关联或交集。

– Clint Pachl
20-2-29在0:05

#8 楼


还是在某些情况下不想添加这样的字段?


当然。

首先,有一些数据库可以没有自动增量(例如,Oracle,当然不是周围最小的竞争者之一)。这应该是不是每个人都喜欢或需要它们的第一个迹象。

更重要的是,考虑一下ID的实际含义-它是数据的主键。如果您的表具有不同的主键,那么您就不需要ID,也应该没有ID。例如,表(EMPLOYEE_ID, TEAM_ID)(每个雇员可以同时在多个团队中)具有一个明确定义的主键,该主键由这两个ID组成。添加自动递增的ID列(也是该表的主键)根本没有任何意义。现在,您要拖着2个主键,“主键”中的第一个单词应该提示您实际上应该只有一个。

评论


(不是Oracle用户,所以可以原谅这个问题,但是)Oracle是否不像其他人使用自动增量/标识那样使用Sequence?是说Oracle没有Autoincrement数据类型真的只是一个语义参数?

–布拉德
16年8月16日在17:53

好吧,那只是一个小问题;主要部分是运行ID不适合每个表,因此习惯于仅在每个表上使用自动ID可能不是最明智的选择。

– AnoE
16年8月16日在19:58

没有两个主键,只有一个主键,并且所有其余的都可以用作主键的都称为候选键。

– rahul tyagi
16-09-25在17:11

#9 楼

在为“长期”数据(我希望插入一次并无限期保持记录的记录)定义新表时,我通常使用“身份”列(自动递增整数),即使它们最终通过设置位字段而被“逻辑删除” )。

在某些情况下,当您不想使用它们时,我会想到这种情况,大多数情况归结为一个数据库实例上的一个表不能成为权威来源的情况。对于新的ID值:



增量ID对于潜在的攻击者来说是太多信息。将标识列用于“面向公众”的数据服务,使您容易受到“德国坦克问题”的攻击;如果存在记录ID 10234,则可以推断出存在记录10233、10232等,至少回到记录10001,然后可以很容易地检查记录1001、101和1以确定标识列从何处开始。主要由随机数据组成的V4 GUID通过设计打破了这种递增行为,因此,仅由于存在一个GUID,就不一定存在通过递增或递减GUID字节而创建的GUID,这使得攻击者更难使用专用的服务用于单记录检索作为转储工具。还有其他一些安全措施可以更好地限制访问,但这会有所帮助。

在M:M交叉引用表中。这有点像给我,但我以前看过。如果数据库中的两个表之间存在多对多关系,则首选解决方案是一个交叉引用表,其中包含引用每个表PK的外键列。该表的PK实际上应该始终是两个外键的复合键,以获得内置索引行为并确保引用的唯一性。

当您计划在此表上进行大量插入和删除时。标识列的最大缺点可能是在插入来自另一个表或查询的行时要经历的额外麻烦,而在该表或查询中您要维护原始表的键值。您必须打开“身份插入”(无论如何在DBMS中完成),然后手动确保要插入的密钥是唯一的,然后在完成导入后,必须在表的元数据达到当前的最大值。如果此操作在此表上发生很多,请考虑使用其他PK方案。

对于分布式表。标识列非常适合单实例数据库,故障转移对以及其他情况,其中在任何给定时间,一个数据库实例都是整个数据模式的唯一权限。但是,只有这么大的空间,您仍然可以拥有一台足够快的计算机。复制或事务日志传送可以为您提供其他只读副本,但是该解决方案的规模也受到限制。迟早,您将需要两个或多个服务器实例来处理数据插入,然后彼此同步。当出现这种情况时,您将需要一个GUID字段而不是一个增量字段,因为大多数DBMS已预先配置为使用它们生成的一部分GUID作为实例特定的标识符,然后随机生成其余的标识符或逐步地。在这两种情况下,两个GUID生成器之间发生冲突的几率都是零,而在这种情况下,标识整数列是管理的噩梦(可以通过偏移种子并将增量设置为2来实现偶数/奇数运算,但是如果一个服务器看到的活动多于您浪费ID的其他活动。)

当您必须在数据库中的多个表之间实施唯一性时。例如,在会计系统中,通常以一系列代表每个日历月/年。然后可以创建视图以将它们连接在一起以进行报告。从逻辑上讲,这都是一个很大的表,但是将其切碎会使数据库的维护工作更加轻松。但是,它提出了如何管理多个表中的插入的问题(允许您在下个月开始记录事务,同时仍然关闭最后一个月)而没有重复的键。同样,GUID而不是标识整数列是首选解决方案,因为DBMS旨在以一种真正独特的方式生成它们,因此单个GUID值在整个DBMS中将只能看到一次。

正如我希望提到的那样,有一些变通办法可以在这些情况下使用标识列,但是在大多数情况下,从标识整数列升级到GUID更简单,并且可以解决更多问题完全。

评论


在某些情况下,由于将属性附加到M:N关系的实例,您仍然需要M:N表中的ID(使用列ID,ID_M,ID_N)。

– Miroxlav
16年8月17日在7:47

不保证V4 GUIDS使用具有加密功能的PNRG,因此您真的不应该在第一个示例imo中使用它(尽管如果您的数据库引擎做出更强的承诺,您可能会很好,但是那是不可移植的)。否则,一个合理的职位。

– Voo
16年8月19日在6:13

@miroxlav-我要断言,如果一个表具有足够的关于关系的元数据,那么两个FK之外的单独PK是个好主意,那么它就不再是交叉引用表了;它是自己的实体,碰巧引用了另外两个实体。

– KeithS
16年8月19日在15:10



@Voo-对的,不能保证V4 GUID是加密随机的,只是唯一的(就像所有GUID一样)。但是,美国喷气式战斗机的机尾号也不是从密码随机种子数据/算法生成的。您真正要寻找的是一个人烟稀少的域名; V4 GUID具有112字节的随机数据,能够唯一地标识5e33记录。

– KeithS
16年8月19日在15:19

从数字上看,这个星球上的每个男人,女人和孩子(全部70亿)在我们的数据库中都可以拥有741万亿个单独分类和ID的数据点,而我们仍然仅使用十亿美元的GUID值。大数据作为一个全球性行业,还远远没有达到如此庞大的知识水平。即使给GUID生成提供了一种模式,也存在其他熵源,例如数据进入系统并分配GUID的顺序。

– KeithS
16年8月19日在15:28



#10 楼

一个自动递增的(标识)主键是一个好主意,除了要注意,它在数据库上下文和该数据库的直接客户端之外毫无意义。例如,如果您将某些数据传输并存储到另一个数据库中,然后继续将不同的数据写入两个数据库表,则ID会有所不同-即,一个数据库中ID为42的数据不一定与数据匹配另一个ID为42。

鉴于此,如果仍然需要能够在数据库外部唯一地标识行(并且经常是这样),那么您必须为此使用不同的键。精心选择的业务密钥可以做到,但是您通常最终会处于需要保证唯一性的大量列的位置。另一种技术是将Id列作为自动递增的聚集主键,而将另一个uniqueidentifier(guid)列作为非聚集的唯一键,以唯一地标识世界上存在的行。在这种情况下,您仍然拥有自动递增的密钥的原因是因为对自动递增的密钥进行群集和索引比对GUI进行相同的操作更为有效。

您可能不希望使用自动递增键的情况是多对多表,其中主键是其他两个表的ID列的组合(您仍然可以使用自动递增键,但我看不到要点。

另一个问题是自动递增密钥的数据类型。使用Int32可为您提供较大但相对有限的值范围。就我个人而言,我经常将bigint列用作ID,以便实际上不必担心会用完值。

#11 楼

正如其他人提出的增加主键的理由一样,我将为GUID设置一个。应用程序中的数据的数据库。 (例如,对于类型表,您可以将GUID存储在应用程序中,并使用该GUID来检索记录。如果使用身份,则需要按名称查询数据库,我已经看到很多应用程序可以通过该操作来获取PK然后再查询一次以获取完整详细信息。)
对于隐藏数据很有用。 www.domain.com/Article/2让我知道您只有两篇文章,而www.domain.com/article/b08a91c5-67fc-449f-8a50-ffdf2403444a告诉我什么都没有。
您可以合并来自不同数据库的记录
MSFT使用GUIDS进行标识。

编辑:重复点

评论


-1。不能保证GUID / UUID是唯一的,也不是100%唯一的。 GUID仍然是有限长度的,因此尽管不太可能,但是在某些时候您可能会有获得重复的风险。关于减少数据库访问次数的观点也是无效的-为什么不能像使用GUID键那样将主ID存储在应用程序中?

–尼古拉斯H
16年8月16日在11:51

杰夫·阿特伍德(Jeff Atwood)说,这比我以前做的好得多。 blog.codinghorror.com/primary-keys-ids-versus-guids

–三种价值逻辑
16年8月16日在13:42

至于为什么不能在应用程序中存储主ID?因为数据库创建了它。如果在空数据库上运行种子,则可以假定ID为1。如果在其中包含数据的数据库上运行相同脚本,该怎么办?该ID不会是1。

–三种价值逻辑
16年8月16日在13:51

您没有说有关在应用程序中创建ID的任何内容-您只写了“存储”。但是,如果有必要在数据库外部创建ID,则可以,GUID可能是答案。

–尼古拉斯H
16年8月16日在13:54

我会补充说它们的规模更好。像Cassandra这样的大数据NoSQL数据库甚至都不支持自动增量键。

–卡尔·比勒费尔特(Karl Bielefeldt)
16年8月16日在14:19

#12 楼

作为良好设计的原则,每个表都应具有可靠的方式来唯一标识一行。尽管这就是主键的用途,但并不总是要求存在主键。向每个表添加主键不是一个坏习惯,因为它提供了唯一的行标识,但这可能是不必要的。

要维护两个或多个表的行之间的可靠关系,您需要通过外键执行此操作,因此至少在某些表中需要主键。向每个表添加主键可以更轻松地扩展需要向现有数据添加新表或关系的数据库设计。提前计划总是一件好事。

作为一项基本原则(也许是硬性规定),主键的值在其整个生命周期中都不应改变。明智的做法是假设一行中的任何业务数据在其生命周期内都可能发生变化,因此任何业务数据都不适合用作主键。这就是为什么抽象的东西(例如自动递增的整数)通常是个好主意的原因。但是,自动递增的整数确实有其局限性。

如果您的数据在数据库中只会存在生命,那么自动递增的整数就可以了。但是,正如在其他答案中提到的那样,如果您曾经希望共享,同步数据或以其他方式在数据库之外拥有生命,则自动递增的整数会导致不良的主键。更好的选择是guid(又称uuid“通用唯一ID”)。

#13 楼

该问题和许多答案都遗漏了一个重要的问题,即每个表的所有自然键仅位于数据库的逻辑模式中,而每个表的所有代理键仅位于数据库的物理模式中。其他答案仅讨论整数与GUID代理键的相对好处,而没有讨论正确使用代理键的原因以及何时使用。

BTW:让我们避免使用定义不明确和不精确的术语primary键。它是关系前数据模型的产物,首先被(不明智地)选择加入关系模型,然后由各种RDBMS供应商选择加入物理域。它的使用仅会混淆语义。

从关系模型中注意到,为了使数据库逻辑模式采用第一范式,每个表都必须具有用户可见的字段集,称为自然键,可唯一标识表的每一行。在大多数情况下,这样的自然密钥很容易识别,但是有时必须构造一个自然密钥,无论是决胜局字段还是其他形式。但是,这样构造的键始终对用户仍然可见,因此始终驻留在数据库的逻辑架构中。

通过对比,表上的任何替代键都纯粹驻留在数据库的物理架构中(并且因此,出于安全原因和维护数据库完整性,必须始终对数据库用户完全不可见)。引入代理密钥的唯一原因是为了解决数据库的物理维护和使用中的性能问题。无论是联接,复制,数据的多个硬件源还是其他。

由于引入代理密钥的唯一原因是性能,因此让我们假设我们希望它具有高性能。如果出现了性能问题,那么我们必然希望使代理密钥尽可能地窄(不会妨碍硬件,因此通常会省略短整数和字节)。连接性能取决于最小的索引高度,因此一个4字节的整数是自然的解决方案。如果您的性能问题是插入率,那么4字节整数可能也是一个自然的解决方案(取决于RDBMS的内部)。如果表的性能问题是复制或多个数据源,而不是其他替代键技术,则GUID或两部分键(主机ID +整数)可能更适合。我个人并不是GUID的最爱,但它们很方便。

总而言之,并非所有表都需要代理键(任何类型);仅在认为对于考虑中的表格的执行必要时才应使用它们。无论您喜欢哪种常用的代理关键技术,在做出选择之前都要仔细考虑表的实际需求。更改表的替代关键技术选择将使您筋疲力尽。记录表的关键性能指标,以便您的后继者了解所做的选择。出于审核(或其他目的)目的,该字段不是代理密钥;这是自然键(有额外要求)。从文档中,自动递增整数只能生成代理密钥,因此请找到另一种机制来生成它。显然,某种监视器是必需的,并且如果您要从多个站点采购交易,则一个站点将是特殊的,因为它是监视器的指定宿主站点。
如果您的表永远不会超过一百行,那么索引高度就无关紧要;每次访问都将通过表扫描进行。但是,长字符串的字符串比较仍将比4字节整数的比较昂贵,并且比GUID的比较昂贵。具有4字节整数的性能。尽管我对此没有任何证据,但我经常使用该假设,并且从未有过理ru的理由。


#14 楼

这不仅不是一个好习惯,而且实际上在Bill Karwin的《 SQL Antipatterns》一书中将其描述为一种反模式。具有模型的语义价值-并且没有理由总是将其称为id

评论


在先前的9个答案中,这似乎并没有提供任何实质性的解释。

– gna
16年8月16日在15:48

为什么这很重要?

– gna
16年8月16日在16:28

@gnat因为这是一本有关最佳做法的书,可以直接解决该问题。这不是很明显吗?

– Pedro Werneck
16年8月16日在16:32

没有丝毫。 Google搜索“ book sql最佳实践”显示了大约90万个链接,为什么这个链接特别值得

– gna
16年8月16日在16:34

@gnat我不会整天争论。您不喜欢答案,这就是降票的目的。

– Pedro Werneck
16年8月16日在16:36

#15 楼

这非常通用-否则您将需要验证密钥实际上是唯一的。这将通过查看所有其他键来完成,这将非常耗时。如果记录数接近密钥溢出值,则拥有增量密钥会变得昂贵。需要从外部指向记录,则不需要ID。

评论


密钥转换值?

– AJJ
16年8月15日在20:51

一个无符号整数的最大值为4294967295,然后再加上1将其翻转为0。请记住,如果添加一条记录然后将其删除,则计数器仍会增加。确保对字段类型使用unsigned int,否则限制为该数字的一半。

–约翰尼五世
16年8月15日在20:53

整数溢出-en.wikipedia.org/wiki/Integer_overflow

–约翰尼五世
16年8月15日在20:54

如果您添加/删除很多行,则自动递增计数器最终将溢出。

–约翰尼五世
16 Aug 15 '20:56



人们如何处理过渡?如果存在ID较低的记录从未被删除,但是您开始接近一些ID位于4294967295上限的末端怎么办?可以做“重新索引”吗?

– AJJ
16年8月15日在20:57



#16 楼

我不会说应该总是这样做。我这里有一张没有唯一键的桌子,它不需要一个。这是审核日志。永远不会有更新,查询会将所有更改返回到所记录的内容,但这是可以合理做到的最好方法,这需要人工定义错误的更改。 (如果代码允许的话,它本来就不允许这样做的!)

#17 楼

主键的自动递增计数器不是一个好主意。这是因为在插入数据之前,您需要返回数据库以查找下一个键并增加一个。

话虽如此,我通常会使用数据库可以提供的任何主键,而不是将其作为应用程序的一部分。

通过让数据库本地为您提供数据库,可以保证密钥对于其所需内容是唯一的。

当然,并非所有数据库都支持它。在这种情况下,我通常使用一个表来存储密钥存储区,并使用在应用程序中管理的上限和下限范围。这是我发现的性能最高的解决方案,因为您可以得到10000个数字范围,并在应用程序实例上自动递增它们。另一个应用程序实例可以使用另一个数字桶。您确实需要足够大的主键原语,例如64位长。

我不使用UUID作为主键,因为构建和存储UUID的成本比将长值加1的成本高得多。 UUID仍然处理生日悖论,因为理论上可能会出现重复。

评论


否。自动递增键表示该键的递增由数据库自动完成。有时(我正在看您,Oracle!)您需要序列+触发器组合来执行此操作,但是您无需查找键的先前插入值,将其加1即可使用。

– SQB
16年8月19日在14:57

使用某些持久性框架(例如JPA),如果您想将已创建的键的值返回给调用者,则需要加载记录以查看键。

–Archimedes Trajano
17年5月8日在14:03