database-design - 在 PostgreSQL 中实现 Type-2 缓慢变化维度

标签 database-design postgresql

我计划实现 Type-2 SCD在 PostgreSQL 中。 SCD 的缺点是,如果像经常看到的那样使用,则不能让外键引用这些表。换句话说,我经常看到在应用程序代码中处理引用完整性。这让我印象深刻,因为它可以直接在数据库中完成。添加少量触发器甚至可以向应用程序编码人员隐藏此实现细节。

我想出了以下模式。这些可以吗?

一对多

--
-- One-to-Many
--
BEGIN;

   CREATE TABLE document(
      id serial not null,
      revision integer not null default 1,
      title varchar(30),
      primary key (id, revision)
   );

   CREATE TABLE page(
      id serial not null,
      title varchar(30),
      document_id integer not null,
      document_revision integer not null,
      foreign key (document_id, document_revision) references document(id, revision)
   );


   -- Insert the first revision
   INSERT INTO document (title) VALUES ('my first document');
   INSERT INTO page (title, document_id, document_revision) VALUES ('my first page', 1, 1);

   -- DEBUG: display
   SELECT * FROM document d inner join page p ON ( d.id = p.document_id and d.revision = p.document_revision );

   -- "update" the document, by inserting a new revision
   INSERT INTO document (id, revision, title) VALUES (1, 2, 'my first document, edited');

   -- update the references
   UPDATE page SET document_revision = 2 WHERE document_id = 1;

   -- DEBUG: display
   SELECT * FROM document d inner join page p ON ( d.id = p.document_id and d.revision = p.document_revision );

ROLLBACK;

多对一

--
-- Many-to-One
--
BEGIN;

   CREATE TABLE page(
      id serial not null primary key,
      title varchar(30)
   );

   CREATE TABLE document(
      id serial not null,
      revision integer not null default 1,
      title varchar(30),
      page_id integer references page(id),
      primary key (id, revision)
   );

   -- Insert initial revision
   INSERT INTO page (title) VALUES ('my first page');
   INSERT INTO document (title, page_id) VALUES ('my first document', 1);
   INSERT INTO document (title, page_id) VALUES ('my second document', 1);

   -- DEBUG: display
   SELECT * FROM page p inner join document d on (p.id = d.page_id);

   -- destroy the link "from" the old revision
   UPDATE document SET page_id = NULL WHERE id=1;

   -- Add a new revision, referencing the page
   INSERT INTO document ( id, revision, title, page_id ) VALUES ( 1, 2, 'My First Document, edited', 1 );

   -- DEBUG: display
   SELECT * FROM page p inner join document d on (p.id = d.page_id);
   SELECT * FROM document;

ROLLBACK;

多对多

--
-- Many-to-Many
--
BEGIN;
   CREATE TABLE page(
      id serial not null primary key,
      title varchar(30)
   );

   CREATE TABLE document(
      id serial not null,
      revision integer not null default 1,
      title varchar(30),
      primary key (id, revision)
   );

   CREATE TABLE page_contains_document(
      page_id integer not null references page(id),
      document_id integer not null,
      document_revision integer not null,
      foreign key (document_id, document_revision) references document( id, revision )
   );

   -- Insert initial revision
   INSERT INTO page (title) VALUES ('My First page');
   INSERT INTO document (title) VALUES ('My Fist Document');
   INSERT INTO page_contains_document (page_id, document_id, document_revision) VALUES (1, 1, 1);

   -- DEBUG: display
   SELECT p.title, d.title, d.revision FROM page p INNER JOIN page_contains_document pcd ON (p.id = pcd.page_id) INNER JOIN document d ON (d.id = pcd.document_id and d.revision = pcd.document_revision);

   -- Add a new document revision
   INSERT INTO document (id, revision, title) VALUES (1, 2, 'My Fist Document, edited');

   -- update the reference
   UPDATE page_contains_document SET document_revision=2 WHERE document_id=1;

   -- DEBUG: display
   SELECT p.title, d.title, d.revision FROM page p INNER JOIN page_contains_document pcd ON (p.id = pcd.page_id) INNER JOIN document d ON (d.id = pcd.document_id and d.revision = pcd.document_revision);

ROLLBACK;

最佳答案

好的。我认为我们需要澄清一些关于为什么要进行 2 型 SCD 的重要误解。

它应该将所有数据保存在一个表格中,并用日期括起来(不是修订号!)。

所以,你可以:

   id   ,  name    ,  valid_from, valid_to
  1111  , MyBook   , '2009-03-01', '9999-12-31'

After an update:
  1111  , Mybook   , '2009-03-01', '2009-06-20'
  1111  , Mybook   , '2009-06-21', '9999-12-31'

“页面”数据库中应该存在具有有效开始日期和有效结束日期的类似结构。

重点是现在您可以通过以下方式获取最新版本:

select * from books where valid_to = '9999-12-31'

或者获取 4 月 1 日有效的版本

select * from books where valid_to >= '2009-04-01' and valid_from <= '2009-04-01'

同样在您的页面结构中,您只需要存储更新的页面。您不需要每次修订都为所有页面制作一份新副本。

关于database-design - 在 PostgreSQL 中实现 Type-2 缓慢变化维度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1620791/

相关文章:

sql - Postgresql SQL 排除查询

sql - 使用单个查询在 postgres 数据库中搜索多个表?

postgresql - 使用一条命令自动设置附加组件 Heroku-Postgresql

分布式应用中的数据库瓶颈

mysql - 类别和子类别

database-design - 表示数据库中的产品分类

mysql - 如果数据按排序顺序存储,sql 搜索是否会更快

postgresql - Postgres案例查询问题

ios - 关于核心数据库设计

postgresql - Postgres - 选择日期列值在另一行值范围内的所有行?