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

Database design 数据库:插入新行还是更新现有行?,database-design,Database Design,面向对象设计鼓励使用不可变对象来提高线程安全性和性能。我想知道这是否会影响到关系数据库 更新现有行或插入用作覆盖的新行是否更好? 用例 每个员工只与一家公司关联 随着时间的推移,员工会改变他们的公司 员工姓名应是唯一的 模式 雇员[姓名、公司] 选项1:每次员工更换公司时,插入新员工[姓名,公司]行。应用程序被指示跳过较旧的行(随着时间的推移,这些行在后台线程中被修剪)。 选项2:每次员工更换公司时,更新现有行 选项1让我想起了不可变对象,因为它是线程安全的(不需要锁)。另一方面,每

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

更新现有行或插入用作覆盖的新行是否更好?

  • 用例
    • 每个员工只与一家公司关联
    • 随着时间的推移,员工会改变他们的公司
    • 员工姓名应是唯一的
  • 模式
    • 雇员[姓名、公司]
选项1:每次员工更换公司时,插入新员工[姓名,公司]行。应用程序被指示跳过较旧的行(随着时间的推移,这些行在后台线程中被修剪)。 选项2:每次员工更换公司时,更新现有行

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


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

基本区别在于:

  • 如果您为每次更改插入新行,例如通过设置“ValidTo”日期来“停用”旧行,那么您就有了随时间变化的历史记录—您将进入“临时”数据库的区域

  • 如果一次又一次地更新同一行,则始终具有当前状态,但没有历史记录


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

我之所以发布这篇文章,是希望它能在将来帮助其他人。我个人在这条(错误的)道路上浪费了无数天

不可变对象用于值类型(比如整数、时间戳、温度读数等)。它们是永远不会改变的类型。当您开始谈论修改不可变对象的值时,这就非常强烈地表明您走错了路。当您使用真正的不可变对象时,您不应该更新关联对象的引用

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

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

  • 主表可以利用完整性检查(即“员工姓名必须唯一”)
  • 主表的读取速度仍然非常快,较大/较慢的审核表可以随着时间的推移进行修剪

  • 这可以让你享受两个世界中最好的一面。

    这些都不是选项。它们是完全不同的东西,它们需要完全不同的桌子。令人痛苦的是,表中的数据看起来可能完全相同。下面是如何区分它们的

    关系数据库中的每个表都有且只有一个谓词。谓词确定表中的行的含义。一个数据如下的表

    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" 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.
    
    然后,当此人更换雇主时,您必须更新公司。如果你的谓词归结为

    Person named NAME is currently an employee of company COMPANY.
    
    Person named NAME once was an employee of company COMPANY.
    
    然后,当此人更换雇主时,必须插入新行

    tblPerson
        PersonID 
        LastName
        FirstName
    
    tblCompany
        CompanyID
        CompanyName
    
    tblCompany_Employee
        PersonID
        CompanyID
        StartDate
        EndDate
    
    永远不会从tblCompany_员工中删除行-在雇用人员时插入记录,并注明开始日期。雇佣关系结束时,EndDate从NULL更新为雇佣关系结束的日期

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

    要查找特定公司特定人员的就业状态,请执行以下操作:

    SELECT PersonID
    FROM tblCompany_Employee
    WHERE PersonID = @PersonID
    AND CompanyID = @CompanyID
    AND EndDate IS Null
    
    注意:如果该人员从未受雇于相关公司,则上述内容可能应封装在一个函数中,该函数仅在返回有效EmployeeID时返回true 该声明不会返回任何记录,因此为假

    在这种情况下,将保留一个审计跟踪,并且可以(显然,通过一些额外的改进-我在这里非常粗略地介绍了这一点)确定:

    A.所有公司员工的雇佣历史 B.受雇于特定公司的所有人员 C.目前受雇于某公司的人员 D.目前未受雇于某公司的人员 等等


    不会因覆盖历史记录的更新而丢失数据

    一般来说,数据仓库往往遵循“仅插入”模式。原因是仍然需要DioDimension表中的过时行将旧事实放置在新事实时存在的上下文中

    示例:宾夕法尼亚州在1月1日之前一直是东北销售区的一部分,当时它已成为