如何在PostgreSQL中存储表历史记录

时间:2019-05-24 15:35:22

标签: postgresql triggers

场景

我们需要将记录历史记录存储在PostgreSQL中,以便在将记录插入主表或在主表中更新记录(例如: pets )时,该记录会自动备份到历史记录中表格( pets_history )。

理想情况下,我们需要基于主表的架构生成历史表,而无需任何人工干预。

INSERT INTO pets(name, species) VALUES ('Meowth', 'Cat')
pets:
+---+------------+-------------+
|id | name       | species     |
+---+------------+-------------+
| 1 | Meowth     | Cat         |
+---+------------+-------------+

Trigger应该自动将记录插入 pets_history

pets_history:
+----+--------+-----------+---------+
| id | ref_id | name      | species |
+----+--------+-----------+---------+
| 1  | 1      | Meowth    | Cat     |
+----+--------+-----------+---------+

对宠物进行更新后,我的猫的名字从Meowth更改为Persian。例如:

UPDATE pets SET name = 'Persian' WHERE id = 1;
pets:
+---+------------+-------------+
|id | name       | species     |
+---+------------+-------------+
| 1 | Persian    | Cat         |
+---+------------+-------------+

我想结束以下内容...

pets_history:
+----+--------+-----------+---------+
| id | ref_id | name      | species |
+----+--------+-----------+---------+
| 1  | 1      | Meowth    | Cat     |
| 2  | 1      | Persian   | Cat     |
+----+--------+-----------+---------+

稍后将另一列/字段添加到pets表时,例如: color

pets:
+---+--------+---------+-------+
|id | name   | species | color |
+---+--------+---------+-------+
| 1 | Meowth | Cat     | cream |
+---+--------+---------+-------+

我们希望这会自动反映在pets_history表中:

pets_history:
+----+--------+---------+---------+-------+
| id | ref_id | name    | species | color |
+----+--------+---------+---------+-------+
| 1  | 1      | Meowth  | Cat     | null  |
| 2  | 1      | Persian | Cat     | null  |
| 3  | 1      | Persian | Cat     | cream |
+----+--------+---------+---------+-------+

如果有人知道在PostgreSQL中以其他方式进行此操作的方式,否则请分享。 我们看了这个问题/答案Implementing history of PostgreSQL table,它部分解决了难题,但它不会自动创建_history表。

1 个答案:

答案 0 :(得分:0)

您可以使用 to_jsonb 将整行作为 JSON 对象保存在历史表中。在这种情况下,您不需要关心在历史表中添加新列,因为值的键将是列名。

宠物桌

CREATE TABLE public.pets
(
  id serial NOT NULL,
  name text,
  species text,
  PRIMARY KEY (id)
);

宠物历史表

CREATE TABLE public.h_pets
(
  id serial NOT NULL,
  target_row_id integer NOT NULL,
  executed_operation integer NOT NULL,
  operation_executed_at timestamp without time zone NOT NULL DEFAULT now(),
  data_after_executed_operation jsonb,
  PRIMARY KEY (id)
);

向历史表中添加行的函数

CREATE OR REPLACE FUNCTION public.on_content_change()
  RETURNS trigger
  LANGUAGE 'plpgsql'
AS $BODY$
  DECLARE
    target_history_table TEXT;
  BEGIN
    target_history_table := TG_ARGV[0];

    IF TG_OP = 'INSERT'
    THEN
      EXECUTE
        format(
          'INSERT INTO %I (target_row_id, executed_operation, data_after_executed_operation) VALUES ($1.id, 0, to_jsonb($1))',
          target_history_table
        )
        USING NEW;
      RETURN NEW;
    ELSIF TG_OP = 'UPDATE'
    THEN
      EXECUTE
        format(
          'INSERT INTO %I (target_row_id, executed_operation, data_after_executed_operation) VALUES ($1.id, 1, to_jsonb($1))',
          target_history_table
        )
        USING NEW;
      RETURN NEW;
    ELSIF TG_OP = 'DELETE'
    THEN
      EXECUTE
        format(
          'INSERT INTO %I (target_row_id, executed_operation) VALUES ($1.id, 2)',
          target_history_table
        )
        USING OLD;
      RETURN OLD;
    END IF;
  END;
$BODY$;

和宠物表的触发器

CREATE TRIGGER pets_history_trigger
  BEFORE INSERT OR DELETE OR UPDATE
  ON public.pets
  FOR EACH ROW
  EXECUTE PROCEDURE public.on_content_change('h_pets');