2014-08-27 128 views
0

我想創建觸發器,每次更改任何列時觸發 - 無論是新更新還是新插入。我創造了這樣的事情:觸發每次更新或插入

CREATE TRIGGER textsearch 
    BEFORE INSERT OR UPDATE 
    ON table 
    FOR EACH ROW 
    EXECUTE PROCEDURE trigger(); 

和身體觸發()函數是:

BEGIN 
    NEW.ts := (
    SELECT COALESCE(a::text,'') || ' ' || 
      COALESCE(b::int,'') || ' ' || 
      COALESCE(c::text,'') || ' ' || 
      COALESCE(d::int, '') || ' ' || 
      COALESCE(e::text,'') 
    FROM table 
    WHERE table.id = new.id); 

    RETURN NEW; 

END 

我希望這是清楚我想做的事情。

我的問題是觸發器只觸發更新,而不是插入。我猜這是行不通的,因爲我有BEFORE INSERT或UPDATE,但是如果我將其更改爲AFTER INSERT或UPDATE,那麼它對INSERT和UPDATE都不起作用。

+0

可能你需要[而不是插入觸發器](http://msdn.microsoft.com/en-us/library/ms175521%28v=sql.105%29.aspx) – 2014-08-27 13:00:38

+0

不,我想插入所有數據和concat所有列並將其保存在另一列中。 – thecoparyew 2014-08-27 13:09:23

+1

不要使用'select'。直接使用'new'記錄中的值。 – 2014-08-27 13:41:08

回答

1

你需要直接使用NEW記錄:

BEGIN 
    NEW.ts := concat_ws(' ', NEW.a::text, NEW.b::TEXT, NEW.c::TEXT); 
    RETURN NEW; 
END; 

concat_ws超過||的優點是concat_ws將以不同的方式處理NULL值。 'foo'||NULL的結果將產生NULL這很可能不是你想要的。 concat_ws將使用空字符串NULL值。

0

它不起作用,因爲你在函數內調用SELECT。

當它運行BEFORE INSERT那麼沒有一行可供選擇,是嗎?

其實,BEFORE UPDATE你會看到該行的「舊」版本,不是嗎?

只是直接使用字段:NEW.a等而不是選擇。


作爲一個編輯 - 這裏是一個顯示觸發函數可以看到的例子。這與您在BEFORE觸發器中所期望的一樣。

BEGIN; 

CREATE TABLE tt (i int, t text, PRIMARY KEY (i)); 

CREATE FUNCTION trigfn() RETURNS TRIGGER AS $$ 
DECLARE 
    sv text; 
BEGIN 
    SELECT t INTO sv FROM tt WHERE i = NEW.i; 
    RAISE NOTICE 'new value = %, selected value = %', NEW.t, sv; 
    RETURN NEW; 
END; 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER trigtest BEFORE INSERT OR UPDATE ON tt FOR EACH ROW EXECUTE PROCEDURE trigfn(); 

INSERT INTO tt VALUES (1,'a1'); 
UPDATE tt SET t = 'a2' WHERE i = 1; 

ROLLBACK; 
+0

不,如果我更新一些列,我會得到新的值。 – thecoparyew 2014-08-27 13:21:28

+0

@thecoparyew - 我不認爲你會這樣做。請參閱我的答案的編輯版本。 – 2014-08-27 14:10:28

0
在COALESCE語句

當你施放B ::詮釋我不得不改變你的聚結,使用整數佔位符代替。正如a_horse_with_no_name所提到的,這可以以空值結束,但您可以看到如何使您的特定代碼示例運行。我僅包含「RAISE NOTICE」行,僅用於調試目的。

根據您提供的信息爲我以下工作:

CREATE TABLE my_table (id SERIAL NOT NULL,a TEXT,b INTEGER,c TEXT,d INTEGER,e TEXT); 

CREATE OR REPLACE FUNCTION my_triggered_procedure() RETURNS trigger AS $$ 
BEGIN 
    if(TG_OP = 'UPDATE' OR TG_OP = 'INSERT') THEN 
     NEW.ts := (SELECT COALESCE(a::text,'') || ' ' || 
          COALESCE(b::int,0) || ' ' || 
          COALESCE(c::text,'') || ' ' || 
          COALESCE(d::int, 0) || ' ' || 
          COALESCE(e::text,'') 
        FROM my_table 
        WHERE id=NEW.id);   
     RAISE NOTICE 'INSERT OR UPDATE with new ts = %',NEW.ts; 
     RETURN NEW; 
    ELSIF (TG_OP = 'DELETE') THEN 
     OLD.ts := ' ';   
     RAISE NOTICE 'DELETED old id: %',OLD.id; 
     RETURN OLD; 
    END IF; 
END; 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER text_search 
AFTER INSERT OR UPDATE OR DELETE 
ON my_table 
FOR EACH ROW 
EXECUTE PROCEDURE my_triggered_procedure(); 

INSERT INTO my_table (a,b,c,d,e) VALUES('text11',12,'text21',3,'text4'); 
>NOTICE: INSERT OR UPDATE with new ts = text11 12 text21 3 text4 
>INSERT 0 1 

DELETE FROM my_table WHERE id=24; 
>NOTICE: DELETED ID = 24 
>DELETE 1 

PostgreSQL::Trigger Procedures

+0

是的,它會觸發,但我不知道如何編寫函數來更新字段值,如果我創建AFTER觸發器。 – thecoparyew 2014-08-27 13:40:40

+0

'select'是完全沒有必要的,並且使得觸發器*的速度慢得多。將列轉換爲「int」也是沒有意義的,因爲整數值由於在「||」運算符中的使用而立即被轉換爲「text」。 – 2014-08-27 20:31:10

+0

COALASCE函數似乎在concat操作符執行之前得到處理。我只是試圖讓他的代碼示例工作不會改變它,或者改進它或印記我的編碼概念。問題不在於如何改進他的代碼,而在於如何使其運行。 IMO優化的代碼和功能代碼是兩種不同的動物。 :) – JDA 2014-08-27 20:38:29