Sto*_*aft 2 foreign-key primary-key surrogate-key enum natural-key
我正在设计我的第一个数据库,我发现自己对为分类变量的每个实例存储整数或字符串之间的选择感到沮丧。
我的理解是,如果我有一个包含城市的表,我想将其作为国家/地区表的子级,那么最有效的方法是将国家/地区表的 PK 作为城市表中的 FK。然而,为了便于使用和调试,最好始终将字符串名称与国家/地区 PK 相关联。我考虑过的每个解决方案要么不推荐,要么看起来过于复杂。
我想了解这些方法的优点(或了解新方法),并了解是否必须如此,或者数据库是否只是因为传统而如此。
可能的方法:
使用字符串作为国家/地区的 PK。然后我将在任何子表中为其提供一个人类可读的 FK。显然,性能不如使用整数,但我怀疑这可能是获得我想要的便利的最不糟糕的方法。
使用应用程序逻辑创建一个视图,将每个国家/地区的字符串名称连接到 states 表。
enum
数据类型。出于本能,这将是我的首选方法,因为它似乎是自然键和合成键之间的理想平衡:使用整数 ID 并为 ID 提供字符串标签,以便字符串本身不需要重复。虽然 ypercube 对于 的具体示例提出了一个很好且合乎逻辑的观点Countries
,但我会避免使用基于字符串的数据类型,因为不同数据库系统对字符串所做的某些假设可能会产生潜在的意外影响。例如,在 Microsoft SQL Server 中,优化器通常假设VARCHAR
列是半满的,并将根据该假设生成请求内存的执行计划。这可能会导致为单个查询服务的内存资源分配过多(甚至不足)。我想其他数据库系统也围绕基于字符串的数据类型做出其他有趣的假设,无论好坏。
但比性能更重要的是数据准确性。表的首要任务是存储数据,并且最好准确地存储数据。主键的首要任务是建立唯一性,理想情况下它应该是不可变的。代理键的好处是,在人类可读值能够改变的情况下,确保所有这些事情都保持正确。这实际上遵循了另一个称为“单字段单一用途”的良好原则,因为代理键的含义与业务对象的含义完全解耦。
回到你的Countries
例子,a 的名称改变并不常见Country
,但也不是不可能。过去 50 年来,一些国家更名了。即使使用 ISO 代码也不能 100% 保证它永远不会改变,Country
因为这些代码的生成方式是有一定意义的(尽管与使用业务的人类可读值相比,它更多地从业务对象中删除)对象本身)。
因此,如果使用自然键值并且可能会发生变化,那么在它发生变化的那天,您现在就面临数据准确性的风险,因为您不仅需要确保表Countries
正确更新,还必须对引用的每个表执行相同的操作Countries
在外键中。
当然,与使用代理键作为主键时仅在一处更新它相比,更新引用旧值的每条记录也会带来额外的性能开销。但在我看来,更大的担忧(回到表格的主要目标)是数据准确性。
视图是统一、转换数据并将其呈现给应用程序层的绝佳工具,甚至在某些情况下(例如当您的表结构需要更改时)有助于以后的数据维护。由于视图可以充当应用程序和数据库表之间的层,因此在更改这些表的结构时应用程序的风险较小。从性能角度来看,使用它们并没有本质上的错误,并且JOIN
对于正确架构和索引的数据库来说,性能(通过代理键)不应该成为问题。
什么时候使用特殊的查找表进行 JOIN 与在父表中保留人类可读的列并让它们通过层次结构继承更好?
这取决于。由于缺乏更清晰的方式来描述它,通常当当前在其存在的主表中重复该人类可读值时,将人类可读的值重构到单独的表中是有意义的。这样就有一个地方可以唯一地定义该值可以轻松准确地进行维护。如果处理得当,这将大致遵循标准化原则。
如果“特殊查找表”是指用于多种对象的单个表(例如您的帖子提到的枚举表),我不建议这样做。它可能比多个单独的对象表更容易维护,但是您会丢失关系数据库系统的一些关系属性。
为什么代理键不能具有在所有用途中作为 FK 传播的可变字符串标签?
首先,这又回到了数据的准确性。没有什么可以阻止您这样做,这只是不是最佳实践,因为会增加数据准确性的风险,并且当您需要更新值时会使数据管理变得更加困难且性能较低。如果它是外键表中的通用值,则您将面临锁升级的风险,从而可能导致更长的等待时间并阻塞对该表的读取查询。
为什么不通过为新州创建一个新行+为存在年数创建一列来处理不断变化的国家名称?
有些人实现了这种设计,但更多的是因为他们的业务规则和用例依赖于历史数据跟踪。但对于具有标准用例的常规事务数据库,它会夸大您的数据,并且仍然无法解决上述外键引用,您也必须使用更改来更新它们或也夸大这些表。即使我有维护历史数据的用例,我个人也会将事务历史记录与活动记录存储在单独的历史表中。
归档时间: |
|
查看次数: |
892 次 |
最近记录: |