sql - 插入触发器最终在分区表中插入重复行

标签 sql postgresql postgresql-9.1

我有一个分区表,其中包含(我认为的)适当的 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 并且仍然能够获取自动递增的 idROW_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/

相关文章:

python - 如何将唯一标识符与 Python 应用程序中的 PostgreSQL 连接关联起来?

sql - 在一组实验室测试中选择第一个和最后一个测试

oracle - 需要帮助将 Oracle 触发器转换为 PostgreSQL

postgresql - Postgres SELECT ... FOR UPDATE 函数

sql - 如何将单列转置为单行行

mysql - 为什么在这个 sql 中,函数 AVG() 必须在子查询中?

postgresql - 在 PostgreSQL 中,哪些类型可以先用类型名称进行转换?

android - Android中的postgres JDBC驱动程序

sql - 如何批量插入数据到两个SQL表

sql - 相当于 "when deleting the user, delete all his posts"的数据库概念是什么?