始终将单个整数列作为主键的缺点是什么?

Ale*_*xei 18 database-design sql-server identity sql-server-2014

在我正在处理的一个 Web 应用程序中,所有数据库操作都是使用一些在实体框架 ORM 上定义的通用存储库进行抽象的。

但是,为了通用存储库的简单设计,所有涉及的表都必须定义一个唯一的整数(Int32在 C# 中,int在 SQL 中)。直到现在,这一直是桌上的PK,也是IDENTITY.

外键被大量使用,它们引用这些整数列。它们对于一致性和 ORM 生成导航属性都是必需的。

应用层通常会做以下操作:

  • 从表(*)加载初始数据-SELECT * FROM table
  • 更新-UPDATE table SET Col1 = Val1 WHERE Id = IdVal
  • 删除-DELETE FROM table WHERE Id = IdVal
  • 插入-INSERT INTO table (cols) VALUES (...)

不太频繁的操作:

  • 批量插入-BULK INSERT ... into table后跟 (*) 所有数据加载(以检索生成的标识符)
  • 批量删除- 这是一个正常的删除操作,但从 ORM 的角度来看是“庞大的”:DELETE FROM table where OtherThanIdCol = SomeValue
  • 批量更新- 这是一个正常的更新操作,但从 ORM 的角度来看是“庞大的”:UPDATE table SET SomeCol = SomeVal WHERE OtherThanIdCol = OtherValue

*所有小表都缓存在应用程序级别,几乎都SELECTs不会到达数据库。典型的模式是初始加载和大量的INSERTs、UPDATEs 和DELETEs。

根据当前的应用程序使用情况,在任何表中达到 100M 记录的可能性很小。

问题: 从 DBA 的角度来看,如果有这个表设计限制,我会遇到什么重大问题吗?

[编辑]

阅读答案(感谢您的精彩反馈)和参考文章后,我觉得我必须添加更多细节:

  1. 当前的应用程序细节- 我没有提到当前的 Web 应用程序,因为我想了解该模型是否也可以用于其他应用程序。但是,我的特殊情况是从 DWH 中提取大量元数据的应用程序。源数据非常混乱(以一种奇怪的方式去规范化,有一些不一致,在许多情况下没有自然标识符等),我的应用程序正在生成清晰的分离实体。此外,IDENTITY还显示了许多生成的标识符 ( ),以便用户可以将它们用作业务密钥。除了大量的代码重构之外,这还排除了 GUID 的使用

  2. “它们不应该是唯一标识一行的唯一方法”(Aaron Bertrand?)——这是一个很好的建议。我所有的表还定义了一个 UNIQUE CONSTRAINT 以确保不允许业务重复。

  3. 前端应用驱动设计与数据库驱动设计——设计选择是由这些因素造成的

    1. 实体框架限制- 允许多列 PK,但无法更新其值

    2. 自定义限制- 具有单个整数键极大地简化了数据结构和非 SQL 代码。例如:所有值列表都有一个整数键和一个显示值。更重要的是,它保证任何标记为缓存的表都能够放入Unique int key -> value映射中。

  4. 复杂的选择查询- 这几乎永远不会发生,因为所有小(< 20-30K 记录)表数据都在应用程序级别缓存。这使编写应用程序代码时的生活变得更加困难(更难编写 LINQ),但数据库受到的打击要好得多:

    1. 列表视图- 不会SELECT在加载时生成查询(所有内容都被缓存)或如下所示的查询:

      SELECT allcolumns FROM BigTable WHERE filter1 IN (val1, val2) AND filter2 IN (val11, val12)
      
      Run Code Online (Sandbox Code Playgroud)

      所有其他必需的值都是通过缓存查找 (O(1)) 获取的,因此不会生成复杂的查询。

    2. 编辑视图- 将生成如下SELECT语句:

      SELECT allcolumns FROM BigTable WHERE PKId = value1
      
      Run Code Online (Sandbox Code Playgroud)

(所有过滤器和值都是ints)

Aar*_*and 19

除了额外的磁盘空间(以及内存使用和 I/O)之外,即使向不需要 IDENTITY 列的表(不需要 IDENTITY 列的表的示例)添加 IDENTITY 列也没有任何害处是一个简单的联结表,就像将用户映射到他/她的权限一样)。

我反对在 2010 年的一篇博文中盲目地将它们添加到每个表格中:

