将UPDATE传播到动态确定的表

时间:2013-10-11 10:11:08

标签: postgresql database-design triggers

在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我需要在新创建的表中插入行,所以使用执行命令我' m运行查询以在新创建的表中插入行。

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

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

UPDATE
 虽然在新创建的表中没有更新这些行,但也没有给出错误。我的输出为UPDATE 0

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

4 个答案:

答案 0 :(得分:0)

它是一个before触发器,你返回null - 这取消了语句。返回新的。

或者更恰当的是,因为它有副作用,所以请将其作为后触发。

额外注意:在tblVar上使用quote_ident(),或将其强制转换为regclass,以便对其进行清理。


  

更新虽然在新创建的表中没有更新这些行但是也没有给出错误。我得到输出为UPDATE 0

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

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

答案 1 :(得分:0)

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

当您更新表RECORDS并且没有受影响的记录时,您的触发器将不会执行。将您的return语句更改为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;

答案 2 :(得分:0)

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

  1. 仅对{em> base 表使用ON INSERT触发器(我认为它是records)。

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

  2. 对于ON UPDATE部分,向 base 表添加触发器没有任何意义,因为您的设计是空的。相反,您应该为您正在使用的每个分区创建一个这样的触发器,如果​​您是动态创建它们,那么您应该确保每个这样的分区都有它自己的触发器。不过,他们都可以共享一个功能。
  3. 还有一件事需要记住。 当您更新作为您的分区键的列时,您必须格外小心:

    • 如果您使用CHECK约束来限制分区中的键范围,您将获得例外;
    • 如果没有进行检查,最终可能会出现凌乱的数据布局。

    因此,如果新值对应不同的分区,则不是分区键列UPDATE,而是应该DELETE + INSERT

    另一种方法是完全限制对partitiong键列的更改。你可以这样做:

      触发器中的
    • ,比较OLDNEW值;
    • 仅为那些不属于您的分区键的列授予UPDATE权限。

答案 3 :(得分: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触发器的updatable VIEWlike @Denis already proposed)。然后在视图中可以看到辅助表的行。但是,对于完全动态的表名,这是不可能的。您必须在创建时知道涉及的表名。

这里可行的是inheritance。这会级联UPDATE以自动继承表。但不是INSERT,并且适用了许多限制。就像,主表上的外键约束不起作用等等。

UPDATE也适用于RULE。然后可以在取消操作之前传播WHERE子句。同样,动态表名称不可能。你可以用NOTIFY ...

做一些魔术

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