2011-02-12 82 views
5

面向对象设计鼓励使用不可变对象来提高线程安全性和性能。我想知道这是否继承关系数据库。数据库:插入新行或更新现有行?

我最好更新现有的行,或插入新的行为作为覆盖?

  • 用例
    • 每位员工恰好与一个公司相关联
    • 员工改变自己的公司随着时间的推移。
    • 员工姓名应该是唯一的。
  • 架构
    • 员工[姓名,公司名称]

选项1:每次员工更改公司,插入一个新的雇员[姓名,公司名称]行。指示应用程序跳过较旧的行(随着时间推移在后台线程中修剪)。 选项2:每当员工更换公司时,更新现有的行。

选项1让我想起了不可变的对象,因为它是线程安全的(不需要锁定)。另一方面,员工每次更换公司时,都必须克隆所有相关对象并将其指向新记录。此外,不清楚如何防止重复员工被错误地创建。

选项2可以很容易地防止重复员工,但是在READ_COMMITTED事务隔离中返回可能不一致的关联的缺点。

回答

4

我发布这个希望,这有助于他人在未来。我个人浪费了无数天在这条(错误的)路上走下坡路。

不可变对象的意思是价值类型(认为是整数,时间戳,温度读数等)。他们是永远不会改变的类型。当你开始讨论修改不可变对象的值时,这是一个非常强烈的迹象表明你正在走错路。当你使用真正的不可变对象时,你不应该更新关联对象的引用。

因此,无论是面向对象编程还是数据库设计,正确答案都是更新可变对象。

UPDATE:marc_s提到了一些事实,即某些系统需要不可变的审计线索。我建议将系统分成两部分。主表在更新数据的同时将副本插入单独的审计表。这有两个优点:

  1. 主表可以利用完整性检查(即“员工姓名必须是唯一的”)。
  2. 主表保持读取速度非常快,随着时间的推移可以修改较大/较慢的审计表。

这让你享受两全其美。

1

的基本区别在于:

  • 如果插入每个变化的新行,如通过设置“ValidTo”日期来“关闭”旧的行,那么你就会知道随着时间的推移,你将进入“时间”数据库区域的变化的历史记录。一遍又一遍的同一行,那么你总是有现在的状态 - 但不是历史。

所以我想,这真的是一个大问题:你需要时间信息,例如能够“回到时间”,现在你的数据状态是三个月前的状态?如果是这样,选项1(包括“软删除” - 只将行标记为已删除,不实际删除它们)是您唯一的选择。显然,缺点是增加了复杂性和更多的存储需求。

+1

这是一个很好的观点。可审计系统需要不可变的历史记录。话虽如此,难道你不能将系统分为实时数据和历史数据吗?实时数据将就地更新(简化完整性检查)并将副本插入审计表。 – Gili 2011-02-12 22:22:04

+0

如果您经常使用时态信息并及时回溯,您可能需要考虑新文档数据库之一(来自NoSQL世界)。其中一些明确支持鼓励只插入设计,通过优化他们的数据存储来准确地使用这种用例。这使插入*更快*并可以改善报告。常见的关系型数据库在改变现有数据方面效果更好,并且对于大型数据集越来越慢(当数据集只包含死/过时数据时,这通常是不需要的)。 – 2011-02-12 22:22:32

+0

我想我只是重复你的答案marc_s。没有打算使用plagarism。 – XIVSolutions 2011-02-13 05:18:05

0

那些不是选项。他们是完全不同的东西,他们需要完全不同的表格。令人痛苦的是,表中的数据看起来可能完全一样。以下是如何区分他们的方法。

关系数据库中的每个表都有且仅有一个谓词。谓词决定了表中的行的含义。因此,一个表,其数据看起来像这样

Name Company 
-- 
Gili Microsoft 
Marc Oracle 

可能意味着

Person named "Gili" is currently an employee of company "Microsoft". 
Person named "Marc" is currently an employee of company "Oracle". 

这样的表将排除顾问,因为他们不是雇员。 (在美国,他们不是,反正。)

但它也可能意味着

Person named "Gili" once was an employee of company "Microsoft". 
Person named "Marc" once was an employee of company "Oracle". 

并且该表也允许

Person named "Gili" once was an employee of company "Oracle". 

不同的谓词,不同的表。 (这意味着你必须构造表不同捕捉谓词的含义。)

什么你不能有一个表,这意味着在一个表

Person named "Gili" is currently an employee of company "Microsoft". 
Person named "Marc" once was an employee of company "Oracle". 

两个不同的谓词。无法在关系系统中完成。

因此,如果您谓词归结为

Person named NAME is currently an employee of company COMPANY. 

,那么你必须当人改变雇主更新的公司。如果你的谓词归结为这个

Person named NAME once was an employee of company COMPANY. 

然后你必须插入一个新的行,当人改变雇主。

0
tblPerson 
    PersonID 
    LastName 
    FirstName 

tblCompany 
    CompanyID 
    CompanyName 

tblCompany_Employee 
    PersonID 
    CompanyID 
    StartDate 
    EndDate 

行是永远不会从tblCompany_Employee删除 - 当一个人被雇佣记录插入,用的开始日期。在工作结束时,EndDate从NULL更新为empoyments结束的日期。

要了解当前的员工对某个特定的公司,选择是PersonID FROM tblCompanyEmployee WHERE结束日期为空。

要找到一个特定公司的就业状况对特定的人:

SELECT PersonID 
FROM tblCompany_Employee 
WHERE PersonID = @PersonID 
AND CompanyID = @CompanyID 
AND EndDate IS Null 

注:以上或许应该被包裹在一个函数只返回一个有效的雇员则返回true - 如果此人从未雇用过该公司 该声明将不返回记录,因此是错误的。

在这种情况下,审计跟踪维护,并有可能(有一些additiona细化,显然 - 我已经把这个漂亮的粗制滥造这里)来确定:

A.就业史上一个人在所有的公司谁一直受雇于某公司 C.谁是目前由某公司 D.谁不是目前由某公司雇用 D.等等

而从不丧失使用 B.所有的人由于UPDATES覆盖历史记录而导致的数据。

1

一般来说,数据仓库倾向于遵循“仅插入”模式。原因在于二元表中的陈旧行仍然需要将陈旧的事实置于当它们是新事实时存在的上下文中。

例如:宾夕法尼亚州是东北销售区域的一部分,直到1月1日,当它成为大西洋中部销售区域的一部分。去年12月的销售额需要返回到地理维度表中的一行,将其放在东北地区。如果更新“国家”维度表,则会使这一旧的事实无效。

在OLTP数据库的趋势是在地方执行更新,并且只跟踪什么是目前的情况。但是,这可能会导致将某些数据复制到事务行中。例如,采购订单系统中的发票明细行可能包含从产品表中的行复制的订购商品的价格。这样,如果价格在产品表中更新,影响此发票的价格不会遭到破坏。