sql >> データベース >  >> RDS >> Oracle

動的入力パラメーターを「即時実行」に渡す

    バインド値の文字列リストを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;]';
    



    1. 多くのselectステートメントの結果を1つのカスタムテーブルとして返す方法

    2. カレンダー用のmySQLのwhileループ内のjson_encode()配列

    3. Laravelの移行にインデックスが存在するかどうかを確認するにはどうすればよいですか?

    4. HerokuのRailsアプリはPostgreSQLデータベースに書き込めず、読み取りのみ