RDBMS 建模:具有极端历史版本控制的多对多

RDBMS Modeling: Many to Many with Extreme Historical Versioning

您有两个 table,foobar,它们具有 M:N 关系。

您想维护 foobar 的非常极端的历史版本,以及它们之间的关系,例如:

  1. 你在Foo中插入一行,然后在Bar中插入一行,然后在FooBar中插入一行linking两人在一起。您应该能够及时回顾并看到 Foo 中的行曾经是独立的,Bar.

  2. 中的行也是如此
  3. 然后将另一行插入 Bar,然后将一行插入 FooBar link 将第二个 bar 插入第一个 foo。您应该能够及时回顾并看到 foo 行仅 linked 到第一个 bar 行。

  4. 然后您更新 foo 行的属性之一。您应该能够及时回顾并看到 Bar 中的两行都曾经 linked 到具有先前属性的 foo 行。

虽然我能够实现这个,但我的解决方案相当乏味,并且会导致单个 update/insert 的大量 DML 操作。在 Bar 和 Baz 之间添加一个带有 M:N 的 Baz table 会显着增加 DML 的数量。是否有比以下方法更好地完成此任务的标准方法?

这是我的解决方案:


DDL

Foo
--------------
foo_id            INT            --sequence generated
foo_version_id    INT     UNIQUE --sequence generated
foo_name          VARCHAR
active            INT     CHECK (active in (0,1))
CONSTRAINT  PRIMARY_KEY (foo_id, foo_version_id)


Bar
--------------
bar_id            INT            --sequence generated
bar_version_id    INT     UNIQUE --sequence generated
bar_name          VARCHAR
active            INT     CHECK (active in (0,1))
CONSTRAINT  PRIMARY_KEY (bar_id, bar_version_id)

FooBar
--------------
foo_version_id    FK to foo.foo_version_id
bar_version_id    FK to bar.bar_version_id
CONSTRAINT PRIMARY KEY (foo_version_id, bar_version_id)

DML

以下是三种情况的伪代码。我已将这些作为程序实施。

对于案例#1,这导致对 link 两个独立的 foobar 进行 4 次 DML 操作,不包括前两行:

Insert the first foo row
Insert the first bar row
Update the first foo row and set active to 0. 
Insert a new foo row with the same foo_id, foo_name, but new foo_version_id
Update the first bar row and set active to 0
Insert a new bar row and with the same bar_id, bar_name, but new bar_version_id
Insert a row into foo_bar with the foo_version_id and bar_version_id from the newly created active foo and bar rows.

对于 案例 #2,这导致 9 个 DML 操作 link 一个新的 barfoo 即 linked 到第一个 bar,不包括第一行:

Insert the second bar row 
Update the active foo and set active to 0
Insert a new foo row with same foo_id, foo_name, but new foo_version_id
Update the first active bar and set active to 0
Insert a new bar row with same bar_id, bar_name, but new bar_version_id
Update the second active bar and set active to 0
Insert a new bar row with same bar_id, bar_name, but new bar_version_id
Insert a row into foo_bar with the foo_version_id and bar_version_id from the foo and first bar.
Insert a row into foo_bar with the foo_version_id and bar_version_id from the foo and second bar.

对于案例#3,这会导致 8 个 DML 操作来更新 foo 上的一个属性,linked 为两个 bars:

Update the active foo and set active to 0
Insert a new foo row with same foo_id, but new foo_version_id, foo_name
(repeat from case #2 starting at line 4)

SQL

给定一个已知的 foo_id,我可以在 foo_version_idbar_version_id 上加入 foofoo_barbar 并查看每个所讨论的特定 foo 的可能历史状态。

select f.foo_id, f.foo_version_id, f.foo_name, b.bar_id, b.bar_version_id, b.bar_name
FROM foo f, foo_bar fb, bar b
WHERE 1 = 1
    AND f.foo_version_id = fb.foo_version_id (+)
    AND fb.bar_version_id = b.bar_version_id (+)
ORDER BY f.foo_version_id, b.bar_version_id
;

foo_id | foo_version_id | foo_name | bar_id | bar_version_id | bar_name
     1 |              1 |        a |        |                |           -- 1) independent foo
     1 |              2 |        a |      1 |              2 |       b   -- 2) link foo to first bar
     1 |              3 |        a |      1 |              4 |       b   -- 3) link second bar to foo
     1 |              3 |        a |      2 |              5 |       b2  -- 3) link second bar to foo
     1 |              4 |        A |      1 |              6 |       b   -- 4) rename foo_name to A
     1 |              4 |        A |      2 |              7 |       b2  -- 4) rename foo_name to A

您需要一个时态数据库,即支持 SQL:2011 时态(或类似的专有系统)

据我所知,没有开源数据库支持这一点。一段时间以来,我一直在缠着 Posgres 的人去做这件事。

这意味着您需要 shell 拿出一些现金。以下数据库支持它:

IBM DB2 10+
甲骨文 12c
微软 SQL 服务器 2016