在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;
答案 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)
我假设你在这里建立了分区验证触发器。我最近一直在研究完全相同的问题。
仅对{em> base 表使用ON INSERT
触发器(我认为它是records
)。
此触发器将记录移动到所需的分区。
ON UPDATE
部分,向 base 表添加触发器没有任何意义,因为您的设计是空的。相反,您应该为您正在使用的每个分区创建一个这样的触发器,如果您是动态创建它们,那么您应该确保每个这样的分区都有它自己的触发器。不过,他们都可以共享一个功能。还有一件事需要记住。 当您更新作为您的分区键的列时,您必须格外小心:
CHECK
约束来限制分区中的键范围,您将获得例外; 因此,如果新值对应不同的分区,则不是分区键列和的UPDATE
,而是应该DELETE
+ INSERT
。
另一种方法是完全限制对partitiong键列的更改。你可以这样做:
OLD
和NEW
值; 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 VIEW(like @Denis already proposed)。然后在视图中可以看到辅助表的行。但是,对于完全动态的表名,这是不可能的。您必须在创建时知道涉及的表名。
这里可行的是inheritance。这会级联UPDATE
以自动继承表。但不是INSERT
,并且适用了许多限制。就像,主表上的外键约束不起作用等等。
UPDATE
也适用于RULE
。然后可以在取消操作之前传播WHERE
子句。同样,动态表名称不可能。你可以用NOTIFY
...
你要做的事很难实现。我可能会将整个逻辑打包成一个plpgsql函数并调用它,而不是尝试使用普通的INSERT
和UPDATE
语句。