函数从update语句成功调用,但从select语句调用时出错

时间:2015-05-10 14:14:54

标签: oracle function plsql

// Your own class Int
class Int { ... }

Int x = new Int(12);
int y = x; // auto-unboxing, not possible with Int

运行以下区块后,我会在屏幕上看到什么?

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;

我认为第2行和第3行会抛出错误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行引发了异常。谁能解释第2行和第3行之间的区别?

1 个答案:

答案 0 :(得分:0)

第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);

在您发表评论并通过测试验证后,您所说的更新无效,因为它会抛出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