我们最近已从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这样做,先前的声明就不会:
开始无限循环?
更新:
通过Jeffrey的链接,我在Oracle文档中发现了这一点:
默认情况下,Oracle保证语句级别的读取一致性。单个查询返回的数据集相对于单个时间点是一致的。
作者在他的帖子中也发表了评论:
有人可能会争辩说,为什么Oracle对于出现在SQL语句中的重复函数调用不保证这种“语句级读取一致性”。就我而言,它可能被认为是一个错误。但这就是目前的工作方式。
我是否假定Oracle 10和11版本之间的行为已更改,这是正确的吗?
首先,
不启动无限循环,因为查询将看不到插入的数据-仅显示语句开始时已存在的数据。新行仅对后续语句可见。
这很好地解释了这一点:
当Oracle退出当前正在执行update语句的SQL引擎并调用该函数时,则此函数-就像行后更新触发器一样- 会看到执行更新过程中存在的EMP中间状态陈述。这意味着函数调用的返回值在很大程度上取决于行被更新的顺序。