但是代理键确实有有效的用例 - 请注意不要假设它们保证唯一性(有时这就是它们被添加的原因 - 它们不应该是唯一标识行的唯一方法)。如果您需要使用 ORM 框架,并且您的 ORM 框架需要单列整数键,即使您的实际键不是整数,也不是单列,或者两者都不是,请确保定义唯一约束/索引也为你真正的钥匙。


小智 13

根据我的经验,为每个表使用单独 ID 的主要原因如下:

几乎在每一个案例中,我的客户在构思阶段都发誓,一些外部的“自然”场XYZBLARGH_ID将永远保持独特,对于给定的实体永远不会改变,永远不会被重用,最终出现了这样的情况:主键属性已损坏。它只是行不通。

然后,从 DBA 的角度来看,使 DB 变慢或膨胀的事情肯定不是每行 4 个字节(或其他),而是诸如错误或缺少索引、忘记表/索引重组、错误的 RAM/表空间调整参数之类的事情,忽略使用绑定变量等等。那些可以使 DB 减慢 10、100、10000 倍……而不是额外的 ID 列。

因此,即使每行额外增加 32 位存在技术上的、可衡量的缺点,这不是您是否可以优化 ID 的问题,而是 ID在某个时候是否必不可少,这将是更多可能比没有。而且我不会计算软件开发立场的所有“软”好处(例如您的 ORM 示例,或者当所有 ID 设计为具有相同的数据类型时,它使软件开发人员更容易,等等) .

注意:请注意,n:m关联表不需要单独的 ID,因为对于此类表,关联实体的 ID 应构成主键。一个反例是一个奇怪的n:m关联,它允许相同的两个实体之间出于任何奇怪的原因进行多个关联——然后那些需要他们自己的 ID 列来创建一个 PK。有无法处理多列的PK的,所以这将是宽松与开发商的理由,如果他们有这样的图书馆工作ORM库。

  • “奇怪的 n:m 关联,它允许相同的两个实体之间存在多个关联”在现实生活中非常常见。例如,一个人拥有一辆汽车,然后要求在所有权开始和结束时更改为记录,(一个人可以卖掉汽车然后再买回来,然后使您的软件崩溃....) (2认同)

nvo*_*gel 6

如果您总是向每个表添加一个无意义的额外列,并且仅将这些列作为外键引用,那么您几乎不可避免地会使数据库变得更加复杂和难以使用。实际上,您将从外键属性中删除用户感兴趣的数据,并强制用户/应用程序执行额外的连接以检索相同的信息。查询变得更加复杂,优化器的工作变得更加困难并且性能可能会受到影响。

您的表中填充的“真实”数据将比其他情况下更加稀疏。因此,数据库将更难以理解和验证。您可能还会发现很难或不可能强制执行某些有用的约束(其中约束将涉及不再在同一个表中的多个属性)。

我建议您更仔细地选择您的密钥,并仅在您有充分理由时才将它们设为整数。您的数据库设计基于良好的分析、数据完整性、实用性和可验证的结果,而不是依赖教条规则。

  • 这个问题询问了可能的缺点,因此我的回答。我不否认如果使用得当,代理键是有意义的。但我见过有 3、4、5(或更多)无意义外键的表,因此需要 3、4、5 或更多连接才能从中获得有用的结果。更实用的设计可能根本不需要连接。 (2认同)
  • 我不相信这种查询的执行才是人们在这种设计中遇到的主要问题——他们经常反对的是查询的编写。 (2认同)

CaM*_*CaM 5

根据我对各种数据库的经验,Integer 主键总是比根本没有定义键的应用程序要好或者具有以不合逻辑的尴尬方式连接六个 varchar 列的键...... (叹气)

我见过从整数 PK 切换到 GUID 的应用程序。他们这样做的原因是在某些情况下需要合并来自多个源数据库的数据。开发人员将所有切换到 GUID,这样合并就可以发生而不必担心数据冲突,即使在不属于合并的表上也是如此(以防这些表成为未来合并的一部分)。

我想说一个整数 PK 不会咬你,除非你打算合并来自不同来源的数据,或者你的数据可能超出了你的整数大小限制——这一切都很有趣和游戏,直到你用完插入的空间.

我会说,虽然,它可以是有意义的设置您的聚集索引上比你的PK等一列,若该表将被更频繁地这样询问。但这是一个异常情况,特别是如果大量更新和选择基于 PK 值。

  • 听起来像是更改 guids 的所有键的可怕理由。我目前正在使用一个数据库,该数据库使用所有代理键的 guid.. 这不好玩。 (2认同)
  • 不。使用 GUID 并不好玩。我不喜欢它们,但我尊重它们在某些用例中的价值。 (2认同)