2

在 PostgreSQL 中,我创建了触发器过程来执行一些验证并在插入或更新表中的行之前执行一些查询。对于插入操作,我的触发器运行良好,所以我正在执行一些查询,但对于更新,我无法执行查询。

CREATE FUNCTION RECORDS_VALIDATE() RETURNS TRIGGER AS $$
DECLARE
 tblVar text := dynamically im creating table name;
BEGIN
IF NEW.date <= CURRENT_DATE THEN
 RAISE NOTICE 'DATE IS LESS THAN THE CURRENT DATE'; 
 RETURN NULL;   
END IF;

IF (TG_OP = 'UPDATE') THEN
  EXECUTE 'UPDATE '|| tblVar || ' SET id = $1,date = $2 where id = $3'
  USING NEW.id,NEW.date,OLD.id;
 RETURN NULL;
END IF;
IF(TG_OP = 'INSERT') THEN 
  EXECUTE 'INSERT INTO ' || tblVar || ' VALUES($1,$2)' 
  USING NEW.id, NEW.date;
  RETURN NULL;
END IF;
END;
$$ LANGUAGE plpgsql;

扳机

CREATE TRIGGER RECORDS BEFORE INSERT OR UPDATE ON RECORDS
FOR EACH ROW EXECUTE PROCEDURE RECORDS_VALIDATE();

注意:RECORDS 是来自这个基表名称的基表我正在动态创建表,所以我不想在 RECORDS 中插入行,这就是为什么我返回 NULL 我需要在新创建的表中插入行,所以使用执行命令我正在运行查询以在新创建的表中插入行。

INSERT
这将在新创建的表中插入不在记录中的行

INSERT INTO BILLING_RECORDS VALUES(1,date '2013-10-15');

更新
在新创建的表中运行此行时未更新。也没有给出错误。我的输出为 UPDATE 0

UPDATE RECORDS SET id = 10,date = date '2013-12-30' where id=1;
4

4 回答 4

0

这是一个前触发器,您返回 null - 这会取消该语句。而是返回新的。

或者更恰当地说,因为它有副作用,所以让它成为后触发。

额外说明:在 tblVar 上使用 quote_ident(),或将其转换为 regclass,因此它已被清理。


更新运行此行时未在新创建的表中更新。也没有给出错误。我得到的输出为 UPDATE 0

这是因为您取消了更新。0 对应于受影响的行数,并保存最后一个查询返回的内容——您取消了该查询。这是您永远不应在前触发器中产生副作用的原因之一。

考虑使用可更新视图而不是表,并instead of根据需要使用触发器来处理插入和更新。

于 2013-10-11T10:38:58.430 回答
0

当您NULL从触发器返回以进行 INSERT 操作时,表中不会添加任何记录RECORDS

当您更新表RECORDS并且没有记录受到影响时,您的触发器将不会执行。将您的退货声明更改为NEW

  EXECUTE 'INSERT INTO ' || tblVar || ' VALUES($1,$2)' 
  USING NEW.id, NEW.date;
  RETURN NEW;

并且对于UPDATE

  EXECUTE 'UPDATE '|| tblVar || ' SET id = $1,date = $2 where id = $3'
  USING NEW.id,NEW.date,OLD.id;
  RETURN NEW;
于 2013-10-11T10:40:32.277 回答
0

我假设你在这里建立分区自动触发器。我最近一直在研究完全相同的问题。

  1. 仅对您的ON INSERT表使用触发器(我想是这样)。records

    此触发器会将记录移动到所需的分区中。

  2. 对于这一ON UPDATE部分,将触发器添加到您的表没有任何意义,因为您的设计它是空的。相反,您应该为正在使用的每个分区创建这样的触发器,如果​​您是动态创建它们,那么您应该确保每个这样的分区都有自己的触发器。尽管如此,它们都可以共享一个功能。

还有一件事要记住。当您更新作为分区键的列时,您必须格外小心:

  • 如果您使用CHECK约束来限制分区内的键范围,则会出现异常;
  • 如果没有进行检查,您最终可能会得到一个混乱的数据布局。

因此,不要UPDATE使用分区键列,并且如果新值对应于不同的分区,则应该使用DELETE+INSERT代替。

另一种方法是完全限制对分区键列的更改。你能行的:

  • 在触发器中,比较OLDNEW值;
  • UPDATE仅为那些不属于您的分区键的列授予权限。
于 2013-10-11T11:49:57.720 回答
0

RETURN NULL取消 BEFORE 触发器的当前行上的 INSERT / UPDATE,但不会取消副作用。在这方面其他答案是不正确的。

上面的测试用例应该可以工作。我测试了它 Postgres 9.1。然而...

您的INSERT语句一定会失败,因为您正在插入另一个表:

INSERT INTO BILLING_RECORDS records VALUES(1,date '2013-10-15');

您的UPDATE语句可能会失败,因为 中没有匹配的行records,因为该行已插入<dynamically im creating table name;>并且在 中不可见records

UPDATE RECORDS SET id = 10,date = date '2013-12-30' where id=1;

您可以使用带有 INSTEAD 触发器的可更新视图如 @Denis 已经提出的)。然后辅助表的行将在视图中可见。但这对于完全动态的表名是不可能的。您必须在创建时知道所涉及的表名。

在这里可以工作的是继承。这会自动级联UPDATE到继承表。但不是INSERT,并且存在许多限制。比如,主表上的外键约束不起作用,等等。

UPDATE也可以与RULE. 然后WHERE可以在取消操作之前传播该子句。同样,动态表名不可能。你也许可以用NOTIFY......做一些魔法

你想做的事情很难实现。我可能会将整个逻辑打包到一个 plpgsql 函数中并调用它,而不是尝试使用普通的INSERTUPDATE语句。

于 2013-10-11T15:23:02.133 回答