動的 SQL を使用した方がパフォーマンスが優れているという Tony の意見には同意しますが、コンテキスト変数はバインド変数を使用するよりも優れたアプローチです。
UsingIN_VARIABLE IS NULL OR table.fieldx = IN_VARIABLE
は、オプションの値を処理するのに理想的ではありません。クエリが送信されるたびに、Oracle は最初に共有プールをチェックして、ステートメントが以前に送信されたかどうかを確認します。含まれている場合は、クエリの実行プランが取得され、SQL が実行されます。ステートメントが共有プールで見つからない場合、オラクルはステートメントを解析し、さまざまな実行パスを調べて、実行前に最適なアクセス プラン (別名「ベスト パス」) を考え出すプロセスを実行する必要があります。このプロセスは「ハード解析」と呼ばれ、クエリ自体よりも時間がかかる場合があります。Oracle のハード/ソフト解析の詳細については、こちらを、AskTom についてはこちらをお読みください。
要するに - これ:
and (:bcode is null or q.bcode = :bcode)
...同じ、動的またはその他の方法で実行されます。オプションのパラメーターに動的 SQL でバインド変数を使用するメリットはありません。セットアップはまだ SARGability を破壊します...
コンテキスト パラメータは、Oracle 9iで導入された機能です。これらはパッケージに関連付けられており、属性値の設定に使用できます (パッケージに対する EXECUTE 権限を持つユーザーのみ。スキーマに CREATE CONTEXT を付与する必要があります)。コンテキスト変数を使用して動的 SQL を調整し、フィルター/検索基準に基づいてクエリに必要なものだけを含めることができます。IN_VARIABLE IS NULL OR table.fieldx = IN_VARIABLE
対照的に、Bind 変数 (動的 SQL でもサポートされています)では、検索クエリでテストを実行できる値を指定する必要があります。実際には、値のコンタミネーションのリスクを排除するために、プロシージャまたは関数ごとに個別のコンテキスト変数を使用する必要があります。
コンテキスト変数を使用したクエリは次のとおりです。
L_CURSOR SYS_REFCURSOR;
L_QUERY VARCHAR2(5000) DEFAULT 'SELECT num
FROM (SELECT DISTINCT q.num
FROM CQQV q
WHERE 1 = 1 ';
BEGIN
IF IN_BCODE IS NOT NULL THEN
DBMS_SESSION.SET_CONTEXT('THE_CTX',
'BCODE',
IN_BCODE);
L_QUERY := L_QUERY || ' AND q.bcode = SYS_CONTEXT(''THE_CTX'', ''BCODE'') ';
END IF;
IF IN_LB IS NOT NULL THEN
DBMS_SESSION.SET_CONTEXT('THE_CTX',
'LB',
IN_LB);
L_QUERY := L_QUERY || ' AND q.lb = SYS_CONTEXT(''THE_CTX'', ''LB'') ';
END IF;
IF IN_TYPE IS NOT NULL THEN
DBMS_SESSION.SET_CONTEXT('THE_CTX',
'TYPE',
IN_TYPE);
L_QUERY := L_QUERY || ' AND q.type = SYS_CONTEXT(''THE_CTX'', ''TYPE'') ';
END IF;
IF IN_EDATE IS NOT NULL THEN
DBMS_SESSION.SET_CONTEXT('THE_CTX',
'EDATE',
IN_EDATE);
L_QUERY := L_QUERY || ' AND q.edate = SYS_CONTEXT(''THE_CTX'', ''EDATE'') - 30 ';
END IF;
L_QUERY := L_QUERY || ' ORDER BY dbms_random.value()) subq
WHERE rownum <= :numrows ';
FOR I IN 0 .. (TRUNC(LENGTH(L_QUERY) / 255)) LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(L_QUERY, I * 255 + 1, 255));
END LOOP;
OPEN L_CURSOR FOR L_QUERY USING IN_ROWNUM;
RETURN L_CURSOR;
END;
この例では、値がオプションではないため、依然として rownum にバインド変数を使用しています。
DBMS_SESSION.SET_CONTEXT('THE_CTX', 'LB', IN_LB);
SET_CONTEXT パラメータは次のとおりです。
- コンテキスト変数名。インスタンスの作成は必要ありません
- コンテキスト変数内の変数。コンテキスト変数はセッション変数のようなもので、Web アプリケーションとセッション オブジェクトに精通していることを前提としています。
- パラメータ #2 で定義された変数の値。
バインドとコンテキスト
変数をバインドするということは、Oracle が変数参照が入力されることを期待していることを意味します。それ以外の場合は ORA エラーです。例えば:
... L_QUERY USING IN_EXAMPLE_VALUE
...移入される単一のバインド変数参照があることを期待しています。がIN_EXAMPLE_VALUE
null の場合、クエリに存在する必要があります。いいえ::variable
AND :variable IS NULL
コンテキスト変数を使用すると、無関係/冗長なロジックを含める必要がなくなり、値が null かどうかをチェックできます。
重要: バインド変数は、名前順ではなく、発生順に処理されます (序数として知られています)。USING
句にデータ型宣言がないことに気付くでしょう。序数は理想的ではありません。USING
句を更新せずにクエリで序数を変更すると、修正されるまでクエリが壊れます。