在触发过程中什么也不做
Do nothing in a trigger procedure
我在尝试执行触发器时遇到了麻烦。假设我们有 2 个 table,我想将数据从 table A 复制到 table B 但每个 table 都有一个 唯一约束 .
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.
触发器有什么问题?
您的示例已损坏。源和目标在触发器中的 INSERT
中是相同的,这必然会引发独特的违规行为 每次 (插入 NULL 时除外)- 被 [=16= 抑制], 所以触发器中什么也没有发生。
您还忘记了原始 INSERT
中的唯一约束。见下文。
<strike>INSERT INTO test2(test_name2)
VALUES(NEW.test_name2)
...
CREATE TRIGGER trigger_test
AFTER INSERT
ON test2</strike>
从一个不那么令人困惑的设置开始:
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() < 1)
?
注意 您以这种方式捕获 test2
中的唯一违规(没有任何反应),但 test1
仍然会引发异常,除非你在那里也有 ON CONFLICT ... DO NOTHING
。你的测试是如意算盘:
<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;
相关:
- Insert data in 3 tables at a time using Postgres
- PostgreSQL multi INSERT...RETURNING with multiple columns
我在尝试执行触发器时遇到了麻烦。假设我们有 2 个 table,我想将数据从 table A 复制到 table B 但每个 table 都有一个 唯一约束 .
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.
触发器有什么问题?
您的示例已损坏。源和目标在触发器中的 INSERT
中是相同的,这必然会引发独特的违规行为 每次 (插入 NULL 时除外)- 被 [=16= 抑制], 所以触发器中什么也没有发生。
您还忘记了原始 INSERT
中的唯一约束。见下文。
<strike>INSERT INTO test2(test_name2)
VALUES(NEW.test_name2)
...
CREATE TRIGGER trigger_test
AFTER INSERT
ON test2</strike>
从一个不那么令人困惑的设置开始:
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() < 1)
?
注意 您以这种方式捕获 test2
中的唯一违规(没有任何反应),但 test1
仍然会引发异常,除非你在那里也有 ON CONFLICT ... DO NOTHING
。你的测试是如意算盘:
<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;
相关:
- Insert data in 3 tables at a time using Postgres
- PostgreSQL multi INSERT...RETURNING with multiple columns