Oracle 用于自定义函数参数惰性求值/短路的语法

Oracle 用于自定义函数参数惰性求值/短路的语法,oracle,plsql,lazy-evaluation,short-circuiting,Oracle,Plsql,Lazy Evaluation,Short Circuiting,Oracle定义了几个结构,这些结构使用了看起来像是惰性评估,但实际上是短路的内容 例如: x := case when 1 = 2 then count_all_prime_numbers_below(100000000) else 2*2 end; create or replace package constants is c_is_trace_enabled constant boolean := false; end; / declare

Oracle定义了几个结构,这些结构使用了看起来像是惰性评估,但实际上是短路的内容

例如:

x := case when 1 = 2 then count_all_prime_numbers_below(100000000)
          else 2*2
     end;
create or replace package constants is
    c_is_trace_enabled constant boolean := false;
end;
/

declare
    v_dummy number;
begin
    $if constants.c_is_trace_enabled $then
        v_dummy := sleep(1);
        This line of code does not even need to be valid!
        (Until you change the constant anyway)
    $else
        null;
    $end
end;
/
永远不会调用函数count_all(…)

但是,我更感兴趣的是看起来像常规函数调用的语法:

x := coalesce(null, 42, hundreth_digit_of_pi());
由于coalesce不是一个常规函数,而是一个看起来像一个的语法糖,因此将不会调用\u pi()的第一百位\u,因为调用该函数时会计算常规参数

问题是: 是否可以在plsql中定义一个行为方式相同的自定义过程/函数

如果你不相信,我会举一个有用的例子:

我们使用“框架”进行日志记录。 要跟踪称为过程的内容,请执行以下操作:

trace_something('A text to be saved somewhere');
Trace_something是执行以下步骤的“pragma自治事务”过程: 1.请参阅,如果启用了任何跟踪方法(例如file/db table) 2.对于每个启用的方法,使用该方法将参数保存在某个位置。 3.如果已将其保存到DB,请提交

当构建要跟踪的实际字符串时,会出现一个问题,这可能需要相当长的时间,如果一开始没有启用跟踪,我们也不想花费时间

以伪代码的形式,目标是:

procedure lazily_trace_something(some_text lazily_eval_type) {
    if do_i_have_to_trace() = TRUE then
        trace_something(evaluate(some_text));
    else
        NULL; -- in which case, some_text doesn't get evaluated     
    end if;
}


/*

*/

lazily_trace_something(first_50_paragraphs_of_lorem_ipsum(a_rowtype_variable));
可以在plsql中执行吗?

可以使用ref游标、条件编译或execute immediate(部分)实现惰性计算。ANYDATA类型可用于传递通用数据

参考光标 Ref游标可以使用静态SQL语句打开,作为参数传递,并且在需要时才会执行

虽然这确实回答了你关于懒惰评估的问题,但我不确定它是否真的实用。这不是ref游标的预期用途。而且,将SQL添加到所有内容可能并不方便

首先,为了证明slow函数正在运行,创建一个只需休眠几秒钟的函数:

grant execute on sys.dbms_lock to <your_user>;

create or replace function sleep(seconds number) return number is
begin
    dbms_lock.sleep(seconds);
    return 1;
end;
/
此函数可以通过执行SQL语句来执行工作。SQL语句必须返回某些内容,即使您可能不需要返回值

create or replace procedure trace_something(p_cursor sys_refcursor) is
    v_dummy varchar2(1);
begin
    if do_i_have_to_trace then
        fetch p_cursor into v_dummy;
    end if;
end;
/
现在创建一个过程,该过程将始终调用跟踪,但不一定要花时间评估参数

create or replace procedure lazily_trace_something(some_number in number) is
    v_cursor sys_refcursor;
begin
    open v_cursor for select sleep(some_number) from dual;
    trace_something(v_cursor);
end;
/
默认情况下,它正在工作,速度很慢:

--Takes 2 seconds to run:
begin
    lazily_trace_something(2);
end;
/
但是,当您更改
DO\u I\u HAVE\u TO\u TRACE
以返回false时,过程很快,即使它传递的参数很慢

create or replace function do_i_have_to_trace return boolean is
begin
    return false;
end;
/

--Runs in 0 seconds.
begin
    lazily_trace_something(2);
end;
/
其他选择 条件编译通常用于启用或禁用检测。例如:

x := case when 1 = 2 then count_all_prime_numbers_below(100000000)
          else 2*2
     end;
create or replace package constants is
    c_is_trace_enabled constant boolean := false;
end;
/

declare
    v_dummy number;
begin
    $if constants.c_is_trace_enabled $then
        v_dummy := sleep(1);
        This line of code does not even need to be valid!
        (Until you change the constant anyway)
    $else
        null;
    $end
end;
/
您可能还需要重新考虑动态SQL。编程风格和一些语法上的甜言蜜语在这里会有很大的不同。简而言之,可选的引号语法和简单模板可以使动态SQL更具可读性。有关更多详细信息,请参阅我的帖子

传递通用数据 任何类型都可以用来存储和传递任何可以想象的数据类型。不幸的是,每种行类型都没有本机数据类型。您需要为每个表创建一个类型。这些自定义类型非常简单,所以必要时可以自动执行步骤

create table some_table(a number, b number);
create or replace type some_table_type is object(a number, b number);

declare
    a_rowtype_variable some_table_type;
    v_anydata anydata;
    v_cursor sys_refcursor;
begin
    a_rowtype_variable := some_table_type(1,2);
    v_anydata := anydata.ConvertObject(a_rowtype_variable);
    open v_cursor for select v_anydata from dual;
    trace_something(v_cursor);
end;
/

我不这么认为。关于函数参数,可能与您所问的类似。@Glenn感谢您的评论,不幸的是,它没有为我切掉它(在我的具体案例中,“first_50_段落”以行类型作为参数,并且不全局可见,我相应地编辑了问题)。我也尽量避免立即执行。