【问题标题】:Trigger on every update or insert在每次更新或插入时触发
【发布时间】:2014-08-28 04:28:16
【问题描述】:

我想创建每次更改任何列时都会触发的触发器 - 无论是新更新的还是新插入的。我创造了这样的东西:

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

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 OR UPDATE,但是如果我将其更改为 AFTER INSERT OR UPDATE 那么它既不适用于 INSERT 也不适用于 UPDATE。

【问题讨论】:

  • 可能你需要instead of insert trigger
  • 不,我想插入所有数据并连接所有列并将其保存在另一列中。
  • 不要使用select。直接使用new 记录中的值。
  • @a_horse_with_no_name 类似NEW.ts = NEW.a || ' ' || NEW.b || ' ' || ... ?
  • 正确。 concat_ws(' ', new.a, new.b, new.c, ...) 会更短一些,并且可以正确处理 NULL。

标签: sql postgresql


【解决方案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 值。

【讨论】:

    【解决方案2】:

    它不起作用,因为您在函数内部调用 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;
    

    【讨论】:

    • @thecoparyew - 我不认为你这样做。请参阅我的答案的编辑版本。
    【解决方案3】:

    在您的 COALESCE 语句中,当您转换 b::int 时,我必须更改您的合并以使用整数占位符。正如 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

    【讨论】:

    • 是的,它会触发,但如果我创建 AFTER 触发器,我不知道如何编写函数来更新字段值。
    • select 是完全没有必要的,它会使触发器大大慢于它可能的速度。将列转换为 int 也没有意义,因为整数值将立即转换为 text,因为它在 || 运算符中使用。
    • COALASCE 函数似乎在 concat 运算符之前得到处理。我只是想让他的代码示例正常工作,而不是改变它或改进它或烙印我的编码概念。问题不在于如何改进他的代码,而是如何让它运行。 IMO优化代码和功能代码是两种不同的动物。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-22
    • 1970-01-01
    相关资源
    最近更新 更多