Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/laravel/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我应该如何用PostgreSQL实现记录历史表?_Postgresql_Database Design - Fatal编程技术网

我应该如何用PostgreSQL实现记录历史表?

我应该如何用PostgreSQL实现记录历史表?,postgresql,database-design,Postgresql,Database Design,我想为现有应用程序中的记录添加修订,该应用程序将数据存储在PostgreSQL数据库中。我阅读了有关策略的文章,例如,in和 我认为创建很少被查询的第二个历史记录表的方法最有效。然而,我确实有一些实际问题。假设这是我想要添加修订控制的表: create table people( id serial not null primary key, name varchar(255) not null ); 对于这个非常简单的表,我的历史记录表可以如下所示: create table peo

我想为现有应用程序中的记录添加修订,该应用程序将数据存储在PostgreSQL数据库中。我阅读了有关策略的文章,例如,in和

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

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)
);
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
);
这带来了第一个问题:

  • 如何生成修订号?
当然,我可以创建一个序列,从中请求修订号,这将很容易。然而,这将在每个人的修订之间留下很大的差距,因为许多人共享相同的序列,如果修订数字是上升的数字,而每个人没有差距,这将感觉更自然。 所以我很想通过
选择max(revision)+1来找到我的修订号。。。其中peopleId=…
。但是,如果两个线程请求下一个版本号并尝试插入,则可能会导致争用情况。我不得不承认这是不太可能的(特别是在我的情况下,无论如何只有很少的更新发生),并且不会导致数据损坏,因为这将是一个重复的主键,因此会导致事务回滚,但这也不太好。我想知道是否有更好的解决办法

  • 如何将数据插入历史记录表?
我想到两种方法:手动更新主表的每个语句或使用触发器。触发器听起来不太容易出错,因为我不太可能忘记某个地方的查询。但是,我无法准确地与应用程序沟通刚刚创建的修订号,可以吗?因此,如果我想创建两个类似这样的事件表:

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)
);
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语句时进行insert,也许通过这个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");

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