从 update 语句成功调用函数,但从 select 语句调用时出错
Posted
技术标签:
【中文标题】从 update 语句成功调用函数,但从 select 语句调用时出错【英文标题】:Function called successfully from update statement but gets error when called from select statement 【发布时间】:2015-05-10 14:14:54 【问题描述】:CREATE TABLE plch_test
(
x NUMBER
, y VARCHAR2 (3)
);
BEGIN
INSERT INTO plch_test
VALUES (1, 'NO');
INSERT INTO plch_test
VALUES (2, NULL);
COMMIT;
END;
/
CREATE OR REPLACE FUNCTION silly_function (p_x NUMBER)
RETURN VARCHAR2
IS
l_y plch_test.y%TYPE;
BEGIN
SELECT t.y
INTO l_y
FROM plch_test t
WHERE t.x = silly_function.p_x;
RETURN l_y;
END;
运行以下代码块后,我会在屏幕上看到什么?
BEGIN
UPDATE plch_test SET y = 'YES' WHERE silly_function (x) != 'YES'; -- Line 2 Function call
select silly_function(x) from dual; -- Line 3
DBMS_OUTPUT.put_line ('Updated=' || SQL%ROWCOUNT);
EXCEPTION
WHEN VALUE_ERROR
THEN
DBMS_OUTPUT.put_line ('VALUE_ERROR');
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('OTHER_ERROR');
END;
/
我认为第 2 行和第 3 行会抛出错误 X: invalid identifier
。但是,当我执行上面的脚本时,第 2 行执行并且第 3 行抛出了异常。谁能解释一下第 2 行和第 3 行的区别?
【问题讨论】:
【参考方案1】:第 2 行:
UPDATE plch_test SET y = 'YES' WHERE silly_function (x) != 'YES';
... 将更新表中的一行,将y
设置为'NO'
的行。另一行为空,因此不等于或不等于任何内容。 x
是一个有效的标识符,因为您指的是表plch_test
,它有一个具有该名称的列。为每一行调用该函数,该行的值为x
。
第 3 行:
select silly_function(x) from dual;
... 会出错,因为您没有选择任何内容,当然您必须在 PL/SQL 上下文中执行此操作。但是你也没有 x
范围内的变量,所以它仍然会出错。
所以这不会出错(希望;未经测试):
DECLARE
l_x plch_test.x%type;
l_y plch_test.y%type;
BEGIN
UPDATE ... ;
l_x := 1;
select silly_function(l_x) into l_y from dual;
END;
捕捉和压制你得到的实际异常并没有帮助自己,否则这将非常明显。
你也不需要在这里选择,因为你可以做一个作业:
l_y := silly_function(l_x);
在您的 cmets 并通过测试进行验证后,您所说的更新不起作用,因为它会抛出 ORA-04091: table SCHEMA.PLCH_TEST is mutating, trigger/function may not see it
。您的更新是根据y
的当前值决定给y
什么值。但是在更新语句中间的“当前值”是什么意思? Oracle 应该在函数内部的选择中使用 y
的哪个值 - 更新前或更新后的值?这个例外实际上是在说它不能安全且一致地选择,所以你要求它做一些不安全和不一致的事情,这是 RDBMS 真正试图避免的。
解决此问题的一种方法是将查询/函数调用与更新分开,例如带有光标的循环:
DECLARE
CURSOR c IS
SELECT * FROM plch_test
WHERE silly_function (x) != 'YES'
FOR UPDATE;
BEGIN
--UPDATE plch_test SET y = 'YES' WHERE silly_function (x) != 'YES';
FOR r IN c LOOP
UPDATE plch_test SET y = 'YES' WHERE CURRENT OF c;
END LOOP;
END;
/
anonymous block completed
SELECT * FROM plch_test;
X Y
---------- ---
1 YES
2
【讨论】:
但是,显然第 2 行抛出mutating error
并且不会更新任何行。感谢您指出在使用select
时使用INTO
子句。我使用了select silly_function(x) into y from dual;
并得到了同样的错误(无效的X
标识符)。我知道,可以做同样的事情,你在最后写的。但是,我只是想知道为什么从select
调用时调用的相同函数不起作用?
@jWeaver - 由于您没有 into
子句,因此该函数无法从 select 中工作,而且您似乎仍然没有名为 x
的变量。这就是我定义和使用l_x
变量的原因。你为什么现在谈论变异表 - 你在触发器中得到这个问题,而不是你所展示的匿名块。
请执行上面的块。我已经执行了上面的块并看到了错误。
@jWeaver - 好的,我专注于选择。您说更新在问题中有效,但正是它导致了变异表错误。更新了解决方法,尽管这不是您最初询问的内容。
我的问题是,当我评论 line 2
然后 select silly_function(x) into y from dual;
抛出错误 X : invalid identifier
而注释掉 line 3
update
声明有效。当我说工作时,这意味着我没有收到invalid identifier
错误,而是出现了变异错误。这意味着函数是从update
语句调用的。正确的 ?然后问题出现了,为什么 update throws exception 而不是 invalid identifier
error ?我们使用相同的参数调用相同的函数。以上是关于从 update 语句成功调用函数,但从 select 语句调用时出错的主要内容,如果未能解决你的问题,请参考以下文章