【问题标题】:Database normalization - who's right?数据库规范化 - 谁是对的?
【发布时间】:2012-06-01 21:05:57
【问题描述】:

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

例如: 我的教授坚持认为这个设计是正确的: (列列表)

Subject_ID
Description
Units_Lec
Units_Lab
Total_Units

等等……

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

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

student_ID
prelim_grade
midterm_grade
prefinal_grade
average

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

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

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

【问题讨论】:

  • 确实,您提供的是更规范化的解决方案。但请记住,您的教授会对您的解决方案进行评分。你可以以建设性的方式提出分歧。但你不应该偏离他对分歧的解决方案。
  • @redskins80 就在这里。但是应该注意的是,如果你想在他的课堂上取得好成绩,与你的教授争论太多绝不是一个好主意。不管他有多错……
  • Andomar 在这一点上是正确的。尽一切努力取得成绩。但是当你去参加工作面试时(见我在下面答案下的评论),确保你以正确的方式回答这个问题。
  • 谢谢,但是那个项目不在课堂上。我想知道我怎样才能赢得争论。
  • 我们举一个简单的例子,计算 3 个等级。但是,如果是一个更大的计算,每次查询运行需要 2-3 秒怎么办?你每天有成千上万的用户?我并不是说它应该存储在那个表中,但是 ETL 进程会定期预先计算要存储的数据以供 UI 访问,因此它不需要运行那个“快速”(或者你说“快速”)查询.

标签: sql sql-server database database-normalization redundancy


【解决方案1】:

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

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

【讨论】:

  • 同意@redskins80;在某些情况下,对数据进行非规范化是有益的,但仅在您经常需要它并且每次从其来源计算它非常昂贵的情况下作为优化。
  • 这种情况下唯一可能的例外——我想尝试让这位教授一些受益于怀疑——是如果根据系统中的业务规则,Total_Units 并不总是必须等于其他两列的总和......例如,如果 Total_Units 可以包括由不知道的教授酌情授予的奖励单位'不了解数据库规范化。 :-)
  • @KP Taylor:我必须说一个非常有效的观点!可能是可能的,除非学生必须知道是否是这种情况。否则他不会问。
  • @Arman 你也可以告诉你的教授,像我这样的人(负责管理电子商务团队)永远不会雇用像他建议的那样编写数据库表的人。那会让你在第一轮面试中被淘汰。
  • @KPTaylor:你从不反规范化你的模式?
【解决方案2】:

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

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

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

【讨论】:

  • ...或在包含 Total 列的表上创建一个视图。
  • 我不能强调 usr 有多正确!在进行数据库更新时必须保持同步的每一点信息都可能是一个等待发生的错误。
  • 更糟糕的是:每次更改一行时,都必须重新计算所有行的平均值。
  • @usr redskins80 的答案在哪里。是否已被删除。
  • @Shreyansjain 不确定,我没有看到已删除的答案。它一定是被 Stack Overflow 的工作人员硬删除的。或者,redskins80 将自己改名为@arijeet?!我赞成他的回答。
【解决方案3】:

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

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

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

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

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

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

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

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

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

【讨论】:

    【解决方案4】:

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

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

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

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

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

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

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

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

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

    【讨论】:

    • 我在开始项目之前就知道非规范化,并且认为它不会对系统有益。我教授的理由是: 1. 视图可能会被意外删除。 2.借/贷必须相应列出。(与仅查询过去的交易相反)
    • @Arman:您希望我们提供什么答案?您的解决方案是否更规范化 - 是的。您的解决方案是否更有效 - 我们不知道也无法从提供的有限数据量中分辨出来。谁是对的——“mu”。
    • 很好的答案。如果规范化就是一切,那么所有数据库都将是第 5 范式,如果不编写具有多个连接的大型 sql 查询,您将几乎无法找出程序中发生的问题。我研究过过度标准化的系统,它是一个真正的 PITA。在规范化和易用性之间有一个很好的中间地带。
    【解决方案5】:

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

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

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

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

    现在是的,您可以计算例如产品价格 * 金额 * 税金等。这在标准化意义上是有意义的。但是,您将需要完全锁定所有相关记录。以 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 所以它是不正确的。

    所以你有两个选择:

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

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

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

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

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

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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

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

        【讨论】:

        • “你的教授很聪明”?也许他很幸运,因为他恰好在这个特定问题上是对的。虽然,当然,IIRC,你可以用历史价格/产品表解决问题,并以此为基础支付总额,但这通常很麻烦,所以经常使用存储实际支付金额。跨度>
        • 关于您对报告的评论:您说得对,报告数据库通常是非规范化的,而事务性数据库是。 OP没有提到它们是设计的。如果没有更多信息,我会假设交易......
        • @reiniero,甚至事务数据库也已经完成了其中的报告。我在谈论不相关的事情;经常更改和执行这是数据库比计算它的性能更好,特别是如果计算它意味着你会得到错误的答案,因为当前价格不是收取的价格。时间数据是数据库设计中的一种特殊情况。对时态数据进行这种计算以记录交易时的收费情况并不是非规范化的。
        • 如果你想保留你的记录历史,你应该创建一个表来这样做。标准化数据库不应该有这些计算值。
        • @Andre,阅读时间数据以及如何正确处理它。此外,没有必要以变形为借口来避免重复做某事。只需要执行一次的计算应该只执行一次,特别是如果它会经常被引用。每次拉起屏幕或运行报告时都这样做是愚蠢的,尤其是当您有包含数千条记录的报告要计算时。我并不是说你应该一直这样做,而是说在某些有价值的情况下你应该这样做。
        猜你喜欢
        • 1970-01-01
        • 2012-10-08
        • 2012-06-21
        相关资源
        最近更新 更多