我有一个表可以容纳一个帐号的多个记录:不同的金额。“ORA-14450:尝试访问已在使用的事务性临时表”在复合触发器中
ACCOUNTID | AMOUNT
id1 | 1
id1 | 2
id2 | 3
id2 | 4
每一个在这个表中的记录插入时间/更新/删除,我们需要以评估的总量知道我们是否应该触发或不是一个事件(通过插入数据到另一个表)。金额根据此表中记录的总和(每个帐户)计算。
金额的计算应该使用记录的新值,但为了检查某些条件,我们也需要旧值(例如,旧值为X - 新值为Y:如果[X < = threshold并且Y>阈值],然后通过将记录插入另一个表中来触发事件)。
所以为了计算和触发事件,我们在这个表上创建了一个触发器。事情是这样的:
CREATE OR REPLACE TRIGGER <trigger_name>
AFTER INSERT OR UPDATE OR DELETE OF MOUNT ON <table_name>
FOR EACH ROW
DECLARE
BEGIN
1. SELECT SUM(AMOUNT) INTO varSumAmounts FROM <table_name> WHERE accountid = :NEW.accountid;
2. varAmount := stored_procedure(varSumAmounts);
END <trigger_name>;
的问题是,声明1,引发以下错误:“ORA-04091:表变异,触发/功能可能无法看到它。
我们尝试了以下,但没有成功(同样的异常/错误)选择已ROWID比目前的ROWID不同的所有记录:
(SELECT SUM(AMOUNT)
INTO varSumAmounts
FROM <table_name>
WHERE accountId = :NEW.accountid
AND rowid <> :NEW.rowid;)
为了计算量的所有行的量的总和在当前行+当前行的数量(我们在触发器的上下文中)旁边。
我们寻找其他的解决方案,我们发现了一些,但我不知道哪个是更好的,什么是对他们每个人的缺点(尽管它们在某种程度上相似)
使用复合触发器
http://www.oracle-base.com/articles/9i/mutating-table-exceptions.php
http://asktom.oracle.com/pls/asktom/ASKTOM.download_file?p_file=6551198119097816936
为了避免基于解决方案的'table is mutating'错误,我使用了复合触发器与全局临时表的组合。
现在我们有一个复合触发器,它使用一些全局临时表来存储来自OLD和NEW伪相关数据的相关数据。基本上,我们做下的事情:使用选项创建
CREATE OR REPLACE TRIGGER trigger-name
FOR trigger-action ON table-name
COMPOUND TRIGGER
-------------------
BEFORE STATEMENT IS
BEGIN
-- Delete data from global temporary table (GTT) for which source is this trigger
-- (we use same global temporary tables for multiple triggers).
END BEFORE STATEMENT;
-------------------
AFTER EACH ROW IS
BEGIN
-- Here we have access to :OLD and :NEW objects.
-- :NEW and :OLD objects are defined only inside ROW STATEMENTS.
-- Save relevant data regarding :NEW and :OLD into GTT table to use it later.
END AFTER EACH ROW;
--------------------
AFTER STATEMENT IS
BEGIN
-- In this block DML operations can be made on table-name(the same table on which
--the trigger is created) safely.
-- Table is mutating error will no longer appear because this block is not for EACH ROW specific.
-- But we can't access :OLD and :NEW objects. This is the reason why in 'AFTER EACH ROW' we saved them in GTT.
-- Because previously we saved :OLD and :NEW data, now we can continue with our business logic.
-- if (oldAmount<=threshold && newAmount>threshold) then
-- trigger event by inserting record into another table
END AFTER STATEMENT;
END trigger-name;
/
全局临时表“ON COMMIT DELETE ROWS”,这样我保证,从这个表中的数据将在交易结束时进行清洗。 但是,发生此错误:'ORA-14450:尝试访问已在使用的事务性临时表'。
的问题是,应用程序使用分布式事务和Oracle文档中提到的是: “各种内部错误的,可以使用结合的全局临时表(这些GTT)当与分布式或XA事务报道 .. 。
临时表不支持任何分布式协议,因此XA协调事务。 最安全的选择是不使用分布式或XA事务中的临时表,因为它们在此上下文中的使用不受官方支持。 ...
全局临时表,可以安全使用,如果有使用它在数据库只有单支交易,但如果有回送数据库链接或涉及多个分支XA事务,则可能会出现问题,包括块损坏根据错误5344322。 “
值得一提的是,我不能避免XA事务或在同一张表上触发DML(这是触发器的主题)(修复数据模型不是一个可行的解决方案)。尝试使用而不是全局临时表触发器变量 - 一个集合(对象表),但我不确定这种方法。分布式事务是否安全?
在这种情况下,哪些其他解决方案可以解决最初的问题:'ORA-04091:表名正在变异,触发器/函数可能不会看到它',或者第二个:'ORA-14450:尝试访问一个事务性临时表已经在使用'?
什么在你的'AFTER触发器STATEMENT'的 “商业逻辑” 呢?以不需要临时表的另一种方式实现它是否可行(比如可能使用物化视图)?如果不是,那么是否可行(即使有点“黑客”)使用“真实”表而不是临时表(可能使用重复作业删除不再需要的行)。 –
处理每行之后保存的数据,并根据某些条件将记录插入另一个触发其他进程(通过JMS,这些进程有时与第三方连接)的表中。我不知道是否物化视图,黑客或真正的表可以是一个解决方案,通常我们尽量保持它尽可能简单。 –