我有一个分区表,其中包含(我认为的)适当的 INSERT 触发器和一些约束。不知何故,INSERT
语句为每个 INSERT
插入 2 行:一行用于父分区,一行用于相应分区。
设置简要如下:
CREATE TABLE foo (
id SERIAL NOT NULL,
d_id INTEGER NOT NULL,
label VARCHAR(4) NOT NULL);
CREATE TABLE foo_0 (CHECK (d_id % 2 = 0)) INHERITS (foo);
CREATE TABLE foo_1 (CHECK (d_id % 2 = 1)) INHERITS (foo);
ALTER TABLE ONLY foo ADD CONSTRAINT foo_pkey PRIMARY KEY (id);
ALTER TABLE ONLY foo_0 ADD CONSTRAINT foo_0_pkey PRIMARY KEY (id);
ALTER TABLE ONLY foo_1 ADD CONSTRAINT foo_1_pkey PRIMARY KEY (id);
ALTER TABLE ONLY foo ADD CONSTRAINT foo_d_id_key UNIQUE (d_id, label);
ALTER TABLE ONLY foo_0 ADD CONSTRAINT foo_0_d_id_key UNIQUE (d_id, label);
ALTER TABLE ONLY foo_1 ADD CONSTRAINT foo_1_d_id_key UNIQUE (d_id, label);
CREATE OR REPLACE FUNCTION foo_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.id IS NULL THEN
NEW.id := nextval('foo_id_seq');
END IF;
EXECUTE 'INSERT INTO foo_' || (NEW.d_id % 2) || ' SELECT $1.*' USING NEW;
RETURN NEW;
END
$$
LANGUAGE plpgsql;
CREATE TRIGGER insert_foo_trigger
BEFORE INSERT ON foo
FOR EACH ROW EXECUTE PROCEDURE foo_insert_trigger();
经过进一步调试,我隔离了导致该问题的原因:INSERT
触发器返回 NEW
,而不是仅 NULL
。不过,我确实希望我的插入语句返回自动递增的id
,如果我只返回NULL
,情况就不会如此。
解决办法是什么?为什么返回NEW
会导致这种看似“奇怪”的行为?
更新#1
好吧,我知道为什么行被插入两次,因为从触发器的文档中可以清楚地看出:
Trigger functions invoked by per-statement triggers should always return NULL. Trigger functions invoked by per-row triggers can return a table row (a value of type HeapTuple) to the calling executor, if they choose. A row-level trigger fired before an operation has the following choices:
It can return NULL to skip the operation for the current row. This instructs the executor to not perform the row-level operation that invoked the trigger (the insertion, modification, or deletion of a particular table row).
For row-level INSERT and UPDATE triggers only, the returned row becomes the row that will be inserted or will replace the row being updated. This allows the trigger function to modify the row being inserted or updated.
但我的问题仍然是如何可能不返回 NEW
并且仍然能够获取自动递增的 id
或 ROW_COUNT
例如?
更新#2
我找到了一种解决方案,但我确实希望有更好的解决方案。基本上,您可以添加 AFTER TRIGGER 来删除插入到父表中的行。这看起来效率非常低,所以如果有人有更好的解决方案,请发布!
供引用,解决方案是:
CREATE TRIGGER insert_foo_trigger
BEFORE INSERT ON foo
FOR EACH ROW EXECUTE PROCEDURE foo_insert_trigger();
CREATE OR REPLACE FUNCTION foo_delete_master()
RETURNS TRIGGER AS $$
BEGIN
DELETE FROM ONLY foo WHERE id = NEW.id;
RETURN NEW;
END
$$
LANGUAGE plpgsql;
CREATE TRIGGER after_insert_foo_trigger
AFTER INSERT ON foo
FOR EACH ROW EXECUTE PROCEDURE foo_delete_master();
最佳答案
更简单的方法是创建存储过程而不是触发器,例如 add_foo( [parameters] ),它将决定哪个分区适合插入行并返回 id (或新记录值,包括 id) 。例如:
CREATE OR REPLACE FUNCTION add_foo(
_d_id INTEGER
, _label VARCHAR(4)
) RETURNS BIGINT AS $$
DECLARE
_rec foo%ROWTYPE;
BEGIN
_rec.id := nextval('foo_id_seq');
_rec.d_id := _d_id;
_rec.label := _label;
EXECUTE 'INSERT INTO foo_' || ( _d_id % 2 ) || ' SELECT $1.*' USING _rec;
RETURN _rec.id;
END $$ LANGUAGE plpgsql;
关于sql - 插入触发器最终在分区表中插入重复行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22026354/