3

我有一个非常简单的表students,结构如下,主键在哪里id。该表是大约 20 个数百万行的表的替身,这些表经常连接在一起。

+----+----------+------------+
| 编号 | 姓名 | 出生日期 |
+----+----------+------------+
| 1 | 爱丽丝 | 1989 年 1 月 12 日 |
| 2 | 鲍勃 | 1990 年 4 月 6 日 |
| 3 | 卡斯伯特 | 1988 年 1 月 23 日 |
+----+----------+------------+

如果 Bob 想更改他的出生日期,那么我有几个选择:

  1. 更新students新的出生日期。

    积极因素: 1 DML 操作;该表始终可以通过单个主键查找来访问。

    负面因素:我失去了 Bob 曾认为他出生于 1990 年 4 月 6 日这一事实

  2. 在表中添加一列 ,created date default sysdate并将主键更改为id, created。每一个都update变成:

    insert into students(id, name, dob) values (:id, :name, :new_dob)
    

    然后,每当我想要最新信息时,请执行以下操作(Oracle 但问题代表每个 RDBMS):

    select id, name, dob
      from ( select a.*, rank() over ( partition by id 
                                           order by created desc ) as "rank"
               from students a )
     where "rank" = 1
    

    优点:我从不丢失任何信息。

    负面因素:对整个数据库的所有查询都需要更长的时间。如果表格是指示的大小,这无关紧要,但是一旦您在第 5 次left outer join使用范围扫描而不是唯一扫描,就会开始产生影响。

  3. 添加一个不同的专栏,deleted date default to_date('2100/01/01','yyyy/mm/dd')或任何我喜欢的过早或未来主义的日期。将主键更改为id, deleted然后每个update变为:

    update students x
       set deleted = sysdate 
     where id = :id
       and deleted = ( select max(deleted) from students where id = x.id );
    insert into students(id, name, dob) values ( :id, :name, :new_dob );
    

    获取当前信息的查询变为:

    select id, name, dob
      from ( select a.*, rank() over ( partition by id 
                                           order by deleted desc ) as "rank"
               from students a )
     where "rank" = 1
    

    优点:我从不丢失任何信息。

    否定:两个 DML 操作;我仍然必须使用额外成本的排名查询或范围扫描,而不是每个查询中的唯一索引扫描。

  4. 创建第二个表,说student_archive并将每个更新更改为:

    insert into student_archive select * from students where id = :id;
    update students set dob = :newdob where id = :id;
    

    优点:永远不要丢失任何信息。

    否定: 2个DML操作;如果您想获得您必须使用的所有信息union或额外的left outer join.

  5. 为了完整起见,有一个可怕的去规范化的数据结构:id, name1, dob, name2, dob2...等等。

如果我不想丢失任何信息并总是进行软删除,那么第 1 号不是一个选项。5 号可以安全地丢弃,因为它造成的麻烦多于它的价值。

我只剩下选项 2、3 和 4 以及随之而来的负面影响。我通常最终使用选项 2 和可怕的 150 行(间隔很好)多个子选择连接。


tl;博士我意识到我在这里的“非建设性”投票中滑到了接近线的位置,但是:

在不删除任何数据的同时保持逻辑一致性的最佳(单一!)方法是什么?

有没有比我记录的更有效的方法?在这种情况下,我将高效定义为“更少的 DML 操作”和/或“能够删除子查询”。如果您在(如果)回答时能想到更好的定义,请随意。

4

1 回答 1

1

我会坚持 #4 并进行一些修改。无需从原始表中删除数据;在更新(或删除)原始记录之前将旧值复制到存档表就足够了。这可以通过行级触发器轻松完成。在我看来,检索所有信息并不是一项频繁的操作,而且我认为额外的 join/union 没有任何问题。此外,您可以定义一个视图,因此从最终用户的角度来看,所有查询都将是直接的。

于 2012-05-01T19:10:59.860 回答