归一化:是否可以将静态数字(如年份)拆分到自己的表中?


16

我正在与另一位数据库设计师就标准化进行有趣的讨论。在此示例中,我们有一个GameTitles表,每个记录必须包含游戏发布的年份。他说2NF要求必须对所有内容进行规范化,因此,为了合规,年份字段应拆分为带有自己主键的ReleaseYears表,该主键由GameTitles表引用。我说它应该保留为GameTitles表本身上的一个字段。

我对此的论点是,一年只是一个非原始的数值,从本质上说是静态的(即2011年始终为2011年)。因此,它是它自己的标识符,因为它就是它,所以不需要引用它。这还引入了额外的维护,因为您现在必须在表中添加一个新的年份以供参考。如果您在表中使用大范围的年份进行填充,那么您将拥有多余的记录,这些记录可能根本不会引用它们。这也增加了数据库的大小,因为您现在有了额外的表,记录开销以及年份本身的额外主键。如果将年份保留为GameTitles表中的字段,则可以消除所有这些额外的维护和开销。

有这个想法吗?

编辑:打算将此发布在StackOverflow上。有人可以投票删除它或举报以引起注意吗?


6
为什么这样?似乎很适合这里。
Leigh Riffel

我想问的问题是您是在问标准化或实际生产需求吗?对于生产,我会问这是否是正确的事情?
jcolebrand

Answers:


14

另一个数据库设计者完全是错误的,但是您的推理也是错误的。假设您从此表开始,该表具有单个候选键“ game_title”。

Table: game_titles

game_title                      year_first_released
--
The first game                  1998
The second game                 1999
Best game: the third one        2001
The fourth game                 2003
Forty-two, the end of games     2011

您可以通过问自己这些问题来评估它是否在2NF中。

问:首先是1NF吗?

答:是的。

问:主要属性是什么(属于候选关键字的属性)?

答:“ game_title”是唯一的主要属性。

问:什么是非主要属性?

答:“ year_first_released”是唯一的一个。

问:“ year_first_released”在功能上取决于整个“ game_title”,还是仅取决于其中一部分?

答:唯一的候选键“ game_title”是一列;它甚至没有零件。因此,“ year_first_released”在功能上取决于整个“ game_title”。

Voilà。您已找到2NF。

您可以先询问是否使用1NF,然后再回答此问题,以简化一些正式用语。

问:是否有任何复合候选密钥?

答:不可以。

Voilà。您已再次找到2NF。

根据定义,一个表要违反2NF,它必须至少具有一个具有多列的候选键。

这是您拒绝朋友意见的原因。

  • 一年只是一个非原始数字值。
  • 一年本质上是静止的。
  • 一年作为其自己的标识符。
  • 几年来介绍了额外的维护。
  • 年份表可能包含未引用的额外行。
  • 几年的表增加了数据库的大小。

这些原因均与表是否位于2NF中没有任何关系。

在设计数据库时,考虑维护问题,数据库大小,未引用的行,范围约束等是正确的。称这些为标准化是错误的。

哦,那是我上面提供的两列表格,它在5NF中。


2
做得很好。我很想发布一个答案,说除了第一句话外没有什么……“其他数据库设计者完全是错误的”,您已经很好地解释了原因。
Mark Storey-Smith

5

为任何属性创建单独的表与规范化无关。2NF,3NF,BCNF,4NF,5NF都与消除非密钥依赖关系有关。如果您将任何单个属性删除到新表中并用外键属性替换,则该表中的依赖关系在逻辑上将与以前相同-因此该表的修订版不会比其标准化或多或少地标准化以前。


我想为此添加一些内容,但是不确定。您是说,将某物移动到具有1:1相关性的表中(在这种情况下,将1个键精确地转换为1个值,或者将一行移动到一行)对于不需要查找没有好处,对吗?但是,如果您很少需要年份,并且只查看255年或更短的时间,则有潜在的查找益处。可以想象这里可以节省几个字节,但是由于通常这些字节总是以4字节分配的,因此这不是一个合理的假设。
jcolebrand

1
@jcolebrand:同意您的意见。问题的答案仍然是相同的:您是否这样做与规范化本身无关。
nvogel

我同意。就像我说的那样,我的想法有点三心二意。“我觉得OP在这里丢失了一些东西”……因为我不确定该概念在哪里。
jcolebrand

5

从我的角度来看,只有当“发行年”不是日历年,而是例如可能跨越多个日历年(例如,从十月到十月)的财政年度时,单独的年表才有意义。

然后,该表将保存会计年度的定义(实际开始日期和结束日期)


1
+1您仅需要一个具有属性的表即可:)
杰克说请尝试topanswers.xyz 2011年

2

http://en.wikipedia.org/wiki/Second_normal_form

当且仅当在给定任何候选密钥K和非候选密钥组成的任何属性A的情况下,A依赖表都在2NF中,A依赖于整个K而不只是其中一部分。

您没有指出年份是否是候选关键字的一部分,但是我不确定这是否重要,因为无论哪种情况,只要涉及年份,都将满足2NF的要求。

在实践上,出于列出的所有原因,分开年份是个坏主意。


2

我不喜欢针对单独表的参数,因为它的大小很大,否则它将有未使用的行。即使您将此表放入1000年,其大小也可以忽略不计。

就是说,我认为根本不需要这张桌子。在一年中有单独的表格有什么意义?此数据已在主表中,并且通过创建第二个表绝对不会保存任何内容。

对于日历表,该参数可以有所不同,其中每一行代表一天,并且可以具有其他属性(星期几,UTC偏移量,是否为假期等)。

但是一年呢?不,我一点也没有看到任何好处。正如其他人指出的那样,问他们为什么他们认为这更加标准化了?还是他们获得了什么?如果您尝试编写类似

WHERE othertable.year = 2011

代替

WHERE dt >= 20110101 AND dt < 20120101

然后,我会说服您后者在性能(假设dt已建立索引)和存储方面要好得多。如果简单的编码是最重要的,那么我想说一个持久的计算列会比另一个表更好。


1

除了一点,我完全同意Catcall的回答:“年份”可能并不总是原始值,但是我想这更多的是业务逻辑概念,而不是数据库设计概念。

保持相同的设计,让我们假设年份仅应为允许发布的年份。这样,您不是在处理原始数值,而是处理它们的一个子集,并且由于此类子集没有原始实现,因此您必须自己做一个(单独的表?)并引用它(带有FK)。这样,我们仍然在谈论几年,但是我们需要以不同的方式来管理它们,因为它们从概念上改变了它们的含义。但是,它们仍然是“发布年份”,但是在概念上对领域知识人员的意义不同。

对于这种特定情况,我再次说Catcall的答案是正确的,但只是想指出这一点。(对不起,没有足够的代表发表评论。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.