Oracle 定义了几种结构,这些结构利用了看似惰性求值但实际上是短路的结构。
例如:
x := case when 1 = 2 then count_all_prime_numbers_below(100000000)
else 2*2
end;
函数 count_all(...) 永远不会被调用。
但是,我更感兴趣的是看起来像常规函数调用的语法:
x := coalesce(null, 42, hundreth_digit_of_pi());
Hundreth_digit_of_pi() 不会被调用,因为合并不是一个常规函数,而是一种看起来像语法糖的语法糖 - 对于常规函数,参数在调用函数时会被评估。
问题是: 是否可以在 plsql 中定义一个具有相同行为方式的自定义过程/函数?
如果您不相信我会举一个有用的例子:
我们使用“框架”进行日志记录。 要跟踪您称为过程的内容:
trace_something('A text to be saved somewhere');
Trace_something 是“pragma 自治事务”过程,执行以下步骤: 1. 查看是否启用了任何跟踪方法(例如文件/数据库表) 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中完成?
最佳答案
延迟计算可以(部分)使用引用游标、条件编译或立即执行来实现。 ANYDATA 类型可用于传递通用数据。
引用光标
引用游标可以使用静态 SQL 语句打开,作为参数传递,并且在需要时才会执行。
虽然这确实回答了您关于惰性评估的问题,但我不确定它是否真正实用。这不是引用游标的预期用途。而且必须向所有内容添加 SQL 可能并不方便。
首先,为了证明慢速函数正在运行,创建一个仅休眠几秒钟的函数:
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;
/
创建一个函数来确定是否需要评估:
create or replace function do_i_have_to_trace return boolean is
begin
return true;
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_I_HAVE_TO_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;
/
其他选项
条件编译更传统地用于启用或禁用检测。例如:
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 更具可读性。更多详情请参阅我的帖子here .
传递通用数据
ANY 类型可用于存储和传递任何可以想象的数据类型。不幸的是,每种行类型都没有本地数据类型。您需要为每个表创建一个类型。这些自定义类型非常简单,因此如果需要,可以自动执行该步骤。
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;
/
关于oracle - 函数参数的自定义惰性求值/短路的语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49931697/