小编典典

由功能引起的Oracle 11中的表突变

sql

我们最近已从Oracle 10升级到Oracle
11.2。升级后,我开始看到由函数而不是触发器引起的变异表错误(我从未遇到过)。这是旧的代码,可以在Oracle的早期版本中使用。

这是导致错误的情况:

create table mutate (
    x NUMBER,
    y NUMBER
);

insert into mutate (x, y)
values (1,2);

insert into mutate (x, y)
values (3,4);

我创建了两行。现在,我将通过调用以下语句将行加倍:

insert into mutate (x, y)
select x + 1, y + 1 
from mutate;

严格来说,复制该错误不是必须的,但稍后对我的演示很有帮助。因此,表的内容现在看起来像这样:

X,Y
1,2
3,4
2,3
4,5

一切都很好。现在是有趣的部分:

create or replace function mutate_count
return PLS_INTEGER
is
    v_dummy PLS_INTEGER;
begin
    select count(*) 
    into v_dummy
    from mutate;

    return v_dummy;
end mutate_count;
/

我创建了一个查询表并返回计数的函数。现在,我将其与INSERT语句结合起来:

insert into mutate (x, y)
select x + 2, y + 2
from mutate
where mutate_count() = 4;

结果?这个错误:

ORA-04091: table MUTATE is mutating, trigger/function may not see it
ORA-06512: at "MUTATE_COUNT", line 6

所以我知道是什么原因导致了错误,但是我对 为什么 感到好奇。Oracle是否不执行SELECT,检索结果集, 然后
对这些结果进行批量插入?如果在查询完成之前就已经插入了记录,我只会期望发生变异表错误。但是,如果Oracle这样做,先前的声明就不会:

insert into mutate (x, y)
select x + 1, y + 1 
from mutate;

开始无限循环?

更新:

通过Jeffrey的链接,我在Oracle文档中发现了这一点:

默认情况下,Oracle保证语句级别的读取一致性。单个查询返回的数据集相对于单个时间点是一致的。

作者在他的帖子中也发表了评论:

有人可能会争辩说,为什么Oracle对于出现在SQL语句中的重复函数调用不保证这种“语句级读取一致性”。就我而言,它可能被认为是一个错误。但这就是目前的工作方式。

我是否假定Oracle 10和11版本之间的行为已更改,这是正确的吗?


阅读 234

收藏
2021-04-07

共1个答案

小编典典

首先,

insert into mutate (x, y)
select x + 1, y + 1 
from mutate;

不启动无限循环,因为查询将看不到插入的数据-仅显示语句开始时已存在的数据。新行仅对后续语句可见。

很好地解释了这一点:

当Oracle退出当前正在执行update语句的SQL引擎并调用该函数时,则此函数-就像行后更新触发器一样-
会看到执行更新过程中存在的EMP中间状态陈述。这意味着函数调用的返回值在很大程度上取决于行被更新的顺序。

2021-04-07