数据库:插入新行还是更新现有行?

时间:2011-02-12 22:12:32

标签: database-design

面向对象的设计鼓励使用不可变对象来提高线程安全性和性能。我想知道这是否会延续到关系数据库。

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

  • 用例
    • 每位员工只与一家公司相关联
    • 员工随着时间的推移改变他们的公司。
    • 员工姓名应该是唯一的。
  • 架构
    • 员工[姓名,公司]

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

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

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

5 个答案:

答案 0 :(得分:4)

我发布这篇文章是希望将来有助于其他人。我个人浪费了无数天沿着这条(错误的)道路走下去。

不可变对象用于值类型(想想整数,时间戳,温度读数等)。它们是永远不会改变的类型。在你开始讨论修改不可变对象的值的那一刻,这是一个非常强烈的迹象表明你正走错了路。当您使用真正的不可变对象时,您永远不必更新关联对象的引用。

因此,对于面向对象的编程或数据库设计,正确的答案是就地更新可变对象。

更新:marc_s提到某些系统需要不可变审计跟踪的事实。我建议将系统分成两部分。主表在将副本插入单独的审计表时就地更新数据。这有两个好处:

  1. 主表可以利用完整性检查(即“员工姓名必须是唯一的”)。
  2. 主表的读取速度非常快,可以随着时间的推移调整更大/更慢的审计表。
  3. 这使您可以享受这两个世界的精华。

答案 1 :(得分:1)

基本区别在于:

  • 如果您为每次更改插入新行,例如通过设置“ValidTo”日期来“停用”旧行,然后您可以了解更改的位置,随着时间的推移 - 您将进入“时态”数据库区域

  • 如果您一遍又一遍地更新同一行,那么您始终拥有当前状态 - 但不是历史记录。

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

答案 2 :(得分:1)

通常,Data Warehousing倾向于遵循“仅插入”模式。原因是仍然需要在diomension表中过时的行来将旧事实放在当它们是新事实时存在的上下文中。

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

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

答案 3 :(得分: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.

然后,当该人更换雇主时,您必须插入一个新行。

答案 4 :(得分:0)

tblPerson
    PersonID 
    LastName
    FirstName

tblCompany
    CompanyID
    CompanyName

tblCompany_Employee
    PersonID
    CompanyID
    StartDate
    EndDate

行永远不会从tblCompany_Employee删除 - 当雇用某个人时会插入记录,并带有开始日期。在雇佣结束时,EndDate从NULL更新为结束日期。

要查找特定公司的当前员工,请选择PersonID FROM tblCompanyEmployee WHERE EndDAte为空。

查找特定公司特定人员的就业状况:

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

注意:上面应该包含在一个函数中,该函数只返回true,返回有效的EmployeeID - 如果该人员从未在相关公司工作过 声明不会返回任何记录,因此是错误的。

在这种情况下,审计跟踪得以维持,并且有可能(通过一些额外的改进,显然 - 我已经非常粗略地说明了这一点)来确定:

一个。所有公司的人员的就业历史 B.所有受过特定公司雇用的人员 C.目前由某家公司雇用的人员 D.目前没有某个公司受雇的人 D.等等。

由于UPDATES覆盖了历史记录而没有丢失数据。