我正在尝试为数据库建模。对于问题的一部分,我有一个实体区域,其中包含部门,并且每个部门都分配了特定的功能。我有以下。
我想了两种处理关系的方法。第一种是为 Department 表创建一个自增键,假设部门名称可以在不同区域重复。我对 Function 表执行相同操作,将可递增键作为标识符,并为组合(Department_ID、Name)创建一个唯一键。
第二种方式是把字段组合(Department_ID, Name)作为Department的主复合键,Function表也一样,把字段(Department_ID, Name)作为复合主键。
与第一种情况一样,使用简单键而不是复合键有哪些优点或缺点,它如何影响唯一键的使用?这是正确的方法吗?
简短的回答
从逻辑的角度来看,复合键没有固有的缺点。它存在。如果需要保持唯一性/数据完整性,则必须使用它。
从物理实现的角度来看,复合键可能会创建 b 树和聚集索引(如果使用/支持)的碎片,因为不保证插入会附加到表的末尾。大多数商业数据库引擎都非常擅长处理/管理碎片(有些总是将新行附加到新页面),所以这不应该是您选择一个而不是另一个的理由,除非在非常有限的情况下。
更长的答案
所以让我们回顾一下,因为我认为大多数从业者都忽略了关于主键的一个关键问题:
主键必须强制数据的唯一性。
id
列不强制唯一性,因为它是系统生成的,而不是从数据中生成的。因此,在数据建模开始时对每个实体都添加一个是没有意义的,
id
因为它会妨碍您理解数据的实际结构。id
一旦知道实际的键,我们就可以稍后应用列。将id
成为先前主键的代理项,该键将成为备用键。因此,我可以从您的图表中看到,您至少知道后来的事实
Function
,但没有将其应用于Area
orDepartment
。因此,让我们首先从等式中删除行标识符。这就是你所拥有的:
这时候自然会问:
function
独立于一切的departments
吗?department
实现相同function
吗?在第一种情况下,我们的模型将变为:
但如果第二个成立,那么模型将是:
所以你可以看到,在我们开始考虑是否用代理项替换现有的主键之前,还有很多工作要做。
我们可能会认为
Area_Name
它太宽而无法在实践中使用。我们可以将其作为备用键并替换为人类可读的代码/短名称(首选)或系统生成的整数(如果您确实必须)。我们可以类似地决定Department_Name
.所以让我们实现第二个模型,用代理替换宽键:
在这种情况下,
DepartmentFunction
键(Department_Id,Function_Id)
是:Department
,稍后在数据模型中可能需要该关系以实现完整性所以没有令人信服的理由来取代它。
我要强调的一件事是,仅使用整数代理作为最后的手段。如果有可以使用的速记/代码,则最好是:
这是错误的,因为:
Department_ID
也不提供Name
唯一性我们现在只谈论主键,是吗?
复合主键始终必须是自然的。即使它是 2 列的组合,这些列是对另一个表的合成 PK 的引用(M:N 关系连接表)。包含合成组件的复合主键没有意义 - 表上的行唯一性可能仅由此合成表达式控制。当需要某些自然表达式的唯一性时,常规唯一索引就足够了。
你可以而且通常两者兼有是个好主意。一个简单的自增键保证了唯一性,并且通常是最适合加入的,但是复合键有助于揭示实体的逻辑目的及其关系(只要它是自然的而不是强制的)。
至于选择一个作为实际的 PrimaryKey 约束,如果您的表最终同时具有自然复合键和唯一的自动增量字段,您可以将复合键设置为 PrimaryKey 约束,但单独将自动增量字段设置为唯一聚集索引。您还可以在 PrimaryKey 上创建非聚集索引。(从技术上讲,您还可以将复合键设置为聚集索引,并在自动增量字段上执行唯一的非聚集索引。)这样您就可以在任一键上进行高性能连接。