其实有办法做到这一点...
确实不能直接更新同一个表/列
触发,但有一个简单的解决方法,使用
视图和“代替”触发器一直对我有用。
- 重命名表格
- 使用原始名称在该表上创建一个视图
- 在视图上创建一个“instead of”触发器,然后写入原始表
- 通过撤销对原始表的所有权限来强制使用视图
这是所提出问题的真实示例 - 带有日期范围的价目表,
同一项目的其他行需要随时更新
完成插入、更新、删除,以避免有日期范围
重叠(同一日期有多个价格)。
价目表:
item valid_from valid_to price
01 2010-01-01 2013-03-15 120.00
01 2013-04-16 2015-12-31 125.00
01 2016-01-01 2016-05-05 130.00
01 2016-05-06 140.00
首先,将表替换为同名视图:
RENAME TABLE pricelist TO pricelist_table;
CREATE VIEW pricelist AS SELECT * FROM pricelist_table;
REVOKE ALL ON pricelist_table FROM public;
GRANT ALL ON pricelist TO public;
现在做一个插入触发器来更新同一张表的其他行
视图的基础(在此示例中是为了避免从/到日期重叠)。
通过兼顾视图和表名,您还可以避免关于
更新子查询中引用的表(触发器中的第二次更新)。
CREATE TRIGGER ins_pricelist INSTEAD OF INSERT ON pricelist
REFERENCING new AS new
FOR EACH ROW (
-- write the data to the real table
insert into pricelist_table
values(new.item,new.valid_from,new.valid_to,new.price),
-- close off the previous price row by setting its "valid_to" date
update pricelist_table set valid_to = new.valid_from - 1
where item = new.item and valid_from < new.valid_from
and ( valid_to >= new.valid_from or valid_to is null ),
-- if the row we're inserting has a null "valid_to" date,
-- set it to the day before the next "valid_from" date
update pricelist set valid_to =
(select min(valid_from)-1 from pricelist_table
where item = new.item and valid_from > new.valid_from)
where item = new.item and valid_from = new.valid_from
and valid_to is null
);
在支持带有触发器引用的过程的更高版本 (11+) 上,
更好和更易于维护的方法是调用单独的存储过程
来自触发器:
CREATE PROCEDURE write_pricelist()
REFERENCING old AS old new AS new FOR pricelist;
-- do all your inserts, updates, etc to pricelist_table here ...
END PROCEDURE;
-- These triggers all call the same stored procedure (above).
CREATE TRIGGER ins_pricelist INSTEAD OF INSERT ON pricelist
REFERENCING new AS new
FOR EACH ROW (
EXECUTE PROCEDURE write_pricelist() WITH TRIGGER REFERENCES
);
CREATE TRIGGER upd_pricelist INSTEAD OF UPDATE ON pricelist
REFERENCING new AS new old AS old
FOR EACH ROW (
EXECUTE PROCEDURE write_pricelist() WITH TRIGGER REFERENCES
);
CREATE TRIGGER del_pricelist INSTEAD OF DELETE ON pricelist
REFERENCING old AS old
FOR EACH ROW (
EXECUTE PROCEDURE write_pricelist() WITH TRIGGER REFERENCES
);