2014-08-27 132 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