我们应该使用序列或身份作为主键吗?

时间:2018-04-06 15:56:15

标签: sql database postgresql db2 primary-key-design

我们正在创建一个包含20多个表的新数据库,我们的数据库支持:

  • 序列。
  • 标识列(始终以标识/序列生成)。

所以,问题是:我们应该使用序列还是身份?哪一个更好?该团队似乎在这一方面存在分歧,因此我希望听到专业人士缺点,以帮助做出决定。

添加数据库详细信息:

  • 我们正在IBM DB2上创建新数据库,但我们需要确保它与将来迁移到PostgreSQL的计划兼容。

5 个答案:

答案 0 :(得分:2)

您的问题是关于使用序列与身份("始终作为身份生成#34;列,大概是)。在Postgres中,这些将被声明为serial。这些在一列中总是某种数字。

从数据库性能的角度来看,两者之间没有太大区别。一个重要的区别是一些数据库将缓存标识列,这可以加快插入速度但会造成间隙。缓存序列的规则可能不同。在高事务环境中,缓存不足可能是性能瓶颈。在多个表之间共享单个序列会使这个问题变得更糟。

从数据管理的角度来看,存在更大的差异。序列需要管理两个对象(表和序列)。表格中内置了identityserial列。

对于单个表,我只考虑在数据库中使用不支持内置自动增量/串行/标识列的序列(ahem," Oracle")。否则,我将使用设计用于表的机制。

我想指出使用自动递增的代理键还有其他好处。如果数据库中存在这样的概念,这也应该是用于聚类数据的密钥。然后,新的插入物始终位于" end" (尽管如果要删除数据,则可能只部分使用页面)。主键也应该是用于外键引用的唯一键,即使其他列 - 隔离或一起 - 是唯一的和候选主键。

答案 1 :(得分:1)

最好的答案是指出你的情况。

首先,许多人更喜欢序列,因为它们易于生成并提供单一数据类型来导航您的联接。此外,许多商店需要单列主键来进一步提高代码复杂性。

让我们谈谈缺点:

序列: 当使用b树索引时,序列通常按升序插入,这可能导致"不平衡树"并且随着时间的推移导致不完美的性能(在b树索引上)。有时,人们会生成哈希或GUID,以产生更平衡的树。

序列可能导致难以阅读"使用"查找表"时的代码,尤其是在数据库中对值进行硬编码时。示例:"其中status_seq = 1"比#34;更难阅读,其中status_id =' ACTIVE'"。

使用ID的缺点: 混合数据类型可能会导致混淆。有时它们是数字的,有时它们是varchar或char。许多ORM可能会混淆这些并导致前导零,从而导致结果出错。 IE 01234!= 1234,但您的ORM可能会返回1234而不是01234。

许多人以人类可读的形式存储ID,例如" VALID"或州缩写。从长远来看,这可能会导致令人头疼的问题,因此即使您在桌面上使用ID,您也可能希望避免直接向您的客户展示这些ID。

ID字段更有可能需要更改"在未来,而不是一个序列。示例:假设您有一个国家/地区代码表,并且发生了一次革命并且国家/地区代码发生​​了变化。你真的想通过主表和所有引用它的外键,输入新的国家代码 - 或者使用旧的国家代码,这是你的选择。如果在这种情况下使用序列,只需更新基表中的其他非键列,就可以了。

优点:

序列的好处: 序列本质上是自动生成的。 ID并非总是如此。添加记录时,您真的希望程序员或用户命名一个无法轻易更改的ID吗?当您使用序列时,很少需要重新编号,如果出现错误,可以轻松更改基础的人类可读数据。

如上所述,它们始终是一种数字数据类型,如果使用得当,可以帮助导航"你的应用程序(IE,通常只需要"传递"一个数字来导航你的表结构)

当使用DB和您的编程语言之间的通信时,您可以指望能够将整数转换为整数而不会出现任何奇怪的数据转换问题。

的ID: 主要好处是我们已经在上面解释过的更容易阅读的代码。

总之,我认为这取决于表格和列的使用情况。如果您要使用ID,请避免向用户显示值的诱惑。如果表格不会改变并只是持有旗帜,或者" enum"类型数据,然后ID肯定有助于代码可读性。否则,序列通常是数据可维护性的最佳选择。

有些人会选择GUID或ID来帮助提高索引效果,但就个人而言,如果代码可读性有任何损失或代码变得更复杂,我会花一些钱在更好的硬件上之前#&# 39; d编写更复杂的代码 - 因为好处微乎其微。

资料来源:Oracle认证DBA(关于此主题的培训),以及20多年与开发人员和企业数据库合作的经验。

答案 2 :(得分:0)

我是序列的粉丝。如果所有ID都是相同的类型,我喜欢它,并且所有ID都来自相同的序列。这不是必需的,只是让你能够弄清楚事情发生的顺序......这通常不是技术要求,而是调试辅助工具。我倾向于支持bigint作为我的密钥类型,因此我几乎可以保证永远不会用完ID。如果您使用的是int(或更小),则需要为每个表使用一个序列。

话虽如此,但有些问题需要注意。例如,可以“刻录”序列而不将它们实际放入数据中。同样,这可能是也可能不是问题。一般来说,我没有必要关心。

序列通常通过在表的ID列上创建默认约束来实现。这意味着需要注意的几件事情。实际上可能会在插入中提供列的值...这不会“碰撞”您的序列,并且可能会与未提供值的未来插入冲突。对我而言,这是最重要的问题。如果您的所有ID都是默认提供的,那么这不是问题。

程序(和远程客户端)可以保留或“刻录”序列。这非常方便...让您的过程代码事先知道ID是什么,而不必将它们提交给数据。您可以随时执行以下操作:

insert someTempTable( Id, Name )
select
  next value for dbo.MySequence,
  Name
from
  dbo.SomeTable

...它会烧掉序列值,但不错的是,当我将工作表someTempTable中的行插入 real 表时,我可以放心ID不会发生冲突。这比基于标识列的ID更简单。我可以在temp中构建一系列相关数据,然后将它们全部移动到持久存储中。这通常效率更高。

答案 3 :(得分:0)

我没有使用序列,但我可以讨论身份字段。

首先,他们在使用SQL Server的过去18年中使用它们的每种情况下都能很好地工作。这很可能在其他数据库上也是如此,这对于使用它们的数据库来说是一个关键特性。我们从来没有遇到过使用身份的任何问题。如果您希望拥有一个非常大的数据库,则可能需要在设置时将标识定义为big int。

如果您在创建表时没有设置标识,稍后在SQl Server中进行设置会很麻烦,请检查数据库中的详细信息。但是,如果您将自动生成的密钥专门用作PK,则可以在创建表时执行此操作。

使用标识(或序列或GUID)时的一个关键是除了自动生成的值之外,如果你有自然生成的值,你需要为表中的自然键创建一个唯一的索引。这将防止数据完整性问题。

如果您在回滚时跳过了数字问题,则可能存在其他问题。由于这些都是占位符,它们不应该有意义,所以它可能不是问题,但我已经看到人们出于商业原因而非技术原因而需要此功能的情况。使用回滚测试两者,看看是否有间隙,如果你需要它们没有间隙。如果两者都有差距,那么你需要推动自己的系统注意竞争条件。

由于您说您要在DB2中创建一个数据库以迁移到Postgres,我会设置一个测试,其中包含几个db2中具有标识的表和一些包含序列的表。在其中插入大量虚假数据。然后我会测试将它们移植到Postgres数据库并开始添加记录是多么困难。这可能是一个关键数据,其中方法在您的特定情况下更好。

您可能还会考虑通过将大量记录插入两个相似的测试表来进行性能测试,除非他们分配Id。可能两种方式都可以接受性能,也可能是一种方式比另一种方式更快。以下链接适用于SQL Server,但测试方法可能是您在确定时可以找到的有用方法。 http://dba-presents.com/index.php/sql-server/25-identity-vs-sequence-performance-test

如果这是一个关键问题,那么对性能等事情做出自己的决定至关重要,因为结果会受到您自己特定设置的影响。

如果你想要一个基于某些文本值和递增数字的有意义的ID(例如CA1,CA2,CA3,TX1,TX2,TX3),那么身份将不起作用,但我认为是一个序列(参见本文:PostgreSQL sequence based on another column)。所以序列可以给你更多的灵活性,但如果你不需要它,那么为什么要这么麻烦?

可能我也会认为维护(以及在你的情况下转换)有时使用一个而有时使用另一个是最令人困惑的。一致如何做事可能是关键。如果你有一个案例,其中序列给你一个灵活性,你必须拥有,但你的身份却没有。我会在整个过程中使用序列,以避免在执行转换时知道哪个表使用了什么不必要的复杂性。

答案 4 :(得分:0)

Db2 IDENTITY列由序列支持(支持缓存和乱序生成以获得更高性能) - 差异纯粹是语法。使用标识列:

create table t1 (
  id integer not null generated always as identity cache 100,
  foobar varchar(111)
)

提供该列的值,它会自动生成并插入:

insert into blah (foobar) values ('something')

如果列未定义为IDENTITY,则必须显式创建序列并在插入行时生成值

create table t2 (
  id integer not null,
  foobar varchar(111)
)

create sequence myseq cache 100

insert into t2 values (next value for myseq, 'something else')

同样,当您需要重新定义标识属性时,可以通过ALTER TABLE语句执行此操作;对于使用ALTER SEQUENCE的序列。

表中只能将一列定义为IDENTITY;如果您需要多个这样的列,则必须使用序列。

使用LOADIMPORT实用程序将数据批量加载到具有标识列的表中时,需要特殊处理 - 您需要覆盖或忽略可能存在的标识值。