数据库规范化 - 谁是对的?

时间:2012-05-26 18:21:10

标签: sql database normalization redundancy

我的教授(声称多年来对系统开发有了深刻的理解),我正在争论数据库的设计。

举个例子: 我的教授坚持认为这个设计是对的: (列表)

Subject_ID
Description
Units_Lec
Units_Lab
Total_Units

等...

注意总单位列。他说必须包括这个专栏。 我试图解释这是不必要的,因为如果你想要它,那么只需添加两个来进行查询。

我向他展示了我在书中找到的一个例子,但他坚持认为我不必过多地依赖书籍制作我们的系统。 同样的情况适用于与此类似的案例:

student_ID
prelim_grade
midterm_grade
prefinal_grade
average

等...

他希望我包含平均值!无论我走到哪里,我都会发现自己在阅读那些让我相信这违反了规范化的文章。如果我需要平均值,我可以轻松计算三个等级。他列举了一些场景,包括('嘿!如果查询被意外删除怎么办?你会做什么?这就是你需要把它包含在你的桌子里的原因!')

我是否需要重建我的数据库(包含大约40多个表)才能符合他的要求?我错了,只是忽略了这些事情?

修改

另一件事是他想在支付表中包括总金额,我认为这是不必要的(只需计算产品的单价和数量)。他指出,我们需要该列来计算对整个系统管理至关重要的借方和/或贷方,这是平衡交易所需要的。请告诉我你的想法。

7 个答案:

答案 0 :(得分:12)

你是完全正确的!规范化的一个规则是减少那些可以通过使用其他属性的值容易推导出的属性。即,通过执行一些数学计算。在您的情况下,只需添加即可获得总单位列。

告诉你的教授,拥有那个特定的专栏会显示​​出明确的传递依赖性迹象,并且根据第3个规范化规则,建议减少那些。

答案 1 :(得分:12)

当你说你的解决方案更加规范化时,你是对的。

但是,有一种称为非规范化(google for it)的内容,它故意违反规范化规则以提高查询效果。

例如,您希望通过减少数量或总单位来检索前五个主题(无论是什么)。

您的解决方案需要对两个表(subjectunit)进行全面扫描,加入结果集并对输出进行排序。

您教授的解决方案只需要从total_units的索引中获取前五条记录。

这当然是以增加维护成本(计算资源和开发方面)为代价的。

我无法告诉你谁是#34;对"这里:我们对项目本身,数据量,要进行的查询等一无所知。这是一个需要为每个项目做出的决定(对于某些项目,它可能是一个核心决策)。

问题在于教授确实有这个要求的理由,这可能是也可能不是。

为什么他自己没有向你解释上述所有内容,这是另一个问题。

答案 2 :(得分:6)

除了redskins80的好答案之外,我想指出为什么这是一个坏主意:每次需要更新其中一个源列时,您还需要更新计算列。这是更容易包含错误的工作(也许1年后,当不同的程序员改变系统时)。

也许您可以使用计算列代替?那将是一个可行的中间立场。

编辑:非规范化有其位置,但它是采取的最后一项措施。这就像化疗一样:医生只会给你注射毒药,以对你的健康造成更大的威胁。这是最后一步。

答案 3 :(得分:6)

认为添加此内容非常重要,因为当您看到问题时,我认为答案并不完整。最初的问题得到了很好的回答,但这里有一个小问题。所以我只考虑下面引用的补充问题:

  

