我有一张表,其中存储某些条件以及输入参数,如下所示:
CONDITION | INPUT_PARAMS
---------------------------------------------------------
:p_end_date < :p_start_date | v_end_date, IN v_start_date
:p_joining_day = 'MONDAY' | v_joining_day
我想使用execute immediate
来评估条件。
select condition, input_param
into v_execute_condition, v_input_param
From table;
v_execute_statement :=
'IF '||v_execute_condition ||' '||
'THEN :o_flag := ''Y'';' ||' '||
'ELSE :o_flag := ''N'';' ||' '||
'END IF;';
v_execute_statement := 'BEGIN '||v_execute_statement||' END;';
dbms_output.put_line(v_execute_statement);
EXECUTE IMMEDIATE v_execute_statement USING IN input_param OUT v_flag;
这给我一个错误。如果我没有动态传递输入参数,它将起作用。
如何动态传递输入参数列表?
答案 0 :(得分:4)
您无法提供绑定值的字符串列表作为using
参数,因此,我看到的唯一方法是使用嵌套的动态SQL调用,这有点混乱,意味着必须在内部声明(并绑定)所有可能的参数。嵌套的动态语句。
declare
v_execute_statement varchar2(4000);
v_flag varchar2(1);
v_start_date date := date '2018-01-01';
v_end_date date := date '2018-01-31';
v_joining_day varchar2(9) := 'MONDAY';
begin
-- loop over all rows for demo
for rec in (
select condition, input_params
From your_table
)
loop
v_execute_statement := q'[
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
IF ]' || rec.condition || q'[ THEN
:o_flag := 'Y';
ELSE
:o_flag := 'N';
END IF;
END;^'
USING ]' || rec.input_params || q'[, OUT :v_flag;
END;]';
dbms_output.put_line('Statement: ' || v_execute_statement);
EXECUTE IMMEDIATE v_execute_statement
USING v_start_date, v_end_date, v_joining_day, OUT v_flag;
dbms_output.put_line('Result flag: ' || v_flag);
end loop;
end;
/
我在这里使用the alternative quoting mechanism来减少转义的单引号引起的混淆。有两个嵌套的引用级别-由q'[...]'
分隔的外层和由q'^...^'
分隔的内层,但是由于实际的表内容,如果遇到其他问题,则可以使用其他字符。将这些报价转义为两个级别将非常难看,并且难以遵循/正确使用。而且您还需要担心condition
字符串中的转义引号,这对于您提供的第二个示例的现有代码已经是一个问题,因为其中包含文本文字。
在显示运行结果后,显示了两个示例表行和虚拟日期/日期值:
Statement:
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
IF :p_end_date < :p_start_date THEN
:o_flag := 'Y';
ELSE
:o_flag := 'N';
END IF;
END;^'
USING v_end_date, IN v_start_date, OUT :o_flag;
END;
Result flag: N
Statement:
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
IF :p_joining_day = 'MONDAY' THEN
:o_flag := 'Y';
ELSE
:o_flag := 'N';
END IF;
END;^'
USING v_joining_day, OUT :o_flag;
END;
Result flag: Y
在生成的语句中要注意的第一件事是声明部分,该部分必须列出在input_params
中可能具有的所有可能的变量名,并从新的绑定变量中进行设置。您必须已经在主程序块/过程中知道了这些信息,它们是局部变量或更可能是过程参数。但它们都在此处重复,因为此时您还不知道需要哪个。
然后,该语句具有自己的内部动态SQL,该动态SQL本质上就是您最初正在执行的操作,但同时连接在input_params
字符串和condition
中。
这里重要的部分是报价。例如,在第一个引号中,:p_end_date
和:p_start_date
都在第二个引号内,在q'^...^'
内,因此它们与内部动态SQL绑定在一起,并带有来自本地v_end_date
中的本地v_start_date
和execute immediate
。
使用所有可能的变量名的绑定值执行整个生成的块,这些绑定值在保留数据类型的同时(通过v_start_date date := :v_start_date;
等提供局部变量的值);加上输出标记。
然后,该块仅使用相关的局部变量(现在具有绑定值)执行其内部execute immediate
语句;并且输出标志仍然是外部execute immediate
的绑定变量,因此外部块仍然可以看到其结果。
您会看到第二条生成的语句使用了不同的条件,并将变量和值绑定到第一条,并且在每种情况下均根据相关条件和参数对标志进行了评估。
顺便说一句,您可以改为使用case表达式来删除对:o_flag
的重复引用(这不是问题,但我觉得有些困惑):
v_execute_statement := q'[
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
:o_flag := CASE WHEN ]' || rec.condition || q'[ THEN 'Y' ELSE 'N' END;
END;^'
USING OUT :v_flag, ]' || rec.input_params || q'[;
END;]';