Man*_*ngo 7 database-design primary-key candidate-key
请原谅这里任何定义的松散,但我试图探索一个简单的概念。
一个主键唯一标识一行。表中可能还有其他列具有唯一值,因此它们也可以唯一标识一行(候选键),但主键是为任务指定的。
使主键更有用的属性包括:
由于这些原因,我通常建议主键没有内在价值,因此永远没有理由更改或回收值。也就是说,它应该是没有意义的。
我见过包含某种代码的主键,例如基于名称的客户端代码。明显的问题是 (a) 如果客户端名称更改,那么 PK 也应该更改,以及 (b) 与具有相似名称的客户端发生冲突的风险太大。
半个例外是使用自动递增的数字,它具有序列号的次要含义。但是,它仍然很稳定。
问题是,在什么情况下,如果有的话,最好使用具有其他实际含义的主键?也就是说,PK应该是任意的,并且你通常可以通过序列号获得的建议有什么问题吗?
在什么情况下,如果有的话,最好使用具有其他实际含义的主键?(强调)
鉴于这个问题的重点是“可取”而不是“可接受”,并且接受这仍然是一个高度主观的话题,我会说我想不出最好的情况是系统有一个真正自然的出于各种原因的关键(其中大部分已经在保罗在对该问题的评论中链接的其他答案中说过):
我强调“真正”,因为有两种情况我更喜欢没有新的代理键:
桥接表
(或任何您喜欢称呼的表格,仅用于或主要用于表示多对多关系)
Thing ThingXTag Tag
------ --------- ---
ThingID INT AutoMagic PK ---> ThingID INT PK, FK
Stuff SomeType TagID INT PK, FK <--- TagID INT AutoMagic PK
TagName VARCHAR
Run Code Online (Sandbox Code Playgroud)
在对桥接表(逻辑模型中不存在但物理模型中需要的表)进行建模时,PK 应该存在通过该表关联的表的现有主键列。这允许在不需要单独的唯一索引/约束的情况下强制执行值的正确唯一性和非 NULL 性。在这种关系需要外键的罕见情况下,它将:
WackyTable ThingXTag
---------- ---------
WackyTableID INT AutoMagic PK
ThingID INT FK ---> ThingID INT PK, FK (to Thing.ThingID)
TagID INT FK ---> TagID INT PK, FK (to Tag.TagID)
AttributeX VARCHAR InsertDate DATETIME
InsertDate DATETIME
Run Code Online (Sandbox Code Playgroud)
我曾在一个系统上工作,其中这些桥接表有自己的自动递增代理键 PK,并且桥接表的单列代理键通过 FK 在其他表中引用:
WackyTable ThingXTag
---------- ---------
WackyTableID INT AutoMagic PK
ThingXTagID INT FK ---> ThingXTagID INT AutoMagic PK
AttributeX VARCHAR ThingID INT FK (to Thing.ThingID)
InsertDate DATETIME TagID INT FK (to Tag.TagID)
InsertDate DATETIME
Run Code Online (Sandbox Code Playgroud)
这是一个可怕的、令人困惑的混乱,我们浪费了太多时间进行调试等。
同级表
这些表是真正的单个实体,因此具有 1 对 1 的关系。出于性能原因,它们仅被拆分为两个(或更多,根据需要)表。我已经对具有 100 万(或更多)行的表进行了此操作,这些行要么非常宽,要么宽度适中,并且有些列要么不经常使用,要么是超过 50 个字节的字符串。像那样的东西。这将实体的核心属性保留在一个更窄的表中,该表适合每个数据页上的更多行。
在这些情况下,“兄弟”表与初始表处于完全相同的级别,并且应该具有相同的PK
. 给它一个自动递增的代理键是没有用的,因为每一行都有相当于初始表的自然键。
Product ProductProperty
------- ---------------
ProductID INT AutoMagic PK ---> ProductID INT PK, FK (to Product.ProductID)
Name VARCHAR ShortDescription VARCHAR
SKU VARCHAR SomethingElse SomeType
Quantity INT UpdateDate DATETIME
CreateDate DATETIME
UpdateDate DATETIME
Run Code Online (Sandbox Code Playgroud)
需要明确的是,我说的是物理模型,而不是概念模型。我认为这个问题的重点是物理模型,因为它是在概念上不存在的问题的上下文中构建的:代理键、处理主键值使用的问题等。考虑到这一点,我不是暗示不应存储自然密钥并将其用于识别。相反,自然键是很好的“备用键”,应该在其上放置唯一的约束/索引。然而,概念模型的理想主义并不总是直接转化为物理模型。数据的完整性(即,数据模型的稳定性和可靠性)是顶,如果不是在top,物理模型的优先级。因此,必须进行实际考虑,例如使用代理键,以确保实现这一目标而不是妥协。意思是,如果您有 SSN 或 SKU 等,那么绝对将它们存储在具有唯一约束的列中,并让系统对该值进行查找,因为无论如何都不应该在外部使用自动生成的数字。用户不需要知道记录的自动生成的 ID 号:他们应该传入一个他们知道的值(例如,电子邮件地址作为 UserID/CustomerID 的查找,航班确认代码与航班日期等)并且系统应该将其转换为它从那时起使用的自动生成的值。
是的,在使用自然键作为备用键时,本答案开头提到的问题仍然是潜在问题。但是,不同之处在于问题被隔离到(通常)只有一张表。如果有人犯了一个错误并在“航班定位器”上创建了一个唯一索引,那么他们可能需要一段时间才能获得违规行为。但是一旦他们这样做了,就很容易删除该唯一索引并重新创建它以包括航班日期。或者,如果您在系统上更改您的电子邮件地址(通常用作登录名)并由于其他人几年前(合法地)使用它而收到错误,那么这很可能由支持人员处理,而不会对您造成任何影响/风险现有的相关记录。在这两种情况下,数据模型的其余部分都不会进行必要的更改。
同样,这是一种务实的方法:
我不知道有多少系统使用 SSN(美国的社会安全号码)作为 PK,但对于使用 SSN(美国的社会安全号码)作为 PK 的系统,其中一些(也许很多)可能避免了它们不像它们应该那样独特的问题。但是,多年来,这些系统都无法避免发生变化,因为需要更安全地处理它们。将 SSN 视为备用密钥的系统需要很少的开发时间来切换到加密这些值,并且系统几乎不需要(或不需要)停机时间来在数据层进行更改。鉴于我们都有可能永远无法完成的项目积压,企业往往更愿意让这些烦人但不可避免的更改花费 5 小时而不是 20 到 40 个小时(不要忘记更改需要测试,
明确地说,在某些情况下,拥有自然键是“可以接受的”,尽管我认为我不会说“可取”。
INT
如果使用二进制排序规则(以_BIN2
, 甚至结尾_BIN
,但_BIN2
首选),那么它的比较速度应该一样快。对此类代码具有相对有意义的值可以使支持/调试更容易。但是,您仍然会遇到这样的情况:随着时间的推移,部门名称等可能会发生变化,并且代码可能不再有意义。 归档时间: |
|
查看次数: |
3370 次 |
最近记录: |