另一件事是他希望将总金额包括在内   付款表,我认为是不必要的(只需计算单位   产品价格和数量。他指出我们需要   用于计算对其来说至关重要的借记和/或信用的列   整体系统管理,它是平衡所需要的   交易。请告诉我你的想法。

此编辑很有趣。基于事实,这是一个处理金钱的交易系统,它必须是负责任的。我采取了一些基本术语:交易,产品,价格,数量。

从这个意义上说,非常规甚至需要非规范化。为什么?因为你需要它负责。因此,当事务被注册时,它可能永远不会被修改。如果您需要更正它,那么您再进行一次交易。

现在是的,您可以计算出例如产品价格*金额*税金等。这在标准化意义上是有意义的。但是,您需要完全锁定所有相关记录。因此,例如产品表:如果您在交易之前更改价格,则应在交易发生时将其考虑在内。但如果之后价格发生变化,则不会影响交易。

因此,加入transaction.product_id = products.id是不可接受的,因为该产品可能会发生变化。例如:

2012-01-01 price = 10
2012-01-05 price = 20
Transaction happens here, we sell 10 items so 10 * 20 = 200
2012-01-06 price = 22

现在我们在2012-01-10查找交易,所以我们这样做:

SELECT 
    transactions.amount * products.price AS totalAmount 
FROM transactions 
INNER JOIN products on products.id=transactions.product_id

这会给10 * 22 = 220,所以它不正确。

所以你有两个选择:

  1. 不允许对产品表进行更新。因此,您对该表进行了版本化,因此对于每条记录,您都添加了一个新的INSERT而不是更新。因此,交易始终指向正确的产品版本。

  2. 或者您只需将字段添加到事务表中。因此,将totalAmount添加到事务表并在插入事务时计算它(在数据库事务中)并保存它。

  3. 是的,它是非规范化的,但它有充分的理由,它使它负责。您只知道并且已通过交易,锁定等验证交易发生的时刻与所描述的产品相关且价格= 20等。

    接下来,无论如何,当你必须这样做时,这只是非规范化的一件好事,它很容易运行报告。总交易金额的月份,年份等。这很容易计算。

    规范化有好处,例如没有双重存储,单点编辑等。但在这种情况下,你只是不想要这个概念,因为这是不允许的,而不是首选的事务日志数据库。

    将交易视为现实世界中发生的事情的注册。它发生了,你写下来了。现在你无法改变历史,它是按原样写的。未来不会改变它,它发生了。

答案 4 :(得分:1)

如果你想实现好的,旧的,经典的关系模型,我认为你所做的是正确的。

总的来说,这实际上是一个哲学问题。一些系统,Oracle就是一个例子,甚至允许你放弃传统的关系模型,而不是对象,它(通过保存在表中的复杂结构)违反了第一个NF但是给你了面向对象模型的强大功能(你可以使用继承,覆盖方法等),这在某些情况下非常棒。使用的语言仍然是SQL,只能扩展。

我知道我的答案偏离了主题(因为我们考虑了一种全新的数据库类型),但我认为在一个非常普遍的问题上分享是一件有趣的事情。

实际应用程序的数据库设计几乎不是要制作哪些表格的问题。目前,在保存和处理数据方面有无数种可能性。我们都知道和喜欢关系系统,对象数据库(如db4o),对象关系数据库(不要与对象关系映射混淆,我的意思是Oracle 11g及其对象等工具),xml数据库(带eXist) ,流数据库(如Esper)和当前蓬勃发展的noSQL数据库(有些人坚持认为它们不应该被称为数据库),如MongoDB,Cassandra,CouchDB或Oracle NoSQL

如果出现其中一些情况,正常化会失去意义。每种型号都有完全不同的用途。我认为“数据库”这个术语的含义比以前更广泛。

谈到关系数据库时,我同意你而不是教授(虽然我不确定强烈反对他是不是一个好主意)。

现在,到了这一点。我认为你可以通过表明你是开放的,并且你明白有很多选择需要考虑(包括他的观点)来赢得他,但这种情况要求你规范化数据。

我知道我的回答对于stackoverflow帖子来说是一种良心,但我希望它不会像疯子一样被接受。

在关系拔河比赛中祝你好运

答案 5 :(得分:1)

您在这里谈论历史和财务数据。存储一些永远不会改变的计算是很常见的,因为这是当时收取的成本。如果您从产品*价格进行计算并且交易后6个月价格发生变化,则您的值不正确。你的教授聪明,听他说。此外,如果您在数据库中进行大量报告,则不希望经常计算在没有其他数据输入记录的情况下不允许更改的值。当您只需要执行一次时,为什么要在应用程序的历史记录中多次执行计算?这浪费了宝贵的服务器资源。

答案 6 :(得分:0)

规范化的目的是消除冗余,以消除更新异常,主要是在事务系统中。到目前为止,Relational仍然是事务处理,DW,主数据和许多BI解决方案的最佳解决方案。大多数NOSQL都具有低完整性要求。所以你丢失了我的推文 - 烦人但不是灾难性的。但要失去我百万美元的股票交易是个大问题。选择不是NOSQL与关系。 NOSQL做得非常好。但是Relational不会去任何地方。它仍然是面向事务,面向更新的解决方案的最佳选择。当数据是只读或大部分读取时,可以放松对规范化的要求。这就是为什么冗余在DW中不是一个巨大的问题;没有更新。