何时去规范数据库devise
我知道在Stack Overflow上已经广泛地讨论了normalis(z)。 我读过很多以前的讨论。 我还有一些额外的问题。
我正在使用至less有100个表的遗留系统。 数据库有一些不规范的结构,包含各种不同数据的表和其他问题。 我已经被赋予了改善它的任务。 我不能只是重新开始,但需要修改现有的架构。
在过去,我一直试图devise规范化的数据库。 现在的问题。 一位高级开发人员build议,在某些情况下,我们无法正常化:
1)具有时间数据。 例如,创build一个链接到产品的发票。 如果客户一年后要求提供此发票的复印件,我们必须能够生成原件的完整副本。 如果产品价格,名称或说明已更新,该怎么办? 这位资深人士build议将价格和其他产品信息复制到发票表中。 我想也许我们应该有另一个表,如productPrice有一个date字段,所以我们可以跟踪价格的变化随着时间的推移。 我想在产品描述和名称上需要同样的东西? 看起来很复杂。 你怎么看?
2)数据库是一个会计系统。 我对会计不是很熟悉。 目前一些汇总数据被导出并存储在数据库中。 例如,一年的总销售额。 我的高级助理指出,会计师喜欢检查的东西是正确的,通过比较这个价值与实际上从发票计算的数据,让他们有信心,应用程序是否正常工作。 他说,目前例如我们可以判断是否有人错误地删除了去年的发票,因为总数不会一样。 他还指出,在飞行中计算这些总数可能相当慢。 当然我说过,数据不应该重复,应该在需要的时候一直计算。 我build议我们可以使用SQL Reporting Services或其他解决scheme来生成这些报告并caching它们。 无论如何,他不相信。 对此有何评论?
非常感谢 :)
干杯
标记
编辑
感谢您的优秀回应。 可惜我只能标记一个作为答案,因为这里有很多好的build议。
您的高级同事是开发人员,而不是数据build模人员。 你最好从头开始,没有他们。 对于那些不会读书的人来说,规范化是复杂的,并且从维基的业余爱好者处获得他们的“知识”。 他让你思考是公平的,但有些问题是荒谬的。
你的号码:
-
您需要了解实际在线数据和历史数据之间的差异; 那么仅仅是历史和档案需求之间的区别。 所有这些都适合具体的业务需求,对其他所有人都是错的,没有普遍的是非。
- 为什么没有发票的纸质副本? 在大多数国家,这将是一个法律和税收的要求,究竟是什么困难捞出旧发票?
- 如果数据库要求存储已closures的发票,那么一旦发票closures,您就需要一种捕获该信息的方法。
-
ProductPrice
(实际上,我将其称为ProductDate
)是一个好主意,但可能不是必需的。 但是你是对的,你需要在整个数据库的整个上下文中评估数据的货币。 - 我看不到如何将产品价格复制到发票表格中会有帮助(没有多less订单项?)
- 在现代数据库中,发票的副本需要反刍,封闭的发票另外以不同的forms存储,例如XML。 一位客户将PDF保存为BLOB。 所以五年前的产品价格是没有的。 但基本发票数据是在线的和即时的,即使是closures的发票; 您只能使用当前价格重新计算古代发票。
- 有些人使用archive_invoice表,但是有问题,因为现在每个代码段或用户报告工具必须在两个地方查找(注意,现在有些用户比大多数开发人员更了解数据库)
- 无论如何,这是所有的讨论,为了您的理解。 我30年来所写的数据库都没有遇到过这样的问题,都符合法律和税务方面的要求。
- 该数据库从一组表中提供当前和归档的目的(没有“归档”表)
- 一旦创build发票,这是一个合法的文件,不能被改变或删除(它可以被反转或部分记入一个新的发票,负值)。 它们被标记为
IsIssued/IsPaid/Etc
-
Products
不能被删除,他们可以被标记为IsObsolete
- InvoiceHeader和InvoiceItem有单独的表
-
InvoiceItem
对于InvoiceHeader
和Product
都具有FK - 出于许多原因(不仅仅是你提到的那些),InvoiceItem行包含
NumUnits; ProductPrice; TaxAmount; ExtendedPrice
NumUnits; ProductPrice; TaxAmount; ExtendedPrice
NumUnits; ProductPrice; TaxAmount; ExtendedPrice
。 当然,这看起来像一个“非正规化”,但不是,因为价格,税率等可能会发生变化。 但更重要的是,法定要求是我们可以按要求复制旧的发票。 - (可以从纸质文件中复制,这不是必需的)
-
InvoiceTotalAmount
是派生列,只是InvoiceItems的SUM()
。
-
那是垃圾。 会计系统和会计师不像那样“工作”。
-
如果它是一个真正的会计系统,那么它将有日记条目,或“复式”; 这是一个合格的帐户需要使用(依法)。
- 双重入场并不意味着重复入场; 这意味着每笔金融交易(一笔金额)都应有一个适用的来源账户和目标账户; 所以没有“去标准化”或重复。 在银行数据库中,因为金融交易是针对单个账户的,所以通常在一个Db交易中作为两个单独的金融交易(行)呈现。 普通的商业数据库约束被用来确保每个金融交易都有两个“方面”。
。
- 双重入场并不意味着重复入场; 这意味着每笔金融交易(一笔金额)都应有一个适用的来源账户和目标账户; 所以没有“去标准化”或重复。 在银行数据库中,因为金融交易是针对单个账户的,所以通常在一个Db交易中作为两个单独的金融交易(行)呈现。 普通的商业数据库约束被用来确保每个金融交易都有两个“方面”。
-
确保发票不可删除是一个单独的问题,与安全性等有关,如果有人对其数据库中的东西被删除有偏见, 而且他们的数据库不是由合格人员担保的,则他们有更多不同的问题与这个问题无关。 获得安全审计,并做他们告诉你的任何事情。
-
这个网站上有几个人认为wiki是一个你可以学到东西的地方。 事实并非如此。 它是业余爱好者写下的“定义”的底色,而“定义”也是其他业余爱好者不断变化的。 没有固定的定义,你可以依靠。 所以,不要担心维基说什么或者维基说什么人,当他们提到维基时,你知道他们的“知识”来自于阅读而不是资格; 他们正在阅读的是一个不断变化的垃圾坑。 他们会预测性地争论“定义”,因为他们没有实际的经验; 有经验的人才会继续工作
-
规范化数据库总是比非规范化数据库快得多。 所以了解Normalization和Denormalisaion是什么,什么不是什么是非常重要的。 当人们有stream动性和业余性的“定义”时,这个过程受到很大的阻碍,这只会导致混乱和浪费时间的“讨论”。 当你有固定的定义时,你可以避免所有这些,然后继续工作。
-
汇总表是非常正常的,以节省时间和处理能力,重新计算不会改变的信息,例如:今年的年度累计总数,但今年; 今年每个月的MTD总计,但不是这个月。 (a)信息非常大且(b)不变时,“总是重新计算”数据有点愚蠢。 仅计算当月
- 在银行系统(每天数百万笔交易)中,我们计算并存储每日总计。 在过去的五天内,这些被覆盖,因为听众正在做出改变,最近5天的JournalEntries针对财务交易是允许的。
- 非银行系统一般不需要每日总计
。
-
汇总表并不是一种“非常化”(除非那些刚刚从其神奇的,不断变化的stream体“来源”中了解到“正常化”的人的眼中,还是作为非实践者应用简单的黑白规则到一切)。 同样,这里的定义也没有争论。 它只是不适用于汇总表。
-
汇总表不影响数据完整性(当然,假设它们来自的数据是不可或缺的)。
-
汇总表是对数据库的补充 ,不需要与数据库具有相同的约束。 基本上有报表或数据仓库表,而不是数据库表。
-
没有更新exception(这是一个严格的定义)与汇总表相关。 您无法更改或删除去年的发票。 更新exception适用于真正的非规范化或非规范化的当前数据。
-
1)这是一个档案。 所有的东西都不应该被更新。 我会和高级人员的build议一起去,让这个发票表是独立的。 也许使用包含标记语言的发票本身的blob?
2)报告服务,一个触发更新的仓库表,你用脚本创build的东西,只要…这些都是好的,我想。 这是理想的正常化,但并不总是很快。 我有一个规模很大的医疗保健数据库,我pipe理这个数据库是完全正常化的…然后有一系列非正规化的表格,卷起方程式和通常拉动的领域。 几乎所有的事情都是从这个非规范化的集合中运行的 – 当文件被加载的时候,用触发器追加到这些集合的速度要快得多,而不是每次我想要查看一个100,000条logging的报表时,都要从各个表中抽取数据。
你提出了有效的观点,但是你并没有完全清楚规范化的意义,例如
1)保持发票的规范化数据的说法是完全错误的。 我们以价格为例 – 如果您有一个业务需求,说明你必须保持价格的历史,那么只保持当前的价格是错误的,它打破了要求。 这与标准化没有任何关系,只是devise不好而已。 非规范化是为了引入可能性模糊到您的模型(和其他文物) – 在这种情况下,你根本不是正确地build模你的问题空间。
将数据库build模为支持时态数据(或版本化和/或将数据库的区域划分为归档/时态和工作集)没有任何错误。
不考虑语义(就需求而言)看标准化是不可能的。
另外,如果你的高级开发人员看不到差异,那么我猜他在RDBMS开发中没有得到他的资历;)
2)第二部分确实是非规范化的。 但是,如果你遇到过那些认真地鼓吹正常化的高级DB分析师,你会听到他/她说,只要你有意识地去做,并且确保超重的缺点以及exception不会引起你的反感就是完全可以接受的。 他们还会告诉你规范逻辑模型,并且在物理模型中允许你为了各种目的(性能,维护等)而偏离理想。 在我的书中,规范化的主要目的是让你没有隐藏的exception(例如参见5NF上的这篇文章)
中间结果的caching甚至可以在规范化的数据库甚至规范化的最大布道者中被允许 – 你可以在应用层(作为某种caching)执行,或者你可以在数据库级执行,或者你可以有一个数据仓库这样的目的。 这些都是有效的select,与规范逻辑模型无关。
而且,至于你的会计师 – 你应该能够说服他,他所声称的不是一个好的testing,并且开发一系列的testing(也许和他一起),在没有用户干预的情况下自动化系统testing,并给你更高的信心,您的系统是无缺陷的。
另一方面,我知道需要用户input重复信息的系统,例如在input实际行之前或之后input发票上的行数,以确保input完成。 这些数据是“重复的”,如果您有一个validationinput的过程,则不需要存储它。 如果这个过程晚些时候被允许存储“非规范化”的数据 – 再次,语义certificate它,你可以看看模型规范化。 (围绕这个概念来包装你的头是有益的)
编辑: (2)中的“非规范化”一词是不正确的,如果你看看正常forms的正式定义,如果你考虑一个devise非规范化,如果它打破任何正常forms(对某些人来说,这是显而易见的,没有其他方式)。
尽pipe如此,您可能还是想习惯这样的想法,即很多人和没有必要的无用文本都会使用术语标准化来尽力减less数据库中的冗余(例如,您会发现科学论文,我并不是说它们必须是正确的,只是作为一个警告,这是常见的,调用派生属性的非规范化forms,见这里 )。
如果你想提到一些更加连贯和公认的权威(再一次没有得到所有人的承认),也许CJDate的话可以作出明确的区分:
许多devise理论与减less冗余有关。 规范化减less了relvars内的冗余,正交性减less了relvars。
从数据库深入挖掘:从业者的关系理论
并在下一页
正如一路未能正常化意味着冗余,并可能导致某些exception,因此也可能不能坚持正交。
所以,冗余的正确术语是正交性(基本上所有的正常forms都谈论单个反向variables,所以如果严格地看正态化,它永远不会build议由于两个不同的依赖关系之间的依赖关系而产生的任何改进)。
无论如何,当您考虑数据库devise时,其他重要概念之一也是逻辑和物理数据库模型之间的区别。 很多在物理层次上有用的东西,比如带有小计或者索引的表格在逻辑模型中没有地位 – 在这里你试图build立和调查你试图build模的概念之间的关系。 这就是为什么你可以说他们是允许的,他们不会毁了devise。
在逻辑模型和物理模型中,线条有时可能会有些模糊。 特别好的例子是带小计的表格。 要将其视为物理实现的一部分,并在逻辑层面上忽略它,您必须:
- 确保用户(和应用程序)不能以与谓词不一致的方式直接更新小计表(换句话说,在小计过程中有一个错误)
- 确保用户(和应用程序)无法更新这些依赖关系的表,而不更新小计(换句话说,某些应用程序不会在不更新总数的情况下从细节表中删除一行)
如果你违反上述规则,你将会得到不一致的数据库 ,这将提供不一致的事实 。 (在这种情况下,如果你想正式devise一个修复或检查问题的程序,你不会认为它只是一个额外的表格,它将存在于逻辑层面;不应该在那里)。
此外,规范化总是取决于您试图build模的语义和业务规则。 例如,DBAPerformance给出了一个例子,其中将TaxAmount
存储在事务表中并不是非规范化的devise,但是他没有提到它取决于你想要build立什么types的系统(这是显而易见的? 例如,如果事务具有另一个称为TaxRate
属性, TaxRate
它通常会被非规范化,因为对一组非关键属性(TaxAmount = Amount * TaxRate => FD:Amount,TaxRate – > TaxAmount)存在函数依赖关系,其中一个应该被删除或保证一致。
显然,你可能会说,但是,如果你正在构build的系统是为一家审计公司工作的,那么你可能没有function上的依赖关系 – 他们可能正在审计使用手动计算的人,或者软件有问题,或者有能力logging不完整的数据而这个计算本来可能是错误的,作为审计公司,你必须logging事实的发生。
因此,由需求决定的语义(谓词)将影响任何正常forms是否被破坏 – 通过影响函数依赖性(换句话说,正确地build立函数依赖性是争取规范化数据库时build模的重要部分)。
(1)我同意你的前辈。 事务表行必须在事务处理时捕获整个状态。 期。 你所build议的不logging实际的数据,所以它是不可接受的。 我也同意(2)。 无论企业想通过交叉检查,你必须执行。 会计核算是基于交叉核对,复式记账,分级分类账等。您必须这样做。 这非常重要,你甚至不应该把它视为非规范化,就像执行业务需求一样。
1)不需要非规范化。 您只需要确定每个需要更改的详细程度,并使用适当的密钥来坚持。
2)与非规范化无关。 存储摘要数据不会使数据库非规范化。 将非关键属性派生的结果存储在同一个表中将是非规范化的一个例子,但是这似乎并不是你在这里讨论的内容。
您的高级开发人员提供了非常有效的点 我自己通过服务系统来学习这些困难的方法,这些系统不会使历史数据不规范。
从某种意义上说,它并不是真的给数据库增加任何开销。 您正在从数据库中的现有数据创build发票表。 发票是及时的快照。 取消规范生成该发票所需的信息可以使您的报告变得更容易。 当您需要提交新报告并希望尽快完成时,您将会意识到非正常化。
就数据库中的总数而言。 当我修改了一个导致数字不相同的应用程序(并不像你想象的那么难)的时候,这就保存了我的屁股。 在现场申请总数给了我一个明确的地方回来,以纠正差异。 我以前写过这个,你可以在这里阅读: http : //jlrand.com/?p=95
#1
发票应该从销售和付款中计算。 如果你没有详细的销售数据,包括价格/产品/折扣/运输/等开始那里。
对于#2
从头开始编写一个会计系统是一个很大的项目。 确保你有会计师给你的业务规则,所以你可以衡量你的系统的准确性。 你最不想要的是首席财务官进入DBA会议,并宣布数据库对客户滥收费用,更糟糕的是,你正在收取额外的费用,并使公司停业。
如果你有SQL Server,请给Adventure Works db一下。 如果你讨厌MS,那么看看Adventure Works,不要那样做。
数据库规范化删除重复项,并使数据更新的SQL查询更高效(并给出了一些其他改进)。
但是,如果大多数查询用于数据select,并且select查询同时连接到多个表,则可以考虑对这些表进行非规范化。 这将增加数据所需的磁盘空间量,SQL更新查询的时间执行,但会改善select查询。
好像你正在考虑是否应该创build一个数据仓库。 为了历史报告的目的,您不应该对数据库进行规范化。 创build档案并将您的信息存储到数据仓库将执行以下两项操作:使大多数信息非规范化并维护数据历史logging。