【问题标题】:How should I actually implement a record history table with PostgreSQL?我应该如何用 PostgreSQL 实现一个记录历史表?
【发布时间】:2015-04-10 15:16:36
【问题描述】:

我想为现有应用程序中的记录添加修订,该应用程序将数据存储在 PostgreSQL 数据库中。我阅读了有关策略的信息,例如在this questionthis questionthis blog post

我认为创建第二个很少被查询的历史表的方法效果最好。但是我确实有一些实际问题。假设这是我要添加修订控制的表:

create table people(
  id serial not null primary key,
  name varchar(255) not null
);

对于这个非常简单的表,我的历史表可能如下所示:

create table people_history(
  peopleId int not null references people(id) on delete cascade on update restrict,
  revision int not null,
  revisionTimestamp timestamptz not null default current_timestamp,
  name character varying(255) not null,
  primary key(peopleId, revision)
);

这带来了第一个问题:

  • 如何生成修订号?

当然,我可以创建一个序列,从中请求修订号,这很容易。然而,这会在每个人的修订之间留下很大的差距,因为许多人共享相同的序列,如果修订数字是递增的数字而没有每人的差距,那会感觉更自然。 所以我很想通过select max(revision)+1 from ... where peopleId=... 找到我的修订号。但是,如果两个线程要求下一个修订号并尝试插入,这可能会导致竞争条件。我不得不承认这不太可能(尤其是在我的情况下,无论如何只发生很少的更新)并且不会导致数据损坏,因为这将是一个重复的主键并因此导致事务回滚,但它也不漂亮。我想知道是否有更漂亮的解决方案。

  • 如何将数据插入历史记录表?

我想到了两种方法:在更新主表的每个语句上手动或使用触发器。触发器听起来不太容易出错,因为我不太可能忘记某处的查询。但是,我无法与应用程序准确地沟通刚刚创建的修订号,可以吗?所以如果我想创建几个这样的事件表:

create table peopleUserEditEvent (
  poepleId int not null,
  revision int not null,
  userId int not null references users(id) on delete set null on update restrict,
  comment text not null default '',
  primary key(paopleId, revision),
  foreign key (peopleId, revision) references people_history
);

列出了一些修订的元数据,解释了修订的更改原因。在这种情况下,具有特定 ID 的用户编辑了数据并可能提供了评论。

在另一种情况下(和另一个事件表),cronjob 可能已经更改了某些内容并记录了可能没有 userId 和没有评论但其他元数据的事件。

要添加这些事件数据,我需要修订 ID,如果修订 ID 是由触发器创建的,则很难找到(或者是否有实用的方法?)。

【问题讨论】:

  • 修订号很重要?我的意思是间隙。您可以拥有数据,串行或大串行可以完成这项工作。扳机,没问题。但是如果你想要更多的控制和返回数据,你可以创建一个函数来更新数据,你的客户端应用程序必须只使用它。这样就可以返回序列值了。
  • 既然有时间戳,为什么还需要修订号?您可以使用 row_number() over (order by revisionTimestamp) 动态创建数字
  • @user_0:修订号并不重要,拥有它们会感觉更自然。查看数据库,修订号实际上对我作为一个人来说意味着什么,因为我可以看到,在修订 42 之后是修订 43,而修订 42 意味着这是第 42 次更改。当然,在需要时还有其他方法可以计算这些数字(如果没有更好的主意,我会这样做)。
  • @FrankHeikens:如果在同一秒内发生两个更改,如果我没有修订号,那将是一个问题。此外,我想使用链接到修订版的外键并引用修订版,例如从网址。当然,外键是可能的,如果我实现一个一致的模式,那么时间戳如何序列化以进行外部访问也是可能的,但对我来说,只是修订计数似乎更简单。
  • @yankee,在 people_history 表中,revisionTimestamp 列将包含什么?我的意思是:它可以包含数据归档的时间。如果您需要存储插入数据的时间,则需要更多代码。

标签: postgresql database-design


【解决方案1】:

好吧,您需要为您拥有的所有表和列使用一种复制策略,您可以创建一个表来维护所有更改并在任何时候进行 UPDATE INSERT 或 DELETE 语句插入,也许使用这个框架 idempiere 更改日志的例子可以提供帮助你

CREATE TABLE ad_changelog (
  ad_changelog_id NUMERIC(10,0) NOT NULL,
  ad_session_id NUMERIC(10,0) NOT NULL,
  ad_table_id NUMERIC(10,0) NOT NULL,
  ad_column_id NUMERIC(10,0) NOT NULL,
  isactive CHAR(1) DEFAULT 'Y'::bpchar NOT NULL,
  created TIMESTAMP WITHOUT TIME ZONE DEFAULT now() NOT NULL,
  createdby NUMERIC(10,0) NOT NULL,
  updated TIMESTAMP WITHOUT TIME ZONE DEFAULT now() NOT NULL,
  updatedby NUMERIC(10,0) NOT NULL,
  record_id NUMERIC(10,0) NOT NULL,
  oldvalue VARCHAR(2000),
  newvalue VARCHAR(2000),
  undo CHAR(1),
  redo CHAR(1),
  iscustomization CHAR(1) DEFAULT 'N'::bpchar NOT NULL,
  description VARCHAR(255),
  ad_changelog_uu VARCHAR(36) DEFAULT NULL::character varying,
  CONSTRAINT adcolumn_adchangelog FOREIGN KEY (ad_column_id)
    REFERENCES adempiere.ad_column(ad_column_id)
    MATCH PARTIAL
    ON DELETE CASCADE
    ON UPDATE NO ACTION
    DEFERRABLE
    INITIALLY DEFERRED,
  CONSTRAINT adsession_adchangelog FOREIGN KEY (ad_session_id)
    REFERENCES adempiere.ad_session(ad_session_id)
    MATCH PARTIAL
    ON DELETE NO ACTION
    ON UPDATE NO ACTION
    DEFERRABLE
    INITIALLY DEFERRED,
  CONSTRAINT adtable_adchangelog FOREIGN KEY (ad_table_id)
    REFERENCES adempiere.ad_table(ad_table_id)
    MATCH PARTIAL
    ON DELETE CASCADE
    ON UPDATE NO ACTION
    DEFERRABLE
    INITIALLY DEFERRED
) 
WITH (oids = false);

CREATE INDEX ad_changelog_speed ON adempiere.ad_changelog
  USING btree (ad_table_id, record_id);

CREATE UNIQUE INDEX ad_changelog_uu_idx ON adempiere.ad_changelog
  USING btree (ad_changelog_uu COLLATE pg_catalog."default");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多