PostgreSQL触发器和临时表

时间:2014-08-28 02:58:17

标签: postgresql plpgsql triggers

我在postgresql数据库表上创建了更新前和更新后触发器。

需要保留历史记录,同时为所述数据创建新记录。旧记录将被标记为已存档。

我计划使用临时表来跟踪新值并重置新值,以便将其标记为已存档。

在我的更新后触发器中,我将从临时表中读取数据,并创建一个全新的活动记录。

我的问题是在更新触发器之前创建的临时表在更新触发器后不可见。此外,我甚至无法将任何参数(类型记录)传递给更新后的触发器,因为它是不允许的。

我已经使用Global Temporary表在Oracle db中实现了预期的结果,但在PostgreSQL中遇到了困难。

以下是更新前触发功能的示例代码:

CREATE OR REPLACE FUNCTION trigger_fct_trig_trk_beforeupdate()
RETURNS trigger AS
$BODY$
DECLARE

    some variable declarations;
    BEGIN

    Drop table  IF EXISTS track_tmp_test;

    CREATE TEMPORARY TABLE track_tmp_test(
    ...
    );

    Insert into track_tmp_test (........)
    values(NEW., NEW..., NEW.., NEW...);

    NEW... := OLD...;
    NEW... := OLD.... ;
    NEW... := OLD...;
    Mark the NEW.status : = 'archived';

RETURN NEW;
END
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE TRIGGER trig_trk_test_beforeupdate
BEFORE UPDATE ON test
FOR EACH ROW EXECUTE PROCEDURE trigger_fct_trig_trk_beforeupdate() ;

现在UPDATE后触发功能:

CREATE OR REPLACE FUNCTION trigger_fct_trg_trk_afterupdate()
  RETURNS trigger AS
$BODY$
DECLARE

    some variables;

-- insert into  original table the data from temporary that was inserted in before update trigger 
    INSERT into TEST (....)
    select ....
    from track_tmp_test ;

    -- delete data from temporary table after insert
    delete from track_tmp_test ;

 EXCEPTION
     WHEN OTHERS THEN
       -- Consider logging the error and then re-raise
       RAISE;
RETURN NEW;
END
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

有没有办法在更新后触发器可以访问在更新触发器功能之前创建的临时表?

我不能拥有永久表保持值,因为许多用户更新表中的数据会触发触发器。

2 个答案:

答案 0 :(得分:2)

临时表应显示为@Pavel explains,但这不是主要问题。

您的方法可能在Oracle中使用全局临时表有意义。但是发布的Postgres代码没有。

每行触发触发器。你会(删除并)为每一行创建一个临时表,并调用另一个触发器,只是为了直接在一个触发器中做你可以做的事。

相反,要保留旧行并将其设置为archived,再加上INSERT新行的副本:

演示表:

CREATE TEMP TABLE test (id int, txt text, archived bool DEFAULT FALSE);

触发器功能:

CREATE OR REPLACE FUNCTION trg_test_beforeupdate()
  RETURNS trigger AS
$func$
BEGIN
   INSERT INTO test SELECT (NEW).*;  -- insert a copy of the NEW row

   SELECT (OLD).* INTO NEW;      -- revert row to previous state

   NEW.archived = TRUE;          -- just set it to "archived"

   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

触发:

CREATE TRIGGER beforeupdate
BEFORE UPDATE ON test
FOR EACH ROW EXECUTE PROCEDURE trg_test_beforeupdate();

测试:

INSERT INTO test VALUES (1, 'foo'), (2, 'bar');
UPDATE test SET txt = 'baz' WHERE id = 1;
SELECT * FROM test;

作品。

答案 1 :(得分:1)

从触发器访问临时表没有问题,并且代码在没有问题的情况下工作(在PostgreSQL 9.4上):

CREATE OR REPLACE FUNCTION public.f1()
RETURNS trigger
LANGUAGE plpgsql
AS $function$ 
begin
  drop table if exists bubu; 
  create temp table bubu(a int); 
  insert into bubu values(10); 
  return new; 
end
$function$

CREATE OR REPLACE FUNCTION public.f2()
RETURNS trigger
LANGUAGE plpgsql
AS $function$ 
declare r record;
begin 
  for r in select * from bubu 
  loop 
    raise notice '%', r; 
  end loop; 
  return null; 
end 
$function$

create trigger xx
  before insert on omega
  for each row execute procedure f1();

create trigger yy
  after insert on omega
  for each row execute procedure f2();

postgres=# insert into omega values(333);
NOTICE:  (10)
INSERT 0 1

所以我确定,所以你的问题不会是访问临时表。它运作良好。在某些8.2,8.3及更旧版本上可能会出现问题,并且对丢弃的对象有无效的计划。这不是你的问题吗?

我可以说,所以你的设计是错的 - 没有任何理由,为什么你必须使用临时表。触发后你可以做同样的工作。触发器内的任何操作都应该快速,快速。删除或创建临时表不是快速操作。

如果你有一个较旧的PostgreSQL版本,你不必每次都删除临时表。您应该只删除内容。查看文章http://postgres.cz/wiki/Automatic_execution_plan_caching_in_PL/pgSQL