SQLで最初に実行される関数に依存できますか


9

次のスクリプトを検討してください。

create or replace function f(p_limit in integer) return integer as
begin
  set_global_context ('limit', p_limit);
  return p_limit;
end;
/

create view v as 
select level as val from dual connect by level<=sys_context('global_context','limit');

select f(2), v.* from v;

/*
F(2)                   VAL                    
---------------------- ---------------------- 
2                      1                      
2                      2                      
*/

select f(4), v.* from v;

/*
F(4)                   VAL                    
---------------------- ---------------------- 
4                      1                      
4                      2                      
4                      3                      
4                      4                      
*/

f(x)10.2で実行されたこのテストケースのように、ビュー内でコンテキストが読み取られる前に実行されることを信頼できますか?


助けることはできませんが(レベルは常に同じになる場合である、)ログイン・トリガがより適切かもしれないと思う
Philᵀᴹ

@Philこれは単なる例です-私はsys_contextを使用してビューをパラメーター化していますが、パラメーターは毎回異なります。このようにいじりをせずにSQLからグローバルコンテキストを設定する方法を知っている場合は、それも聞きたいです。
ジャックは、topanswers.xyzを試してみると12

1
@JackDouglas:ビューをパラメータ化することは、私には「感じられない」アイデアです。MSSQLの下では、(値ではなく)結果セットを返すユーザー定義関数を使用して、実行しようとしていることを実行できますSELECT stuff FROM dbo.FuncReturningTable(param)。Oracleにはおそらく同等の機能があります。ただし、これを大規模なデータセットで使用する場合は、パフォーマンスを監視するように注意します。そのような構文から効率的な計画を作成するために、クエリプランナーがどの程度必要かはわかりません。
デビッドスピレット

@Davidパラメータを使用してビューをパラメータ化するには、通常、sys_contextを使用します。通常は、クエリを実行する前にコンテキストを設定します(たとえば、PL / SQLのビットを使用)。Oracleにはセットを返す関数やパイプライン関数、あるいはその両方がありますが、これを実現するための「通常の」方法ではありません。明確に言うと、タイトルの質問に対する答えは「いいえ」だと思います。誰かがもっとよく知っているのかと思っただけです。
ジャックはtopanswers.xyzを試してみると12

回答:


8

番号。

(connect byではなく)where句に対するコンテキストフィルタリングを使用してビューを書き換えると、以前に設定されたコンテキストの値が取得されます。

create table t as 
 select rownum r from dual connect by level <= 10;

create or replace view v as 
  select r val from t where r <=sys_context('global_context','limit');

select f(2), v.* from v;

F(2) VAL
---- ---
   2   1 
   2   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 
   4   3 
   4   4 

列が選択される前にwhere句が評価されるため、関数に渡される値は、コンテキストが読み取られるまで設定されません。クエリ内のsys_context呼び出しの場所(選択、場所、グループ化など)は、この値が設定されるときに正確に影響します。


私の本では、+ 1はほとんど「ケースクローズ」です、ありがとう。
ジャックはtopanswers.xyzを試してみると12

2

一般的に言って、単一のSQLステートメントを評価するときにDBMSが実行する順序については、安全に想定することはできません。これが、多くのDBMSがそのように使用された関数に副作用を与えることを許可しない理由です(つまり、MSSQLでは、関数がグローバル/接続状態を設定したり、そこで行ったり、テーブルの内容を変更したりできません)。一連のステートメントは、1つのステップから次のステップまで意味のある方法で実行する必要があります(つまり、連続して実行されるか、実行されなかったことが分からない方法で)、単一のステートメント内でクエリプランナーそれがまだ存在していない場所に曖昧さを導入しない限り、自由な統治を持っています(あなたの例では、関数がビューに影響を与える副作用があるため、曖昧さがすでに存在しています)。

クエリプランナーが明るく、ビューが関数の副作用の影響を受けていることを検出できる場合、異なる入力値でその関数を呼び出す可能性のある別のビューに参加した場合はどうなりますか?それは非常にすぐに非常に毛むくじゃらになるかもしれません-この種のことは、一般に、どんなプログラミングコンテキストでも、関数がそれら自身の出力を超えて影響を与えてはならない理由です。

この特定の例では、f(x)はステートメントの「表示」部分であるため、最初にf(x)が呼び出される可能性は低いと言えます。ビュー内の結果セットは、返す列のリストが評価されます。もちろん、これは使用するDBMSによって異なります。私はOracleの専門家ではありません。テスト結果から、これらのインスタンスでは最初に関数が呼び出されているように見えます。ただし、単一のSQLステートメント内の実行順序にまったく依存することには注意が必要です。たとえそれが常に期待どおりに機能しているとしても、将来のリビジョンでは機能しない可能性があります(その実行が常にどこかで公式に文書化されている場合を除く)この方法)。


2
良い答えですが、JackはOracleの決定的な技術的な答えを探しているように感じます。
フィロ2012

1

ドキュメントは、「オプティマイザは最初に、定数を含む式と条件を可能な限り完全に評価する」と約束しているだけです。(10.211.2)。特定の式が最初に評価されること、またはその順序が時々変更されないことが保証されていません(同じリリース内の新しいパッチレベル?)。


1優れた、おかげで(私はそれらのドキュメントを読みにかかわらずはかなり正方形のではありませんクリスの答え
ジャック氏は述べていtopanswers.xyz試し

1
違いは、関数がwhere、select、またはその他の節で呼び出されるかどうかです。selectセクションの関数はオプティマイザの決定に影響を与えないため(サブクエリでない限り)、結果をフェッチするまでこれらを評価する必要はありません。where句の関数は、使用される結合メソッドに影響を与えるため、できるだけ早く評価する必要があります。
Chris Saxon、

@Chrisはその経験を話しましたか、それともドキュメントのどこかから取得しましたか?
ジャックはtopanswers.xyzを試してみると12

ドキュメントリファレンスが見つかりません。私の経験に基づいて、単一のテーブルをフィルターするためにwhere句で呼び出された場合、すべての行(FTSを想定)に対してアクセスされますが、選択リストにある場合にのみ返されます。実行プランは解析中に設定されるため、selectの関数は影響を受けないことを意味します。これをチェックするテストケースは、カウンターを設定する関数(パッケージまたはテーブル内)を作成し、クエリ内の配置場所に基づいて出力を比較することで実行できます。
Chris Saxon、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.