5

コードが再利用され続ける共用体を含む大量の SQL ステートメントがあります。「USING」のために変数を複数回繰り返さずに、単一のバインド変数を再利用する方法があるかどうかを知りたいと思っていました。

以下のコードは、「USING」行を「USING VAR1,VAR2,VAR1;」に変更するまで、「not all variables bound」を返します。

両方のインスタンスで :1 を参照しているので、それを避けたいと思っていました-何かアイデアはありますか?

declare
var1 number :=1;
var2 number :=2;
begin
execute immediate '
select * from user_objects 
where 
rownum = :1
OR rownum = :2  
OR rownum = :1 '
using var1,var2;
end;
/

編集: 追加情報については、where 条件のバンドルも生成するため、動的 SQL を使用しています。

私はSQL配列が苦手です(コードでカーソルを使用していますが、問題が複雑になりすぎると思います)が、疑似コードは次のとおりです。

v_where varchar2(100) :='';
FOR i in ('CAT','HAT','MAT') LOOP
  v_where := v_where || ' OR OBJECT_NAME LIKE ''%' || i.string ||'%''
END;
  v_where := ltrim(v_where, ' OR');

そして、上記の SQL を次のように変更します。

execute immediate '
select * from user_objects 
where 
rownum = :1
OR rownum = :2  
OR rownum = :1 AND ('||V_WHERE||')'
using var1,var2;
4

2 に答える 2

7

検討できるオプションがいくつかありますが、SQLステートメントの実行方法またはSQLステートメント自体のいずれかを変更する必要がある場合があります。

  1. DBMS_SQLEXECUTE IMMEDIATE-の代わりに使用するDBMS_SQLhttp://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htmを参照)は、よりも使用が困難EXECUTE IMMEDIATEですが、プロセスをより細かく制御できます。 (throughDBMS_SQL.BIND_VARIABLEおよびDBMS_SQL.BIND_ARRAY)は、位置ではなく名前でバインドします。
  2. EXECUTE IMMEDIATEとともに使用WITHWITH-最初にサブクエリでバインド変数を収集し、必要なときにいつでもサブクエリに結合する(バインド変数を直接参照するのではなく)句を使用するようにクエリを再構築できる場合があります。こんな感じかもしれません
with your_parameters as 
    (select :1 as p1, :2 as p2 from dual) 
select * 
from your_table, your_parameters 
where your_table.some_column1 = your_parameters.p1 
  and your_table.some_column2 <= your_parameters.p1 
  and your_table.some_column3 = your_parameters.p2

これはクエリのパフォーマンスに影響を与える可能性がありますが、許容できる妥協案である可能性があります。

  1. 動的SQLを使用しない-もちろん、動的SQLが必要ない場合は、EXECUTE IMMEDIATEを使用する必要がないため、「位置のみでバインド」の制限は適用されません。本当に動的SQLを使用する必要がありますか?

編集:編集で投稿したような条件の数が可変であるために動的SQLを使用している場合ORは、次のいずれかを実行することで動的SQLの使用を回避できる可能性があります。

  1. OR基準がテーブル(またはクエリ)からのものである場合-基準のリストを使用する代わりに、そのテーブル(またはクエリ)に結合しORます。たとえば、CAT、HAT、およびMATが、という名前YOUR_CRITERIAのテーブルで名前YOUR_CRITERIA_TABLEが付けられた列にリストされている場合、句に追加YOUR_CRITERIA_TABLEして、句内を次のようなものにFROM置き換えることができます。OBJECT_NAME LIKE '%CAT% OR OBJECT_NAME LIKE '%MAT% OR OBJECT_NAME LIKE '%HAT% OR OBJECT_NAME LIKE '%MAT%WHEREOBJECT_NAME LIKE '%' || YOUR_CRITERIA_TABLE.YOUR_CRITERIA || '%'.
  2. それ以外の場合は、基準をグローバル一時テーブルに配置する可能性があります-基準がテーブル(またはクエリ)からのものでない場合は、(実行時ではなく設計時に)保持するグローバル一時テーブルを作成できます次に、実行時に、条件をグローバル一時テーブルに挿入し、項目1で説明されているようにそれに結合します。
  3. または、条件をネストされたテーブルに配置することもできます。CREATE TYPE...IS TABLE OFこれは、グローバル一時テーブルの代わりにネストされたテーブル(を使用して作成されたテーブル)を使用することを除いて、項目2に似ています。ネストされたテーブルタイプを作成または所有することも、のような組み込みのテーブルタイプを使用することもできますSYS.ODCIVARCHAR2LIST。PL / SQLでは、このタイプの変数を移入してから、項目1のように「実際の」表のように使用します。

項目3の例は次のようになります。

DECLARE
    tblCriteria SYS.ODCIVARCHAR2LIST;

BEGIN
    tblCriteria := SYS.ODCIVARCHAR2LIST();

    -- In "real" code you might populate the nested table in a loop.
    -- This example populates it explicitly so that it will compile.  For the
    -- purpose of the example, we could have populated the nested table in 
    -- a single statement:

    -- tblCriteria := SYS.ODCIVARCHAR2LIST('CAT', 'HAT', 'MAT');

    tblCriteria.EXTEND(1);
    tblCriteria(tblCriteria.LAST) := 'CAT';

    tblCriteria.EXTEND(1);
    tblCriteria(tblCriteria.LAST) := 'HAT';

    tblCriteria.EXTEND(1);
    tblCriteria(tblCriteria.LAST) := 'MAT';

    FOR rec IN
    (
        SELECT
            USER_OBJECTS.*
        FROM
            USER_OBJECTS,
            TABLE(tblCriteria) YOUR_NESTED_TABLE
        WHERE
            USER_OBJECTS.OBJECT_NAME LIKE '%' || YOUR_NESTED_TABLE.COLUMN_VALUE || '%'
    )
    LOOP
        -- Do something.  For example, print out the object name.
        DBMS_OUTPUT.PUT_LINE(rec.OBJECT_NAME);
    END LOOP;
END;
于 2012-07-26T10:45:07.663 に答える
4

いいえ、残念ながら、EXECUTE IMMEDIATE のバインド変数は、ステートメントに現れるのと同じ順序で指定する必要があり、バインド変数名は無視されます。したがって、ステートメントには :1、:2、および :3 を含める必要があります。

于 2012-07-26T03:00:37.210 に答える