当前位置: 首页 > 面试题库 >

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

颛孙飞鸾
2023-03-14
问题内容

我有一个分区表,该表具有(我认为)适当的INSERT触发器和一些约束。不知何故,INSERT语句为每行插入2行INSERT:一行用于父行,另一行用于适当的分区。

简要的设置如下:

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而不是just
NULL。但是,我确实希望我的insert语句返回自动增量,id并且如果我只是返回NULL,则不是这种情况。

有什么解决方案?为什么返回NEW会导致这种看似“奇怪”​​的行为?

更新#1

好吧,我知道为什么行插入两次,这从触发器的文档中可以很明显地看出来:

每个语句触发器调用的触发器函数应始终返回NULL。如果选择,逐行触发器调用的触发器函数可以将表行(HeapTuple类型的值)返回给执行者。在操作之前触发的行级触发器具有以下选择:

它可以返回NULL以跳过当前行的操作。这指示执行程序不要执行调用触发器的行级操作(特定表行的插入,修改或删除)。

仅对于行级INSERT和UPDATE触发器,返回的行将成为将要插入的行或将替换要更新的行。这允许触发器功能修改要插入或更新的行。

但是我的问题仍然是如何不返回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;


 类似资料:
  • 问题内容: 我使用触发器来插入行,并希望使用上一个创建的ID供后续查询使用。 我该怎么办? 代码如下: 问题答案: 您是否看过LAST_INSERT_ID()?但要注意: 如果使用单个INSERT语句插入多行,则LAST_INSERT_ID()仅返回为第一个插入行生成的值。

  • 在“name=new.user_name”后面发现意外的标记“end-of-statement”。预期的令牌可能包括:“”..sqlcode=-104,sqlstate=42601,driver=4.9.78 SQL code:-104,SQL state:42601

  • 问题内容: 我有两个桌子和。我需要一些行插入从,它被插入之后。我知道这个问题,但我无法解决。请帮忙。 我为触发器编写了以下代码: 现在,当我插入行时,将添加到,但不添加到。它引发以下错误: _消息515,级别16,状态2,过程trigger_UpdateItemDetails,行11 无法将值NULL插入表’dbStockHandling.dbo.tbl_ItemDetails’的列’Purcha

  • 我创建了一个用于插入和更新的SQL触发器,它基本上执行以下操作: 从插入的表中获取LineID(表的PrimaryID)和RegionID,并将其存储在INT变量中。然后,它对连接表进行检查,以找到RegionID应该是什么,如果RegionID不等于插入表中应该是什么,那么它应该更新该记录。 我的问题是,它没有更新记录,我猜,这是因为记录还没有插入到PurchaseOrderLine表中,我正在

  • 谁能帮我创建一个触发器,将Sensordata表中的最后一条记录附加到lastssensordata中。我只希望每个ConnectionDeviceId有1个值,并且该值必须是最后插入的值。(它将用于在仪表中显示)。我将在下链接我的sql脚本。

  • null 计划:当用户购买一张票时,我将一条记录插入到适当的表中。例如,如果用户购买: Ticket#1,我将一条记录插入Table1 Ticket#2,我将一条记录插入Table1和Table2 Ticket#3,我将一条记录插入Table1和Table3 问题:如何接收不是类型为1的票证的所有数据,然后拆分参数插入到单独的表中。例如,当我试图为Table2创建触发器时,该触发器只能接收与Tab