如何抵消在“插入......冲突”的更新中调用两次之前触发器?

时间:2017-05-25 12:37:29

标签: postgresql

我已经定义了一个BEFORE触发器,它可以执行以下操作:

  1. 通过添加一些计算数据来调整要保存的当前行
  2. 有条件地将更改写入另一个表。
  3. 虽然我知道在AFTER触发器而不是BEFORE触发器中执行conditionally write some changes to a different table是惯用的,但是提取1和2的逻辑几乎是不可能的。

    所以考虑到这一点,我遇到了一些问题:

    我正在执行insert on conflict,这会导致行更新触发两次BEFORE触发器:一次用于插入,一次用于更新。 (即:PG正确引发'id已存在的异常',但这发生在执行before-trigger之后)

    这导致BEFORE触发器中的一些逻辑,例如写入更改,两次显然是错误的。

    如何抵消这种情况?

    虽然不是很优雅,但我可以想象当现有行以状态TG_OP = 'INSERT'进入触发器时,在BEFORE触发器中手动抛出'id already exists'异常。这会使第一个触发器短路,让PG负责其余的事情:引发冲突,导致更新发生。

    然而,我无法让它发挥作用。是否有可能手动引发异常,这是由'插入...在冲突'中提取的?

    任何替代方案?

2 个答案:

答案 0 :(得分:1)

也许您可以在第二个表上引入一个唯一约束,该约束检测何时输入两次相同的数据。然后你可以在第二个表上使用INSERT ... ON CONFLICT ... DO NOTHING

答案 1 :(得分:0)

你可以使用可写的CTE,我认为这只是PostgreSQL的扩展。它可以模拟UPSERT。

示例:

CREATE TABLE trigger_table (id int, column1 text, column2 int);

INSERT INTO trigger_table VALUES (13, 'test2', 5);

CREATE OR REPLACE FUNCTION trigger_fkey()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
BEGIN
raise exception 'test';
END;
$function$;

CREATE TRIGGER asdf BEFORE INSERT
   ON trigger_table FOR EACH ROW
   EXECUTE PROCEDURE trigger_fkey();

--works, update is done, insert not
WITH cte AS (UPDATE trigger_table SET column1 = 'test1', column2 = 1 
              WHERE id = 13 RETURNING id)
INSERT INTO trigger_table (id, column1, column2)
SELECT id, column1, column2
  FROM (VALUES (13, 'test1', 1)) AS b(id, column1, column2)
 WHERE id NOT IN(SELECT id FROM cte);

--after this before insert trigger will fire (on above insert) and exception is thrown
DELETE FROM trigger_table WHERE id = 13;