15

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

举个例子:我的教授坚持这个设计是正确的:(列列表)

Subject_ID
Description
Units_Lec
Units_Lab
Total_Units

ETC...

注意总单位栏。他说这个栏目必须包括在内。我试图解释它是不必要的,因为如果你想要它,那么只需将两者相加即可进行查询。

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

student_ID
prelim_grade
midterm_grade
prefinal_grade
average

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

我是否需要重建我的数据库(由大约 40 多个表组成)以符合他的要求?我错了,只是忽略了这些事情吗?

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

4

7 回答 7

13

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

告诉您的教授,拥有该特定列将显示传递依赖的明显迹象,并且根据第三规范化规则,建议减少这些。

于 2012-05-26T18:24:22.253 回答
13

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

但是,有一种叫做非规范化(google for it)的东西,它是关于故意违反规范化规则以提高查询性能。

例如,您想检索按减少数量或总单位排序的前五个主题(无论是什么)。

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

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

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

我不能告诉你谁是“正确的”:我们对项目本身、数据量、要进行的查询等一无所知。这是每个项目都需要做出的决定(对于某些项目,它可能是核心决定)。

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

为什么他没有亲自向您解释以上所有内容,这是另一个问题。

于 2012-05-26T19:42:34.507 回答
6

除了 redskins80 的出色回答,我想指出为什么这是一个坏主意:每次您需要更新其中一个源列时,您也需要更新计算列。这是更多可以轻松包含错误的工作(可能在 1 年后,当不同的程序员正在更改系统时)。

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

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

于 2012-05-26T18:37:15.410 回答
6

认为添加此内容很重要,因为当您看到问题时,我认为答案并不完整。原始问题已得到很好的回答,但这里有一个小故障。所以我只考虑下面引用的附加问题:

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

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

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

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

因此,仅加入 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 所以这是不正确的。

所以你有2个选择:

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

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

是的,它是非规范化的,但它有充分的理由,它让它负责。您只知道并通过交易、锁等验证了交易发生的那一刻,它与所描述的产品相关,价格 = 20 等。

除此之外,当您无论如何都必须这样做时,这只是非规范化的一件好事,运行报告非常容易。月、年等总交易金额,计算起来非常简单。

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

将交易视为对现实世界中发生的事情的登记。它发生了,你把它写下来。现在你不能改变历史,它是按原样写的。未来不会改变它,它发生了。

于 2012-07-13T13:03:01.707 回答
1

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

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

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

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

在其中一些情况下,规范化失去了意义。每个模型都有完全不同的目的。我认为“数据库”一词的含义比以前要广泛得多。

谈到关系数据库,我同意你的观点,而不是教授的观点(尽管我不确定强烈反对他是否是个好主意)。

现在,说到重点。我认为你可以通过表现出你的思想开放并且你理解有很多选择需要考虑(包括他的观点)来赢得他,但是这种情况需要你对数据进行规范化。

我知道我的回答对于 stackoverflow 帖子来说是良心之流,但我希望它不会被视为疯子喋喋不休。

在关系拉锯战中祝你好运

于 2012-05-26T19:26:04.450 回答
1

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

于 2014-01-14T23:04:46.243 回答
0

规范化的目的是消除冗余,从而消除更新异常,主要是在事务系统中。迄今为止,关系仍然是事务处理、DW、主数据和许多 BI 解决方案的最佳解决方案。大多数 NOSQL 具有低完整性要求。所以你失去了我的推文——烦人但不是灾难性的。但是失去我的百万美元股票交易是一个大问题。选择不是 NOSQL 与关系。NOSQL 在某些事情上做得很好。但是关系不会去任何地方。它仍然是面向事务的、面向更新的解决方案的最佳选择。当数据为只读或多读时,可以放宽规范化的要求。这就是为什么冗余在 DW 中不是一个大问题的原因。没有更新。

于 2014-01-14T22:39:49.447 回答