ORA-04091 table 正在发生变化
ORA-04091 table is Mutating
我有以下 2 table:
运行s:
+--------+-------------+
| run_id | status |
+========+=============+
| 1 | active |
+--------+-------------+
| 2 | new |
+--------+-------------+
和订单:
+----------+--------+--------------+
| order_id | run_id | order_status |
+==========+========+==============+
| 1 | 1 | finished |
+----------+--------+--------------+
| 2 | 1 | finished |
+----------+--------+--------------+
| 3 | 1 | active |
+----------+--------+--------------+
| 4 | 2 | new |
+----------+--------+--------------+
| 5 | 2 | active |
+----------+--------+--------------+
| 6 | 2 | active |
+----------+--------+--------------+
要求实现以下逻辑:当运行中的所有订单具有相同状态时,应更新运行状态(与其相同订单).
例如,当 order_id = 3
状态设置为 'finished'
时,run_id=1
状态也应设置为 'finished'
。与 order_id = 4
相同,当它采用状态 'active'
然后 run_id = 2 should
也设置为 'active'
。
负责检查订单状态并相应地更新 运行 状态的程序:
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;
我已经创建了触发器
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;
逻辑失败,因为错误:ORA-04091: table ORDERS is mutating, trigger/function may not see it
。
触发器正在调用一个过程,该过程查询相同的 table 触发器被调用。
解决此类问题的最佳方法是什么?
还有其他方法可以解决 mutating trigger
但我会尝试利用 compound trigger
的功能。话虽如此,我们应该尽可能避免 triggers
,在您的情况下,在更新 orders
中的 order status
列期间,在其他程序单元或应用程序代码中的某处调用该过程 table 如我所见,这里的每一行都没有依赖关系,我们需要针对 run_id
而不是 order_id
.
进行更新
话虽如此,我对程序做了一些更改,因为在此用例中我们不需要 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;
/
并创建 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;
/
请测试是否符合您的要求。
我有以下 2 table: 运行s:
+--------+-------------+
| run_id | status |
+========+=============+
| 1 | active |
+--------+-------------+
| 2 | new |
+--------+-------------+
和订单:
+----------+--------+--------------+
| order_id | run_id | order_status |
+==========+========+==============+
| 1 | 1 | finished |
+----------+--------+--------------+
| 2 | 1 | finished |
+----------+--------+--------------+
| 3 | 1 | active |
+----------+--------+--------------+
| 4 | 2 | new |
+----------+--------+--------------+
| 5 | 2 | active |
+----------+--------+--------------+
| 6 | 2 | active |
+----------+--------+--------------+
要求实现以下逻辑:当运行中的所有订单具有相同状态时,应更新运行状态(与其相同订单).
例如,当 order_id = 3
状态设置为 'finished'
时,run_id=1
状态也应设置为 'finished'
。与 order_id = 4
相同,当它采用状态 'active'
然后 run_id = 2 should
也设置为 'active'
。
负责检查订单状态并相应地更新 运行 状态的程序:
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;
我已经创建了触发器
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;
逻辑失败,因为错误:ORA-04091: table ORDERS is mutating, trigger/function may not see it
。
触发器正在调用一个过程,该过程查询相同的 table 触发器被调用。
解决此类问题的最佳方法是什么?
还有其他方法可以解决 mutating trigger
但我会尝试利用 compound trigger
的功能。话虽如此,我们应该尽可能避免 triggers
,在您的情况下,在更新 orders
中的 order status
列期间,在其他程序单元或应用程序代码中的某处调用该过程 table 如我所见,这里的每一行都没有依赖关系,我们需要针对 run_id
而不是 order_id
.
话虽如此,我对程序做了一些更改,因为在此用例中我们不需要 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;
/
并创建 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;
/
请测试是否符合您的要求。