[英]ORA-04091 table is Mutating
i have the following 2 table: runs:我有以下 2 个表:运行:
+--------+-------------+
| run_id | status |
+========+=============+
| 1 | active |
+--------+-------------+
| 2 | new |
+--------+-------------+
and orders:和命令:
+----------+--------+--------------+
| order_id | run_id | order_status |
+==========+========+==============+
| 1 | 1 | finished |
+----------+--------+--------------+
| 2 | 1 | finished |
+----------+--------+--------------+
| 3 | 1 | active |
+----------+--------+--------------+
| 4 | 2 | new |
+----------+--------+--------------+
| 5 | 2 | active |
+----------+--------+--------------+
| 6 | 2 | active |
+----------+--------+--------------+
it is requested to implement the following logic: when all orders in the run have the same status the run status should be updated (to be the same as its orders) .要求实现以下逻辑:当运行中的所有订单具有相同状态时,运行状态应被更新(与其订单相同) 。
for example when the the order_id = 3
status is set to 'finished'
then the run_id=1
status should be set to 'finished'
too.例如,当
order_id = 3
状态设置为'finished'
, run_id=1
状态也应设置为'finished'
。 the same with order_id = 4
when it takes the status 'active'
then run_id = 2 should
set to 'active'
too.与
order_id = 4
相同,当它采用'active'
状态时, run_id = 2 should
设置为'active'
。
the procedure responsible of checking orders status and updating run status accordingly:负责检查订单状态并相应更新运行状态的程序:
CREATE OR REPLACE PROCEDURE check_and_update_run_status (in_order_id IN orders.order_id%TYPE,
in_run_id IN runs.run_id%TYPE,
in_order_status IN orders.order_status%TYPE)
AS
v_update_run VARCHAR2(1) := 'N';
BEGIN
/*query the table ORDERS and check if all orders in the given in_run_id having the same status as in_order_status: */
SELECT CASE
WHEN NOT EXISTS ( SELECT *
FROM ( SELECT order_id,
order_status
FROM orders
WHERE run_id = in_run_id )
WHERE order_status <> in_order_status )
THEN 'Y'
END
INTO v_update_run
FROM dual;
IF v_update_run THEN
UPDATE runs
SET run_status = in_order_status
WHERE run_id = in_run_id;
END IF;
END check_and_update_run_status;
and I've created the trigger我已经创建了触发器
CREATE OR REPLACE TRIGGER trigger1
AFTER INSERT OR UPDATE OF order_status ON orders FOR EACH ROW
BEGIN
check_and_update_run_status( in_order_id => :new.order_id,
in_run_id => :new.run_id,
in_po_status => :new.order_status );
END;
the logic is failing because the error: ORA-04091: table ORDERS is mutating, trigger/function may not see it
.逻辑失败,因为错误:
ORA-04091: table ORDERS is mutating, trigger/function may not see it
。 the trigger is calling a procedure which querying the same table the trigger is invoked against.触发器正在调用一个过程,该过程查询触发器被调用的同一个表。
what would the best way to solve such a problem?解决此类问题的最佳方法是什么?
There would be other ways to resolve mutating trigger
but I would try to leverage the feature of compound trigger
.还有其他方法可以解决
mutating trigger
但我会尝试利用compound trigger
的功能。 Having said that, we should try to avoid triggers
generally if possible and in your case call the procedure somewhere in other program units or in application code during the update of order status
column in orders
table as I see there is no dependency for each row here and we need to update against the run_id
and not order_id
.话虽如此,如果可能的话,我们应该尽量避免通常的
triggers
并且在您的情况下,在更新orders
表中的orders
order status
列期间,在其他程序单元或应用程序代码中的某处调用该过程,因为我看到这里的每一行都没有依赖性我们需要针对run_id
而不是order_id
进行更新。
Having said that I made some changes to the procedure as we don't need order_id parameter in this use case话虽如此,我对程序进行了一些更改,因为在此用例中我们不需要 order_id 参数
CREATE OR REPLACE PROCEDURE check_and_update_run_status
(
in_run_id IN runs.run_id%TYPE
,in_order_status IN orders.order_status%TYPE
) AS
v_update_run VARCHAR2(1) := 'N';
BEGIN
/*query the table ORDERS and check if all orders in the given in_run_id having the same status as in_order_status: */
SELECT CASE
WHEN NOT EXISTS (SELECT *
FROM (SELECT order_id
,order_status
FROM orders
WHERE run_id = in_run_id)
WHERE order_status <> in_order_status) THEN
'Y' ELSE 'N'
END
INTO v_update_run
FROM dual;
IF v_update_run = 'Y'
THEN
UPDATE runs
SET status = in_order_status
WHERE run_id = in_run_id;
END IF;
END check_and_update_run_status;
/
And create the compound trigger
and call the procedure as,并创建
compound trigger
并将过程调用为,
CREATE OR REPLACE TRIGGER trigger1
FOR INSERT OR UPDATE OF order_status ON orders
COMPOUND TRIGGER
--table type to store the status index by run_id value
TYPE table_a_row_data_t IS TABLE OF orders.order_status%TYPE INDEX BY PLS_INTEGER;
-- global variable for the compound trigger
g_row_level_data table_a_row_data_t;
AFTER EACH ROW IS
BEGIN
IF NOT g_row_level_data.exists(:new.run_id)
THEN
g_row_level_data(:new.run_id) := :new.order_status;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
--loop through all run_id and update the status by calling the procedure
--here I used collection.first..collection.last as the index is run_id itself
FOR runid IN g_row_level_data.first .. g_row_level_data.last
LOOP
check_and_update_run_status(in_run_id => runid
,in_order_status => g_row_level_data(runid));
END LOOP;
END AFTER STATEMENT;
END trigger1;
/
Please test and see if it meets your requirement.请测试,看看它是否符合您的要求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.