Mar*_*ans 48 sql database-design normalization denormalization
我知道在Stack Overflow上已经广泛讨论了normalis(z)ation.我读过很多以前的讨论.我有一些额外的问题.
我正在研究一个至少有100个表的遗留系统.数据库具有一些非规范化结构,包含各种不同数据的表以及其他问题.我被赋予了改善它的任务.我不能再重新开始,但需要修改现有架构.
在过去,我一直试图设计规范化的数据库.现在的问题.一位高级开发人员建议在某些情况下我们无法规范化:
1)使用时态数据.例如,创建了一个链接到产品的发票.如果客户在一年后要求提供此发票的副本,我们必须能够生成原件的精确副本.如果产品价格,名称或描述已更新怎么办?资深人士建议将价格和其他产品信息复制到发票表中.我想也许我们应该有另一个表,例如productPrice,它有一个日期字段,所以我们可以跟踪价格随时间的变化.我想对产品描述和名称需要相同的东西吗?看似复杂.你怎么看?
2)数据库是一个会计系统.我对会计不是很熟悉.目前,一些摘要数据被导出并存储在数据库中.例如,当年的总销售额.我的高级助理表示,会计师喜欢通过将此值与实际根据发票等计算的数据进行比较来检查事情是否正确,以使他们相信应用程序正常工作.他说,目前我们可以判断是否有人错误地从去年删除了发票,因为总数不会相同.他还指出,在飞行中计算这些总数可能会非常缓慢.当然,我说数据不应该重复,应该在需要时进行计算.我建议我们可以使用SQL Reporting Services或其他一些解决方案来隔夜生成这些报告并缓存它们.无论如何,他不相信.对此有何评论?
非常感谢:)
干杯
马克
感谢您的出色回应.遗憾的是我只能将其中一个作为答案,因为这里有很多好的建议.
Per*_*DBA 46
您的高级同事是开发人员,而不是数据建模人员.如果没有它们,你最好从头开始.规范化只对那些不会阅读书籍,并从维基百科的业余爱好者那里获得"知识"的人来说很复杂.他让你思考是公平的,但有些问题是荒谬的.
你的号码:
您需要了解实际在线数据与历史数据之间的差异; 那么仅仅是历史和档案需求之间的区别.所有这些都是针对特定业务需求的,对所有其他人来说都是错误的,没有普遍的是非.
ProductPrice(实际上,我会称之为ProductDate)是一个好主意,但可能没有必要.但是你是对的,你需要在整个数据库的完整上下文中评估数据的货币.IsIssued/IsPaid/EtcProducts 无法删除,可以标记 IsObsoleteInvoiceItem两个InvoiceHeader和都有FKProductNumUnits; ProductPrice; TaxAmount; ExtendedPrice.当然,这看起来像一个"非正规化",但它不是,因为价格,税率等可能会发生变化.但更重要的是,法律要求是我们可以按需复制旧发票.InvoiceTotalAmount是一个派生列,只是SUM()InvoiceItems的列那是垃圾.会计系统和会计师不会那样"工作".
如果它是真正的会计系统,那么它将具有JournalEntries或"double entry"; 这是一个合格的帐户需要使用(法律).
确保发票不可删除是一个单独的问题,与安全性等有关.如果有人对从数据库中删除的东西感到妄想,并且他们的数据库没有合格的人保护,那么他们就会遇到更多不同的问题.与这个问题无关.获得安全审计,并做他们告诉你的任何事情.
这个网站上有一些人认为维基是一个你可以学习的地方.事实并非如此.它是业余爱好者写的"定义"的污水池,其他业余爱好者不断改变"定义".没有可以依赖的固定定义.所以不要担心维基说什么或人们说维基说什么,他们提到维基的那一刻,你知道他们的"知识"来自阅读不合格; 而他们正在阅读的是一个不断变化的污水坑.他们可以预见地争论"定义",因为他们没有实际经验; 经验丰富的人将继续这份工作
规范化数据库总是比非规范化数据库快得多.因此,了解Normalization和Denormalisaion是什么以及它不是什么非常重要.当人们拥有流畅的和业余的"定义"时,这个过程就会受到很大的阻碍,这只会导致混乱和浪费时间的"讨论".当您有固定的定义时,您可以避免所有这些,并继续工作.
为了节省时间和处理能力,重新计算不会改变的信息,例如:每年的YTD总数,但是今年,汇总表非常正常; 今年的每月MTD总计,但不是本月.当(a)信息非常大且(b)不变时,"总是重新计算"数据有点傻.仅计算当前月份
汇总表不是"非规范化"(除了那些刚刚从他们神奇的,不断变化的流动"源"中学习"正常化"的人的眼睛;或者作为非实践者,他们应用简单的黑白规则一切).同样,这里没有讨论这个定义; 它根本不适用于摘要表.
汇总表不会影响数据完整性(当然,假设它们来自的数据是完整的).
摘要表是对数据库的补充,不需要与数据库具有相同的约束.主要是报表或数据仓库表,而不是数据库表.
没有与Summary表相关的Update Anomalies(这是一个严格的定义).您无法更改或删除去年的发票.更新异常适用于真正的非规范化或非规范化的当前数据.
1)这是一个档案.其中的所有内容都不应该更新.我会考虑老人的建议,让发票表自成一格.也许使用blob作为包含标记语言的发票本身?
2)报告服务,一个触发更新的仓库表,你用脚本构建的东西......我认为这些都可以.确实是理想化的,但并不总是很快.我有一个很好的医疗数据库,我管理的数据库是完全标准化的...然后有一系列非标准化的表格,包括卷起的方程式和通常拉取的字段.几乎所有东西都是从那个非规范化的集合中运行的 - 加载文件时用触发器附加到这些文件比用每次我想要查看100,000条记录报告时必须从各种表中拉出来更快.
你提出了有效点,但你并不完全清楚规范化及其含义,例如
1)声称将发票保持为非正规化数据是完全错误的.让我们以价格为例 - 如果您的业务要求表明您必须保留价格历史,那么只保留当前价格是错误的并且它违反了要求.它与标准化无关,它设计得不好.非规范化是指在模型(和其他工件)中引入歧义的可能性 - 在这种情况下,您根本就不能正确地对问题空间进行建模.
在建模数据库以支持时态数据(或版本化和/或将数据库区域分为存档/时间和工作集)方面没有任何错误.
在不考虑语义(在需求方面)的情况下查看规范化是不可能的.
此外,如果您的高级开发人员无法看到差异,那么我猜他没有在RDBMS开发中获得资历;)
2)第二部分确实是非规范化.但是,如果你曾经遇到过认真宣扬规范化的高级数据库分析师,你会听到他/她说,只要你有意识地做到这一点,并且确保超重缺陷的好处以及异常不会让你感到厌恶,那么非规范化是完全可以接受的.他们还会告诉您规范化逻辑模型,并且在物理模型中,您可以出于各种目的(性能,维护等等)偏离理想模型.在我的书中,规范化的主要目的是让你没有隐藏的异常(例如参见5NF上的这篇文章)
即使在规范化的数据库上也允许缓存中间结果,甚至是规范化的最大传播者 - 你可以在应用层(作为某种缓存)进行缓存,或者你可以在数据库级别进行,或者你可以拥有一个数据仓库这样的目的.这些都是有效的选择,与规范化逻辑模型无关.
此外,至于你的会计师 - 你应该能够说服他,他所声称的不是一个好的测试并开发一套测试(可能与他一起),这将在没有用户干预的情况下自动化测试系统并给你更高的信心,您的系统是无bug的.
另一方面,我知道需要用户输入重复信息的系统,例如在输入实际行之前或之后输入发票上的行数,以确保输入完成.此数据是"重复的",如果您有一个验证输入的过程,则不必存储它.如果该程序稍后出现,则允许存储"非规范化"数据 - 再次,语义证明它是合理的,您可以将模型看作规范化.(包围这个概念是有益的)
编辑: 如果你看正常形式的正式定义,如果你认为一个设计非规范化,如果它破坏任何正常形式(对某些人来说,这很明显,没有),那么(2)中的术语"非规范化"是不正确的关于它的其他方式).
尽管如此,你可能还是想要习惯这样的想法:很多人而不是必要的无用文本会使用术语规范化来试图减少数据库中的冗余(例如,你会找到科学论文,通过我并没有说它们必须是正确的,正如警告它是常见的那样,调用派生属性是一种非规范化的形式,见这里).
如果你想引用一些更连贯和认可的权威(再次,并非所有人都认可),也许CJDate的话可以明确区分:
许多设计理论都与减少冗余有关; 规范化减少了relvars中的冗余,正交性减少了relvars中的冗余.
并在下一页
正如未能一直正常化意味着冗余并且可能导致某些异常,因此也不能坚持正交性.
因此,跨越relvars的冗余的正确术语是正交性(基本上所有正常形式都谈论单个relvar,因此如果你严格看待规范化,它将永远不会建议由于两个不同relvars之间的依赖性而导致的任何改进).
无论如何,当您考虑数据库设计时,其他重要概念之一也是逻辑和物理数据库模型之间的差异.在物理层面上有很多有用的东西,例如带有小计或索引的表格在逻辑模型中没有位置 - 您尝试建立和调查您尝试建模的概念之间的关系.这就是为什么你可以说它们是允许的并且它们不会破坏设计.
线条有时可能在什么是逻辑模型和什么是物理模型上有点模糊.特别好的例子是带小计的表格.要将其视为物理实现的一部分,并在逻辑级别上忽略它,您必须:
如果您违反上述任何规则,您将最终得到不一致的数据库,这将提供不一致的事实.(在这种情况下,如果你想正式设计一个程序来修复或检查所引起的问题,你不会认为它只是一个额外的表,它将存在于逻辑层面;它不应该存在).
此外,规范化始终取决于您尝试建模的语义和业务规则.例如,DBAPerformance提供了一个示例,其中TaxAmount在事务表中存储不是非规范化设计,但他没有提到它取决于您尝试建模的系统类型(这是显而易见的吗?); 例如,如果事务具有另一个被称为属性的属性,TaxRate那么它通常会被非规范化,因为对一组非关键属性存在功能依赖(TaxAmount = Amount*TaxRate => FD:Amount,TaxRate - > TaxAmount),其中一个应该是被删除或保证一致.
显然,您可能会说,但是,如果您构建的系统是针对审计公司的,那么您可能没有功能依赖性 - 他们可能正在审核使用手动计算或软件有缺陷或必须能够记录不完整数据的人员并且最初的计算可能是错误的,作为审计公司,您必须记录事实.
因此,由需求确定的语义(谓词)将影响是否有任何正常形式被破坏 - 通过影响功能依赖性(换句话说,正确建立功能依赖性是建立规范化数据库时建模的非常重要的一部分).
我同意你的前辈关于(1)的看法。事务表行必须捕获事务时刻的整个状态。时期。你的建议没有记录实际数据,所以它是不可接受的。我也同意(2)。无论企业通过交叉检查的方式想要什么,您都必须实施。会计基于交叉检查、复式记账、汇总分类账等。您必须这样做。这是非常基本的,您甚至不应将其视为非规范化,就像实现业务需求一样。
1) 不需要非规范化。您只需要确定您需要的每个更改的详细程度,并使用适当的键保持它。
2)与非规范化无关。存储摘要数据不会使数据库非规范化。将非关键属性派生的结果存储在同一个表中将是非规范化的一个例子,但这似乎不是你在这里谈论的内容。
小智 5
您的高级开发人员提出了非常有效的观点。我自己通过服务不会对历史数据进行非规范化的系统来艰难地学到了这些。
从某种意义上说,它并没有真正给数据库增加任何开销。您正在从数据库中的现有数据创建发票表。发票是时间的快照。对生成发票所需的信息进行非规范化可以使您的报告变得更加容易。当您需要生成新报告并希望快速完成时,您会喜欢去规范化。
就数据库中的总数而言。之前当我对一个应用程序进行更改导致数字的加法方式不同(不像您想象的那么难)时,这已经救了我的屁股。在实时应用程序中,总数为我提供了一个明确的返回位置,以纠正差异。我以前写过这个,你可以在这里阅读:http : //jlrand.com/?p=95
| 归档时间: |
|
| 查看次数: |
24730 次 |
| 最近记录: |