2014-08-28 51 views
1

我在postgresql數據庫表上創建了更新前和更新後的觸發器。PostgreSQL觸發器和臨時表

需要保留歷史記錄並同時爲所述數據創建新記錄。舊記錄將被標記爲已歸檔。

我打算使用臨時表來跟蹤NEW值並重置NEW值,以便將其標記爲已存檔。

在我更新後的觸發器中,我會從臨時表中讀取數據,並創建一個全新的活動記錄。

我的問題是在更新觸發器之前更新觸發器不可見時創建的臨時表。此外,我甚至不能傳遞任何參數(類型記錄)到更新後觸發器,因爲它是不允許的。

我已經在Oracle數據庫中使用全局臨時表實現了預期的結果,但在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; 

有沒有一種方式,更新觸發後可以訪問創建臨時表在更新觸發器功能之前?

我不能擁有一個永久的表值,因爲許多用戶更新表中的數據可觸發觸發器。

+0

從來沒有這樣做過,但是來自文檔「臨時表在會話結束時或在當前事務結束時自動刪除」。這可能會給你一個提示,爲什麼你不能在另一個事務中訪問表。 – DrColossos 2014-08-28 07:39:51

+0

您的'AFTER'觸發器丟失。請將其添加到問題中。我將假設一種完全不同的方法,但我需要這些信息。並請澄清:您是否想要將* new *行或* old *行復制到* same *表或另一個*表(具有相同的結構)?你是否想將* new *行標記爲「已存檔」?這很奇怪,但這就是你的評論所說的。 – 2014-08-28 17:13:55

+0

另外,請提供您的Postgres版本(as * always *)。 – 2014-08-28 17:32:10

回答

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

2

臨時表應該顯示爲@Pavel explains,但這不是主要問題。

您的方法在Oracle中使用全局臨時表可能有意義。但是發佈的Postgres代碼沒有。

爲每一行觸發觸發器。你可以爲每一行創建一個臨時表,然後調用另一個觸發器,直接在一個觸發器中完成你可以輕鬆完成的任務。

相反,爲了保持舊行並將其設置爲archived,再加上插入新行的副本:

演示表:

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

觸發FUNC:

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; 

作品。

+0

不幸的是,在我想要使用臨時表的場景中沒有列出任何解決方案。我有一個表格說A,我維護現有行的歷史檔案記錄。給定記錄的新行將在某些關鍵字段被修改的情況下創建, – Anurag 2014-09-17 19:34:38