バインド値の文字列リストを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;
/
代替の引用メカニズム
ここでは、エスケープされた一重引用符による混乱を減らします。引用には2つのネストされたレベルがあります-q'[...]'
で区切られた外側のレベル q'^...^'
で区切られた内側のもの 、ただし、実際のテーブルの内容が原因で問題が発生する場合は、他の文字を使用できます。これらの引用符を2つのレベルでエスケープすることは、非常に醜く、従う/正しくするのが困難です。また、condition
で引用符がさらにエスケープされることについても心配する必要があります。 文字列。これは、テキストリテラルが含まれているため、提供した2番目のサンプルの既存のコードですでに問題になっています。
2つのサンプルテーブル行と、実行からの出力の上に示したダミーの日付/日の値を使用すると、次のようになります。
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
生成されたステートメントで最初に注意するのは、declareセクションです。このセクションには、input_params
にある可能性のあるすべての変数名をリストする必要があります。 、および新しいバインド変数からそれらを設定します。これらは、ローカル変数またはより可能性の高いプロシージャ引数として、メインブロック/プロシージャですでに知っている必要があります。ただし、この時点ではどちらが必要かわからないため、これらはすべてここで複製されています。
次に、そのステートメントには独自の内部動的SQLがあります。これは基本的に元々行っていたものですが、input_params
に連結されます。 文字列とcondition
。
ここで重要なのは引用です。たとえば、最初の例では、両方の:p_end_date
および:p_start_date
q'^...^'
内の第2レベルの引用符内にあります 、したがって、ローカルのv_end_date
からの値を使用して、内部の動的SQLにバインドされます。 およびv_start_date
その内側のexecute immediate
から 。
生成されたブロック全体が、可能なすべての変数名のバインド値を使用して実行されます。これにより、ローカル変数の値が提供されます(v_start_date date := :v_start_date;
経由) など)データ型を保持しながら;プラス出力フラグ。
次に、そのブロックは内部のexecute immediate
を実行します 関連するローカル変数のみを使用するステートメント。現在はバインドされた値があります。そして、外部のexecute immediate
からのバインド変数である出力フラグ 、したがって、外側のブロックは引き続きその結果を見ることができます。
2番目に生成されたステートメントが異なる条件を使用し、変数と値を最初のステートメントにバインドし、それぞれの場合に関連する条件とパラメーターに基づいてフラグが評価されることがわかります。
ちなみに、:o_flag
への重複する参照を削除することができます (これは問題ではありませんが、少し混乱します)代わりにcase式を使用します:
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;]';