gpt4 book ai didi

oracle - "ORA-14450: attempt to access a transactional temp table already in use"在复合触发器中

转载 作者:行者123 更新时间:2023-12-01 06:30:48 27 4
gpt4 key购买 nike

我有一张表,可以为一个帐户保存许多记录:不同的金额。

ACCOUNTID | AMOUNT
id1 | 1
id1 | 2
id2 | 3
id2 | 4

每次插入/更新/删除此表中的记录时,我们都需要评估总量,以了解是否应该触发事件(通过将数据插入另一个表)。该金额是根据此表中存在的记录(每个帐户)的总和计算的。

数量的计算应该使用记录的新值,但我们也需要旧值来检查某些条件(例如旧值是 X - 新值是 Y:如果 [X<=threshold and Y>threshold] then trigger事件通过将记录插入另一个表)。

所以为了计算和触发事件,我们在这个表上创建了一个触发器。像这样的东西:
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

  • 为了避免基于解决方案 1 和 2 的“表正在变异”错误,我使用了复合触发器和全局临时表的组合。

    现在我们有一个复合触发器,它使用一些全局临时表来存储来自 :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:尝试访问事务性临时文件”表已在使用'?

    最佳答案

    你应该仔细检查你的代码没有使用自治事务来访问临时表数据:

    SQL> create global temporary table t (x int) on commit delete rows
    2 /

    SQL> insert into t values(1)
    2 /

    SQL> declare
    2 pragma autonomous_transaction;
    3 begin
    4 insert into t values(1);
    5 commit;
    6 end;
    7 /
    declare
    *
    error in line 1:
    ORA-14450: attempt to access a transactional temp table already in use
    ORA-06512: error in line 4

    关于oracle - "ORA-14450: attempt to access a transactional temp table already in use"在复合触发器中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22096184/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com