22

基本上,我需要将来自多个供应商的产品数据合并到一个数据库中(当然,它比这更复杂),其中有几个表需要连接在一起以进行大多数 OLTP 操作。

我将坚持使用默认值并使用自动递增整数作为主键,但是当一个供应商提供他们自己的“ProductiD”字段时,其余的则不提供,我将不得不做很多手动映射到另一个表然后加载数据(因为我必须先将其加载到 Products 表中,然后将 ID 取出并将其与我需要的其他信息一起添加到其他表中)。

或者,我可以使用产品的 SKU 作为其主键,因为 SKU 对于单个产品是唯一的,并且所有供应商都在其数据馈送中提供 SKU。如果我使用 SKU 作为 PK,那么我可以轻松加载数据馈送,因为一切都基于 SKU,这就是它在现实世界中的工作方式。但是,SKU 是字母数字的,并且可能比基于整数的密钥效率略低。

我应该看看有什么想法吗?

4

10 回答 10

46

这是代理和自然主键之间的选择。

恕我直言,总是偏爱代理主键。主键不应该有意义,因为该含义可以改变。就连国名也可以改变,国家可以存在和消失,更不用说产品了。绝对不建议更改主键,自然键可能会发生这种情况。

有关代理键与主键的更多信息:

所以代理键会赢吗?好吧,让我们回顾一下自然键的任何缺点是否适用于代理键:

  • 缺点 1:主键大小 – 代理键通常没有索引大小问题,因为它们通常是 int 类型的单列。这大约是最小的。
  • 缺点 2:外键大小 - 它们没有外键或外索引大小问题,原因与缺点 1 相同。
  • 骗局 3:美学 - 嗯,它是一种旁观者类型的东西,但它们当然不涉及编写与使用复合自然键一样多的代码。
  • 骗局 4 和 5:可选性和适用性——代理键对于不想或无法提供数据的人或事物没有任何问题。
  • 骗局 6:唯一性——它们 100% 保证是唯一的。那是一种解脱。
  • 骗局 7:隐私 - 如果不道德的人获得它们,他们没有隐私问题。
  • 骗局 8:意外的非规范化——您不能意外地对非业务数据进行非规范化。
  • 骗局 9:级联更新 - 代理键不会改变,所以不用担心如何在更新时级联它们。
  • 骗局 10:Varchar 加入速度 - 它们通常是 int 的,因此它们通常会尽可能快地加入。

还有主键的代理键与自然键?

于 2009-02-26T13:10:37.017 回答
10

除了最简单的内部情况外,我建议始终使用代理键。它为您提供未来的选择,并保护您免受未知因素的影响。

没有理由不能将其他密钥(如 SKU)设置为非空来强制执行它们,但至少通过消除对第三方的依赖,您可以选择,而不是从你并在后期忍受痛苦的重写。

无论您选择自动递增的整数还是自己确定下一个主键,都会出现复杂情况。使用 auto-incremented 方法,您可以轻松插入记录并让它分配自己的键,但您可能无法准确识别您的记录给出的键(并且不能保证获取最大键返回您的键)。

我倾向于使用自分配密钥,因为您拥有更多控制权,并且在 sql server 中,您可以从中央密钥表中检索密钥并确保没有其他人获得相同的密钥,所有这些都在一个语句中:

DECLARE @Key INT

UPDATE  KeyTable
WITH    (rowlock)
SET @Key = LastKey = LastKey + 1
WHERE   KeyType = 'Product'

该表记录了最后使用的键。上面的 sql 直接在表中增加该键并返回新键,确保其唯一性。

为什么要避免使用字母数字主键:

三个主要问题:性能、整理和空间。

性能 - 虽然有性能成本,就像下面的 Razzie,我不能引用任何数字,但索引字母数字的效率低于数字。

