【问题标题】:Do nothing in a trigger procedure在触发过程中什么都不做
【发布时间】:2017-04-05 00:25:37
【问题描述】:

我在尝试执行触发器时遇到了麻烦。假设我们有 2 个表,我想将数据从 表 A 复制到 表 B,但每个表都有一个唯一约束

create table test1 (
 test_name varchar);

create unique index test1_uc on test1 USING btree (test_name);

create table test2 (
test_name2 varchar);

 create unique index test2_uc on test2 USING btree (test_name2);

CREATE OR REPLACE FUNCTION trig_test()
  RETURNS trigger AS
$$
BEGIN
  IF pg_trigger_depth() <> 1 THEN
    RETURN NEW;
END IF;
INSERT INTO test2(test_name2)
   VALUES(NEW.test_name2)
ON CONFLICT (test_name2) DO NOTHING;

RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_test
AFTER INSERT
ON test2
FOR EACH ROW
EXECUTE PROCEDURE trig_test();

insert into test2 values ('test');
insert into test2 values ('test'); //should do nothing ?

但我收到此错误:

ERROR:  duplicate key value violates unique constraint "test2_uc"
DETAIL:  Key (test_name2)=(test) already exists.

触发器有什么问题?

【问题讨论】:

  • 您需要在触发器中使用return null。并且触发器必须BEFORE触发器
  • 触发器之前很好,但如果我返回 null 我不能从表 1 复制到表 2 cf rextester.com/LAJB87421

标签: postgresql triggers duplicates plpgsql upsert


【解决方案1】:

你的例子被打破了。触发器中的INSERT 中的源和目标是相同的,这必然会引发一个唯一的违规每次(插入NULL 时除外) - 被ON CONFLICT (test_name2) DO NOTHING 抑制,因此在触发器。

您还忘记了原始INSERT 中的唯一约束。见下文。

INSERT INTO test2(test_name2)
   VALUES(NEW.test_name2)

...

CREATE TRIGGER trigger_test
AFTER INSERT
ON test2

从一个不那么混乱的设置开始:

CREATE TABLE test1 (col1 text UNIQUE);
CREATE TABLE test2 (col2 text UNIQUE);

pg_trigger_depth() 移动到触发器本身会更有效。所以这会起作用,将插入到test1 的行复制到test2(而不是其他方式),仅适用于 first 级别的触发深度:

CREATE OR REPLACE FUNCTION trig_test()
  RETURNS trigger AS
$func$
BEGIN
   INSERT INTO test2(col2)             -- !!
   VALUES (NEW.col1)                   -- !!
   ON     CONFLICT (col2) DO NOTHING;  -- !!

   RETURN NULL;
END
$func$ LANGUAGE plpgsql;

我将其保留为AFTER 触发器。也可以是BEFORE 触发器,但您需要 RETURN NEW;

CREATE TRIGGER trigger_test
AFTER INSERT ON test1                  -- !!
FOR EACH ROW 
WHEN (pg_trigger_depth() < 1)          -- !!
EXECUTE PROCEDURE trig_test();

为什么是(pg_trigger_depth() &lt; 1)

注意,您以这种方式捕获 test2 中的唯一违规(没有任何反应),但 test1 中的唯一违规仍会引发异常,除非您有 @ 987654341@ 也在那里。你的测试是一厢情愿:

<strike>insert into test2 values ('test'); //should do nothing ?</strike>

必须是:

INSERT INTO test1 values ('test') ON CONFLICT (col1) DO NOTHING;

替代方案:使用 CTE 链接两个 INSERT

如果您可以控制test1 上的INSERT 命令,则可以执行此操作而不是触发器:

WITH ins1 AS (
   INSERT INTO test1(col1)
   VALUES ('foo')                  -- your value goes here
   ON CONFLICT (col1) DO NOTHING
   RETURNING *
   )
INSERT INTO test2(col2)
SELECT col1 FROM ins1
ON CONFLICT (col2) DO NOTHING;

相关:

【讨论】:

    猜你喜欢
    • 2023-04-08
    • 1970-01-01
    • 2015-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多