我想看一个例子:
- 什么时候合适
- 当这不合适时
数据库的选择是否会对上述示例产生影响?
这似乎真的是关于代理键的问题,代理键始终是自动递增的数字或 GUID,因此是单列,而自然键通常需要多条信息才能真正唯一。如果您能够拥有一个只有一列的自然键,那么这一点显然是没有实际意义的。
有些人会坚持只使用其中一种。花足够的时间使用生产数据库,您将了解到没有与上下文无关的最佳实践。
其中一些答案使用 SQL Server 术语,但这些概念通常适用于所有 DBMS 产品:
聚集索引。 当数据库只能追加到聚集索引时,聚集索引总是表现最好 - 否则,数据库必须进行页面拆分。请注意,这仅适用于键是顺序的,即自动递增顺序或顺序 GUID。任意 GUID 的性能可能会更差。
关系。如果您的键长为 3、4、5 列,包括字符类型和其他非紧凑数据,那么如果您必须在其他 20 个表中创建与该键的外键关系,您最终会浪费大量空间并随后降低性能。
独特性。有时您没有真正的自然键。也许你的表是某种日志,你有可能同时得到两个相同的事件。或者,也许您的真正密钥类似于物化路径,只能在已插入行后才能确定。无论哪种方式,您总是希望您的聚集索引和/或主键是唯一的,因此如果您没有其他真正唯一的信息,您别无选择,只能使用代理键。
兼容性。 大多数人永远不必处理这个问题,但如果自然键包含类似 a 的内容hierarchyid
,则可能某些系统甚至无法读取它。在这种情况下,您必须再次创建一个简单的自动生成的代理键以供这些应用程序使用。即使自然键中没有任何“奇怪”的数据,一些数据库库在处理多列主键时也会遇到很多麻烦,尽管这个问题很快就会消失。
贮存。许多使用数据库的人从来没有使用过足够大的数据库来关心这个因素。但是当一个表有数十亿或数万亿行时,您将希望在该表中保留尽可能少的数据量。
复制。是的,您可以使用 GUID 或顺序 GUID。但是 GUID 有其自身的权衡,如果由于某种原因您不能或不想使用 GUID,多列自然键是复制方案的更好选择,因为它本质上是全局唯一的- 即就是,您不需要特殊的算法来使其独一无二,它的定义是独一无二的。这使得推理分布式架构变得非常容易。
插入/更新性能。代理键不是免费的。如果您有一组唯一且经常查询的列,因此您需要在这些列上创建覆盖索引;索引最终几乎与表一样大,这会浪费空间并且每次进行任何修改时都需要更新第二个索引。如果您有可能在一张表上只有一个索引(聚集索引),那么您应该这样做!
这就是马上想到的。如果我突然想起其他任何事情,我会更新。
你几乎总是需要一个主键,所以我假设选择是选择现有的两列作为主键,或者创建一个新的自动递增 PK 并在两列上放置一个普通的唯一约束。
当您需要 2 列主键时:
当你想要一个自增主键时:
我认为(至少从应用程序开发人员的角度来看)使主键成为自动生成的键,并在多列上创建唯一约束和索引几乎总是更好。
我遇到过一些令人头疼的情况,因为 DBA 认为多列主键总是足够的,而未来的需求变化证明这是不正确的。
举几个例子...
合适的:
不当:
对于 OLAP 系统中的维度表——您希望使维度键尽可能小,以便您的事实表尽可能小(和快)。
当您不确定组合是否唯一时。当然,这是一个非常糟糕的例子,但是对于多列 PK,“Person”表将是一个糟糕的选择。
一个合适的例子是当你有一个链接表,其中的外键字段连接不同的表。
一般来说,尽可能使用现有的标识字段作为主键可能是个好主意。如果您没有自然 id 字段,并且您必须组合很多字段才能获得唯一的 PK,那么使用自动编号可能会更好。具有超过 2 个字段的主键可能会变得混乱。
当我们使用多列索引和键时,我们发现我们的应用程序的性能大大提高。它允许我们在最常见的查询上创建索引,并且甚至不访问主表,因为整个 select 子句可能在索引中。但是,这取决于您的应用程序和数据集。
有时复合自然键具有直观意义。例如,假设您有一个公司表(PK 是 ComapnyId),列中有公司的一些详细信息。您还需要在公司的整个历史中存储公司的 CEO 姓名。自然不变的是,一家公司一次只能有一位 CEO。然后使用 CompanyId(Company 表中 CompanyId 的 FK)+ FromDate 的复合 PK 创建一个 CompanyCeo 表是很直观的。该表中的其他列可能是 ToDate 和 CeoName。通过这种方式,您可以保证只有一位 CEO 可以在特定日期开始工作。