【问题标题】:Mutating Table in Oracle 11 caused by a functionOracle 11中由函数引起的变异表
【发布时间】:2012-04-13 16:22:07
【问题描述】:

我们最近从 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 的链接,我在 the Oracle docs 找到了这个:

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

his post还有作者的评论:

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

我是否正确假设此行为在 Oracle 版本 10 和 11 之间发生了变化?

【问题讨论】:

    标签: sql oracle plsql oracle10g oracle11g


    【解决方案1】:

    语句级读取一致性和事务级读取一致性”。

    来自手册:

    "如果 SELECT 列表包含函数,则数据库应用 SQL 运行的 语句级别的语句级别读取一致性 在 PL/SQL 函数代码中,而不是在父 SQL 中 级别。例如,一个函数可以访问一个数据为 由另一个用户更改和提交。 对于每次执行 SELECT函数中,一个新的读一致性快照是 成立”

    “Oracle® 数据库概念”中解释了这两个概念:

    http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/consist.htm#sthref1955


    ->>> 更新

    ->>*OP关闭之后添加的部分

    规则

    技术规则,由 Kemp 先生(@jeffrey-kemp)很好地链接并由 Toon Koppelaars here 很好地解释,在“PL/Sql 语言参考 - 控制 PL/SQL 子程序的副作用"(你的函数违反了RNDS读取无数据库状态):

    当从 INSERT、UPDATE 或 DELETE 语句调用时,函数 无法查询或修改该语句修改的任何数据库表。

    如果函数查询或修改表,以及 DML 语句 在该表上调用该函数,然后 ORA-04091 (mutating-table 错误)发生。

    PL/SQL Functions that SQL Statements Can Invoke

    【讨论】:

    • 很高兴知道!除了性能考虑之外,这似乎是尽可能避免在 SQL 语句中调用函数的另一个好理由——它们违反了父 SQL 级别的快照规则。
    • @Dan-A。我使用它们来简化确定性函数中对行值的复杂计算,其中它们不违反 WNDS 和 RNDS(不写入数据状态并且不读取数据状态)。
    【解决方案2】:

    首先,

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

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

    This 解释的很好:

    当 Oracle 退出当前正在执行的 SQL 引擎时 update 语句,然后调用函数,然后调用这个函数——只是 就像行后更新触发器一样——看到中间状态 的 EMP,因为它们在执行更新语句期间存在。这 意味着我们的函数调用的返回值被大量调用 取决于行更新的顺序。

    【讨论】:

    • 好链接,谢谢!我将更新我的问题并将其保留一段时间,看看它是否会引发任何额外的讨论。
    • 这有帮助,但有一个澄清问题......该函数是否可以存在于从更新前触发器调用的包中?由于 Oracles 糟糕的触发器,我像许多其他人一样做噩梦。
    • @RichBianco,在这种情况下,模式级函数和包中定义的函数没有区别。从 SQL 调用时,同样的行为也适用。
    猜你喜欢
    • 2015-09-30
    • 2014-05-26
    • 2018-10-20
    • 1970-01-01
    • 2019-11-05
    • 2015-02-15
    • 2013-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多