1

私はOracleでOLAPのようなパッケージを作成しています。ここでは、多数の左結合を行うことによって返される出力テーブルをアセンブルするメインの制御関数を呼び出します。これらの結合されたテーブルは、パッケージ内の「slave」関数で定義され、関数の引数によってパラメーター化された静的カーソルを使用して特定のサブセットを返します。重要なのは、これらのカーソルはすべて非常に似ているということです。

動的クエリを生成してで使用する以外に、ref cursorこれらを一般化できる方法はありますか。関数を追加するたびに、開発者として、これは特にエレガントではないという奇妙な感覚を覚えます。


Pseduocode

somePackage
  function go(param)
    return select    myRows.id,
                     stats1.value,
                     stats2.value
           from      myRows
           left join table(somePackage.stats1(param)) stats1
           on        stats1.id = myRows.id
           left join table(somePackage.stats2(param)) stats2
           on        stats2.id = myRows.id

  function stats1(param)
    return [RESULTS OF SOME QUERY]

  function stats2(param)
    return [RESULTS OF A RELATED QUERY]

クエリはstatsすべて同じ構造です。

  • まず、便利な方法でデータを集約します
  • 次に、このデータを基準に基づいて論理セクションに分割し、再度集計して(たとえば、部門別、地域別など)、結果を結合します。
  • 次に、結果を返し、関連するタイプにキャストするobjectので、簡単に実行できます。bulk collect

何かのようなもの:

cursor myCursor is
  with fullData as (
    [AGGREGATE DATA]
  ),
  fullStats as (
    [AGGREGATE FULLDATA BY TOWN]
    union all
    [AGGREGATE FULLDATA BY REGION]
    union all
    [AGGREGATE FULLDATA BY COUNTRY]
  )
  select myObjectType(fullStats.*)
  from   fullStats;

...

open myCursor;
fetch myCursor bulk collect into output limit 1000;
close myCursor;

return output;
4

1 に答える 1

1

フィルター操作は、静的 SQL を使用して動的クエリを作成するのに役立ちます。特に列リストが静的な場合。

すでにこのアプローチを検討していたかもしれませんが、パフォーマンス上の理由で破棄しました。「そのうちの 1 つの結果だけが必要な場合、なぜすべての SQL ブロックを実行するのでしょうか?」幸運なことに、オプティマイザーはすでにFILTER操作でこれを行っています。

クエリの例

最初に、実行されるたびに 5 秒待機する関数を作成します。実行されたクエリ ブロックを見つけるのに役立ちます。

create or replace function slow_function return number is begin
    dbms_lock.sleep(5);
    return 1;
end;
/

この静的クエリは、バインド変数によって制御されます。3 つのクエリ ブロックがありますが、クエリ全体が 15 秒ではなく 5 秒で実行されます。

declare
  v_sum number;
  v_query1 number := 1;
  v_query2 number := 0;
  v_query3 number := 0;
begin
  select sum(total)
  into v_sum
  from
  (
    select total from (select slow_function() total from dual) where v_query1 = 1
    union all
    select total from (select slow_function() total from dual) where v_query2 = 1
    union all
    select total from (select slow_function() total from dual) where v_query3 = 1
  );
end;
/

実行計画

このパフォーマンスは幸運の結果ではありません。Oracle が述語をランダムに実行するだけではありません。Oracle は実行前にバインド変数を分析し、無関係なクエリ ブロックを実行しません。それがFILTER以下の操作です。(これは不適切な名前ですが、多くの人は一般にすべての述語を「フィルター」と呼んでいます。しかし、それらの一部のみがFILTER操作になります。)

select * from table(dbms_xplan.display_cursor(sql_id => '0cfqc6a70kzmt'));

SQL_ID  0cfqc6a70kzmt, child number 0
-------------------------------------
SELECT SUM(TOTAL) FROM ( SELECT TOTAL FROM (SELECT SLOW_FUNCTION() 
TOTAL FROM DUAL) WHERE :B1 = 1 UNION ALL SELECT TOTAL FROM (SELECT 
SLOW_FUNCTION() TOTAL FROM DUAL) WHERE :B2 = 1 UNION ALL SELECT TOTAL 
FROM (SELECT SLOW_FUNCTION() TOTAL FROM DUAL) WHERE :B3 = 1 )

Plan hash value: 926033116

-------------------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT |      |       |       |     6 (100)|          |
|   1 |  SORT AGGREGATE  |      |     1 |    13 |            |          |
|   2 |   VIEW           |      |     3 |    39 |     6   (0)| 00:00:01 |
|   3 |    UNION-ALL     |      |       |       |            |          |
|*  4 |     FILTER       |      |       |       |            |          |
|   5 |      FAST DUAL   |      |     1 |       |     2   (0)| 00:00:01 |
|*  6 |     FILTER       |      |       |       |            |          |
|   7 |      FAST DUAL   |      |     1 |       |     2   (0)| 00:00:01 |
|*  8 |     FILTER       |      |       |       |            |          |
|   9 |      FAST DUAL   |      |     1 |       |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - filter(:B1=1)
   6 - filter(:B2=1)
   8 - filter(:B3=1)

問題

FILTER操作は十分に文書化されていません。いつ機能するか機能しないか、クエリの他の部分に与える影響を正確に説明することはできません。たとえば、説明計画ではRows見積もりは 3 ですが、実行時に Oracle はカーディナリティを 1 と簡単に見積もることができるはずです。明らかに、実行計画はそれほど動的ではなく、カーディナリティの見積もりが不十分であると、後で問題が発生する可能性があります。また、静的な式が適切にフィルタリングされていない奇妙なケースもいくつか見てきました。ただし、クエリが単純な等値述語を使用する場合は問題ありません。

このアプローチにより、すべての動的 SQL を削除して、大きな静的 SQL ステートメントに置き換えることができます。これにはいくつかの利点があります。動的 SQL はしばしば「醜く」、デバッグが困難です。しかし、手続き型プログラミングにしか慣れていない人は、単一の SQL ステートメントを 1 つの巨大な神の関数と考える傾向があり、これは悪い習慣です。彼らは、UNION ALLs が SQL の独立したブロックを作成することを認めません。

動的 SQL の方がおそらく優れている

一般的に、私はこのアプローチに反対することをお勧めします。見た目がいいから、持っているものがいい。動的 SQL の主な問題は、人々がそれを実際のコードのように扱わないことです。コメントもフォーマットもされておらず、誰も理解できない恐ろしい混乱のように見えます。クリーンなコードを生成するために余分な時間を費やすことができる場合は、それに固執する必要があります。

于 2014-05-11T17:09:04.363 に答える