При чтении статьи встретилась знакомая по триггерам ошибка mutating table error в неожиданном месте, интересный способ ее обхода и совершенно неожиданные результаты.
CREATE TABLE a(n NUMBER);
Table created
INSERT INTO a VALUES(100);
1 row inserted
INSERT INTO a VALUES(200);
1 row inserted
INSERT INTO a VALUES(300);
1 row inserted
COMMIT;
Commit complete
CREATE OR REPLACE FUNCTION a_avg RETURN NUMBER AS
l_avg NUMBER;
BEGIN
SELECT AVG(n) INTO l_avg FROM a;
dbms_output.put_line('avg=' || l_avg);
RETURN l_avg;
END a_avg;
/
Function created
SHOW ERRORS
No errors for FUNCTION SYSTEM.A_AVG
UPDATE a SET n = a_avg();
UPDATE a SET n = a_avg()
ORA-04091: table SYSTEM.A is mutating, trigger/function may not see it
ORA-06512: at "SYSTEM.A_AVG", line 4
Мы создали простую таблицу и функцию, которая считает среднее значение по этой таблице. С помощью функции мы попробуем усреднить все значения.
В результате мы получаем ORA-04091 без каких-либо триггеров
Далее в статье приводится способ обхода через loopback database link. Модифицируем немного функцию и вставляем @loopback
CREATE OR REPLACE FUNCTION a_avg_loopback RETURN NUMBER AS
l_avg NUMBER;
BEGIN
SELECT AVG(n) INTO l_avg FROM a@loopback;
dbms_output.put_line('avg=' || l_avg);
RETURN l_avg;
END a_avg_loopback;
/
Function created
SHOW ERRORS
No errors for FUNCTION SYSTEM.A_AVG_LOOPBACK
UPDATE a SET n = a_avg_loopback();
avg=200
avg=233.333333333333333333333333333333333333
avg=244.444444444444444444444444444444444444
3 rows updated
ROLLBACK;
Rollback complete
Ошибка пропала, но… Через loopback мы смогли увидеть данные, которые еще не закоммичены. Этакий dirty read, но скорее всего мы просто присоединяемся к той же транзакции. Причем, как видно, результат для каждой строчки считается с учетом обновленных строк.
UPD: дальнейшее исследование показало, что loopback link вообще не создал отдельной сессии.
UPD2: сессия создается, но ровно 1 раз за сессию. Исследование тут
Комментариев нет:
Отправить комментарий