postgresql - 我实际上应该如何使用 PostgreSQL 实现记录历史表?

标签 postgresql database-design

我想为现有应用程序中的记录添加修订,该应用程序将数据存储在 PostgreSQL 数据库中。我阅读了有关策略的信息,例如在 this question , this 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 是由触发器创建的,则很难找到(或者是否有实用的方法来做到这一点?)。

最佳答案

好吧,您需要为您拥有的所有表和列制定一个复制策略,您可以创建一个表来维护所有更改,并在您进行 UPDATE INSERT 或 DELETE 语句时随时插入,也许这个 framwork idempiere changelog 示例可以提供帮助你

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");

关于postgresql - 我实际上应该如何使用 PostgreSQL 实现记录历史表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28434233/

相关文章:

postgresql - 错误 : missing FROM-clause entry for table

performance - Heroku:Mongodb 与 Postgres 的性能对比

sql - 如何设计对具有添加关系的单个数据库表的循环引用?

postgresql - jsonb 和主/外键 : which performs better in PostgreSQL?

database-design - 什么是归一化(或归一化)?

python Django : Join on the same table

postgresql - Postgres IF 变量

mysql - 将所有图像放在一个表中是一种不好的做法吗?

sql-server - 禁止的架构名称 : SYS, DBO (MsSQL)

sql - 是否有一种安全的方法来修改表 pg_constraint 以便不再进行检查(暂时)?