游标的替代

Z.S*_*mon 5 sql oracle plsql

我有一个函数,每次调用都会更改其值。我想在一个查询中多次使用此值,但是我不想多次执行它,因为我想从第一次调用中获取该值。我试过了:

SELECT RESULT value1,
       RESULT value2,
       RESULT value3
  FROM (SELECT function_invocation() RESULT
          FROM dual);
Run Code Online (Sandbox Code Playgroud)

但是每个VALUE列给我不同的值,这意味着该函数被多次调用。

一种替代方法是编写一个游标,但是我想知道纯SQL是否可行。

Jon*_*ler 6

有一些技巧可以防止Oracle不必要地重新执行功能。这个主题很困难,因为99.9%的时间我们依靠Oracle来自动重写查询以使其最佳运行。停止这些优化不是常见的任务。

从理论上讲,没有办法保证声明式SQL语句的操作顺序。实际上,有两种简单的技术可以帮助防止函数重新运行:标量子查询缓存和ROWNUM

首先,让我尝试重现该问题。单个值引用返回三个不同的数字。

create or replace function function_invocation return number is
begin
    return dbms_random.value;
end;
/

SELECT RESULT value1,
       RESULT value2,
       RESULT value3
  FROM (SELECT function_invocation() RESULT
          FROM dual);

VALUE1   VALUE2   VALUE3
------   ------   ------
0.3089   0.7103   0.2885
Run Code Online (Sandbox Code Playgroud)

重新编写查询以使用标量子查询似乎没有必要,但是这种形式使Oracle可以使用标量子查询缓存,这是Oracle避免重新运行代码的一种优化技术。现在,三列返回相同的值。

select result value1, result value1, result value1
from
(
    select (select function_invocation() from dual) result from dual
);

VALUE1   VALUE2   VALUE3
------   ------   ------
0.2450   0.2450   0.2450
Run Code Online (Sandbox Code Playgroud)

另外,我们可以通过添加ROWNUM伪列来防止优化转换:

SELECT RESULT value1,
       RESULT value2,
       RESULT value3
  FROM (SELECT function_invocation() RESULT, rownum
          FROM dual);

VALUE1   VALUE2   VALUE3
------   ------   ------
0.1678   0.1678   0.1678
Run Code Online (Sandbox Code Playgroud)

这些技术在实践中效果很好,可以使结果看起来正确。但是,有可能该函数在执行之前会秘密运行一次。额外的运行用于解析和缓存,并且不会影响结果。但是,如果您的副作用功能只能运行一次,那么您就必须跳过一些额外的障碍。

  • @ Z.Szymon [`ROWNUM`](https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/ROWNUM-Pseudocolumn.html#GUID-2E40EC12-3FCF-4A4F-B5F2-已记录6BC669021726),以影响操作的顺序以及优化程序如何转换语句,但是未记录此确切的用例。这不是完美的,但我认为这是对这类问题最不满意的解决方案。 (2认同)