排序规则 - 您的开发人员可能会在不同的表中创建具有不同排序规则的相同键(它会发生),这会导致在查询中加入这些表时不断使用“排序”命令,并且很快就会变老。

空格 - 像 David 的 9 个字符的 SKU 需要 9 个字节,但一个整数只需要 4 个(2 代表 smallint,1 代表 tinyint)。即使是一个 bigint 也只需要 8 个字节。

于 2010-02-03T04:09:18.283 回答
4

自然键一直存在的危险是,当某些更改超出您的控制范围时,您的初始假设现在或将来会被证明是错误的,或者在某些地方您需要引用一条记录,其中传递有意义的字段不是需要(例如,使用员工的社会安全号码作为主键的 Web 应用程序,然后必须使用 /employee.php?ssn=xxxxxxx 之类的 url)

根据我自己对“独特”SKU 和供应商数据提要的个人经验 - 您是否绝对 确定他们向您发送的提要包含完整、独特、格式良好的 SKU?

在从具有不同 IT 水平和文书能力的供应商处获取信息时,我必须亲自处理以下所有问题:

  • 产品完全缺少其 SKU ("")
  • 文员在他们的数据库中使用了占位符 SKU,例如 999999999 和 00000000,但从未更正它们
  • 那些进行数据输入或导入的人混淆了各种产品编号,将 UPC 与 SCC 之类的东西混为一谈,甚至想方设法将它们拼凑在一起(我见过最后带有不可能校验位的 SCC 代码,因为他们只是复制了UPC 并添加 01 或 10,不更正校验位)
  • 由于特殊原因,或者只是无能,供应商在其数据库中输入了两次相同的产品(例如,同一主板的 rev. 1 和 rev. 2 具有相同的 SKU,但在供应商数据库和数据馈送中作为 2 条记录存在因为 rev 2. 有新功能)
于 2009-02-26T13:21:06.477 回答
2

我也会使用自动增量主键。有一个字母数字主键对性能的影响是存在的,尽管我不敢说出任何数字。但是,如果性能在您的应用程序中很重要,则更有理由使用自动增量主键列。

于 2009-02-26T13:13:15.187 回答
1

我建议将自动递增的“无意义”整数作为主键。如果有人提出重新组织产品 ID 的想法,至少您的数据库不会遇到麻烦。

于 2009-02-26T13:09:32.183 回答
1

和我几个月前的问题很相似...

我应该有一个专用的主键字段吗?

最后我选择了一个自动递增的 PK。

于 2009-02-26T14:09:30.463 回答
1

由于您正在处理来自您无法控制的多个供应商的数据,因此我将使用代理键。您不希望有一天当其中一个碰巧向您发送副本时,您必须重新架构您的数据库设计。

于 2009-02-26T14:22:21.917 回答
1

代理键(自动递增 INT 字段)将唯一标识表中的一行。另一方面,唯一自然键 (productName) 将防止重复的产品数据进入表中。

使用唯一的自然键字段,两行或多行永远不会有相同的数据。

使用代理键字段,由于自动递增 INT 字段,行可以是唯一的,但行中的数据将不是唯一的,因为代理键与数据无关。

让我们以 User 表为例,该表的自然键字段 (userName) 将阻止同一用户注册两次,但自动递增 INT 字段 (userId) 不会。

于 2013-04-10T15:44:12.820 回答
0

如果每个产品都有一个 SKU 并且每个产品的 SKU 都是唯一的,我不明白您为什么不想将它用于可能的主键。

于 2009-02-26T13:06:05.827 回答
0

您总是可以对 SKU 进行哈希处理,从而消除 alpha。您必须为可能的冲突(这应该非常罕见)编写代码,这是一个额外的复杂性。

我会使用散列来填充主键并使初始导入变得容易,但是在 dB 中使用它时,总是将其视为随机数。这样主键将失去它的意义(并具有自动递增键的所有优点),从而在未来允许灵活性。

于 2009-02-26T13:20:35.277 回答