15 電力需要カートリッジの例

この例の電力需要カートリッジには、ユーザー定義のオブジェクト型、拡張可能索引付けおよび最適化が含まれています。カートリッジ全体の定義は、Oracleデモ・ディレクトリにあるextdemo1.sqlファイルからオンラインで参照できます。

関連項目:

15.1 機能の要件

電力会社Power-To-The-Peopleが、自社のリソースの展開方法を決定するための高度なモデルを開発します。この電力会社が電力を供給している地域は、地理上の区域にまたがるグリッドで表されます。このグリッドを図15-1に示します。

図15-1 電力会社の電力供給地域

図15-1の説明が続きます
「図15-1 電力会社の電力供給地域」の説明

この地域の周辺地域には、他の電力会社からの電力供給を必要とするところがあります。図のように、各地域は10x10のグリッド上でセルと呼ばれる地理上の象限で構成されています。セルを識別するには、空間座標(緯度/経度)、マトリックス番号(1,1、1,2など)および図15-2のような連番など、いくつかの方法があります。

図15-2 連番付きの地域グリッド・セル

図15-2の説明が続きます
「図15-2 連番付きの地域グリッド・セル」の説明

各セルで表される区域では、その区域内の消費者による消費電力が1時間ごとに記録されています。たとえば、特定の時間の電力需要測定値が表15-1のようになるとします(ここでは、セルがマトリックスで表されています)。

表15-1 1時間当たりの電力需要測定値のサンプル

- 1 2 3 4 5 6 7 8 9 10

1

23

21

25

23

24

25

27

32

31

30

2

33

32

31

33

34

32

23

22

21

34

3

45

44

43

33

44

43

42

41

45

46

4

44

45

45

43

42

26

19

44

33

43

5

45

44

43

42

41

44

45

46

47

44

6

43

45

98

55

54

43

44

33

34

44

7

33

45

44

43

33

44

34

55

46

34

8

87

34

33

32

31

34

35

38

33

39

9

30

40

43

42

33

43

34

32

34

46

10

43

42

34

12

43

45

48

45

43

32

発電所は、他の2つのソースからもレポートを受け取ります。

  • 地表のセンサーが、すべてのセルについて気温の測定値を提供します

    セルからの電力需要履歴とその地域の気温の測定値との相関関係を分析することで、電力会社は特定の気温における予想需要量の近似値を判断できます。

  • 衛星カメラが、現在の状態に関する画像を提供し、これが図15-3に示すグリッドに対応するグレースケール画像に変換されます。

図15-3 衛星画像のグレースケール表現

図15-3の説明が続きます
「図15-3 衛星画像のグレースケール表現」の説明

これらの画像は、明度が高いほど気温が低いことを示すように設計されています。したがって、この画像は、この地域に南西から寒冷前線が移動しつつあることを示しています。グレースケール・イメージで得られるデータを同時に測定した気温値と相関付けることで、電力会社は成層圏から見た気象条件に基づいて電力需要を判断できるようになりました。

このことが重要なのは、気象の変化と電力の供給につれて送られてくるレポートが示す変化の速度と大きさを認識する作業が、このモデル作成に大きく関係しているためです。図15-4に、第2の記録時における同じ寒冷前線を示します。

図15-4 第2の記録時における気象条件のグレースケール表現

図15-4の説明が続きます
「図15-4 第2の記録時における気象条件のグレースケール表現」の説明

図15-5のように、寒冷前線の範囲と速度を分析することで、電力会社は条件が短期的であるか中期的であるかを予想できます。

図15-5 予想される条件のグレースケール表現

図15-5の説明が続きます
「図15-5 予想される条件のグレースケール表現」の説明

電力会社は、これらの条件と他の変則的な状況(変電所の障害など)に関するデータを組み合せて、リソースの最適のデプロイメントを編成できる必要があります。図15-6は、地域全体における変電所の分布を反映したものです。

図15-6 地域全体の発電所の分布

図15-6の説明が続きます
「図15-6 地域全体の発電所の分布」の説明

発電所の分布は、電力会社が最も需要の大きい地域に電力供給先を変更できることを意味しています。図15-7に、3つの発電所間の重複部分を示します。

図15-7 3つの発電所による供給区域

図15-7の説明が続きます
「図15-7 3つの発電所による供給区域」の説明

電力会社は、変動要件に応じてリソースのデプロイ方法を決定し、供給不足が生じた場合に他の電力会社から電力を購入するかどうかも決定する必要があります。

15.2 アプリケーション・モデルの作成

アプリケーションのモデル化に関連する技術上およびビジネス上の使用例について考えます。図15-8のクラス・ダイアグラムは、Unified Modelling Language(UML)表記法を使用してアプリケーション・オブジェクトを記述したものです。

図15-8 電力需要カートリッジのアプリケーション・オブジェクト・モデル

図15-8の説明が続きます
「図15-8 電力需要カートリッジのアプリケーション・オブジェクト・モデル」の説明

15.2.1 サンプル問合せ

この方法でアプリケーション・モデルを作成すると、次の特定の問合せを実行できるようになります。

  • 指定した期間中の需要が最も大きいセル(地理的象限)の検索

  • 総需要が最も大きい期間の検索

  • 需要が指定値よりも大きい全セルの検索

  • いずれかの時点で需要が指定値と一致するセルの検索

  • 3つ以上のセルの需要が指定値を超えている期間の検索

  • 最小需要を持つセルと最大需要を持つセルとの格差(差異)が最大だった期間の検索

  • 10個以上のセルの需要が指定値以上になっていた時間の検索

  • セルの平均需要が指定された値を上回っていた時間の検索

    平均はTotalPowerDemand/100を通じて簡単に計算できることを前提にしています。

  • セルの需要の中央値が指定された値を上回っていた期間の検索

    中央値は簡単に計算できないことを前提としています。

  • 直前時間の総需要からの総需要上昇率が10%以上だった全期間の検索

前述の問合せは、システムから収集可能な情報のリストの一部にすぎません。たとえば、この種のアプリケーションの開発者は、前の問合せから導出された情報に基づく問合せを作成する必要があることが明らかです。

  • 前の期間と比較した特定セルの需要の変動率

  • 需要の急増または急減を示すセル(指定値からの増加率/減少率として測定)

図15-9は、電力需要カートリッジの実装について説明および図示したものです。

図15-9 電力需要カートリッジの実装モデル

図15-9の説明が続きます
「図15-9 電力需要カートリッジの実装モデル」の説明

電力会社は気象センターから現況レポートを受け取り、発電所から特定地域(10x10グリッド上のセルで表現)の消費電力レポートを受け取ります。次に、この情報を履歴データと比較し、特定の期間における各地域の電力需要を予想します。

電力会社の各サービス・エリアはセルで構成される10x10のグリッドとみなされ、各セルの境界は空間座標(緯度/経度)に関連付けられています。セルで表される地理的区域は、形状や面積が同一の場合も異なる場合もあります。各セルで表される区域では、その区域内の消費者による消費電力が1時間ごとに記録されています。例として、表15-2に特定時間の電力需要測定値を示します。

表15-2 1時間当たりの電力需要測定値のサンプル

- 1 2 3 4 5 6 7 8 9 10

1

23

21

25

23

24

25

27

32

31

30

2

33

32

31

33

34

32

23

22

21

34

3

45

44

43

33

44

43

42

41

45

46

4

44

45

45

43

42

26

19

44

33

43

5

45

44

43

42

41

44

45

46

47

44

6

43

45

98

55

54

43

44

33

34

44

7

33

45

44

43

33

44

34

55

46

34

8

87

34

33

32

31

34

35

38

33

39

9

30

40

43

42

33

43

34

32

34

46

10

43

42

34

12

43

45

48

45

43

32

各セルの数値には、その区域における特定時間の(電力会社が決定した測定単位による)電力需要が反映されています。たとえば、第1セル(1,1)の需要は23、第2セル(1,2)の需要は21などとなっています。最終セル(10,10)の需要は32でした。

電力会社は、このデータを多数のモニタリングおよび分析アプリケーションに使用します。各セルの測定値が監視され、異常な需要増や需要減がないかどうかが調べられます。たとえば、(6,3)の測定値98と(8,1)の測定値87は異常に高い値であり、(4,7)の測定値19と(10,4)の測定値12は異常に低い値の場合があります。発電所別と地域全体について、期間における隣接地の需要の急増や急減などの傾向も分析されます。

15.3 問合せと拡張可能索引付け

ドメイン索引によりメリットが得られる問合せの種類について考えます。拡張可能索引付けを使用するかどうかは、標準のOracle索引を使用する場合と、まったく索引を使用しない場合の、どちらがより効率的に問合せを実行できるかに応じて異なります。

15.3.1 拡張可能索引付けのメリットが得られない問合せ

次の両方の条件に該当する場合、問合せにドメイン索引は不要です。

  • 必要な情報を表の属性(列)に使用し、その列に標準索引を定義できる場合。

  • データに対する問合せの操作が、Bツリー索引に対するequalslessthangreaterthanmaxおよびminなど、標準索引でサポートされている操作に限定されている場合。

PowerDemand_Typオブジェクト型のカートリッジの例では、3つの列(TotGridDemandMaxCellDemandおよびMinCellDemand)の値がファンクションによって設定され、その後はこれらの値が変化しません。(たとえば、1998年1月1日の13:00のグリッドにおける合計電力需要は、計算後に変化しません。)これらの列を使用する問合せの場合、equalslessthangreaterthanmaxminなどの操作を行う際に、各列に標準のBツリー索引を適用するだけで十分であるため、その方法をお薦めします。

拡張可能索引付けによるメリットが得られない問合せの例(電力需要カートリッジを使用)を次に示します。

  • 特定の時点で最大の電力需要を持つセルの検索

  • グリッド全体の電力需要が最大だった時刻の検索

  • 電力需要が指定値よりも大きい全セルの検索

  • セルの平均需要または中央値が指定値よりも大きかった時間の検索

    この問合せが効率的に実行されるようにするには、PowerDemand_Typオブジェクト型内で2つの追加の列(AverageCellDemandおよびMedianCellDemand)を定義し、これらの列の値を設定するファンクションを作成します。(たとえば、AverageCellDemandTotGridDemandを100で割った値です。)次に、AverageCellDemand列およびMedianCellDemand列にBツリー索引を作成します。

15.3.2 拡張可能索引のメリットが得られる問合せ

問合せでドメイン索引のメリットが得られるのは、問い合せるデータを表の単純属性に使用できない場合、またはデータに対して実行する操作がOracle索引でサポートされている標準操作でない場合です。

拡張可能索引付けによるメリットが得られる問合せの例(電力需要カートリッジを使用)を次に示します。

  • 指定時間に電力需要が指定値と一致していた第1セルの検索

    第1セルを問い合せることで、問合せは単純なTRUE-FALSEチェック(指定時間に需要が指定値と一致していたanyセルの有無の検索など)の範囲を超えるため、ドメイン索引によるメリットが得られます。

  • 最小需要を持つセルと最大需要を持つセルとの格差または差異が最大だった時間の検索

  • 3個以上のセルの需要が指定値を超えていた全時間の検索

  • 10個以上のセルの需要が指定値以上になっていた全時間の検索

  • 直前時間のグリッドの総需要からの上昇率が10%以上だった全時間の検索

15.4 ドメイン索引の作成

電力需要カートリッジのうち、拡張可能索引付けに関連する部分について考えます。コード・セグメントを示しながら説明していきます。

カートリッジ全体の定義は、標準Oracleデモ・ディレクトリ(場所はプラットフォームに依存)にあるextdemo1.sqlとしてオンラインで使用可能です。

15.4.1 索引を所有するスキーマの作成

ドメイン索引を作成する前に、索引を所有するデータベース・ユーザーまたはスキーマを作成します。電力需要の例では、ユーザーPowerCartUserが作成され、適切な権限が付与されます。カートリッジに関連するデータベース構造はすべて、例15-1で示されるように、このユーザーとして(つまり、カートリッジ開発者またはDBAがデータベースにPowerCartUserとして接続している間に)作成されます。

例15-1 電力需要カートリッジのデータベース・ユーザーの作成

set echo on
connect sys/knl_test7 as sysdba;
drop user PowerCartUser cascade;
create user PowerCartUser identified by PowerCartUser;

-------------------------------------------------------------------
-- INITIAL SET-UP
-------------------------------------------------------------------
-- grant privileges --
grant connect, resource to PowerCartUser;
grant create operator to PowerCartUser;
grant create indextype to PowerCartUser;
grant create table to PowerCartUser;

15.4.2 オブジェクト型の作成

オブジェクト型PowerDemand_Typは、1時間当たりの電力グリッド測定値を格納します。この型を使用して、測定値を格納する表の列を定義します。

最初に、例15-2で示されるように、後で使用できるように3つの型を定義します。

  • PowerGrid_TypPowerDemand_Typ内のセルを定義します。

  • NumTab_Typ。索引エントリが格納される表で使用します。

  • PowerDemand_Typ型。次の要素で構成されます。

    • 3つのメンバー・プロシージャにより設定される3つの属性(TotGridDemandMaxCellDemandMinCellDemand)

    • 電力需要測定値(グリッドの100個のセル)

    • 電力需要の測定日時。(1時間ごとに100の領域から電力需要の測定値が転送されます。)

例15-2 電力需要カートリッジの型の作成

CREATE OR REPLACE TYPE PowerGrid_Typ as VARRAY(100) of NUMBER;

CREATE OR REPLACE TYPE NumTab_Typ as TABLE of NUMBER;
CREATE OR REPLACE TYPE PowerDemand_Typ AS OBJECT (
  -- Total power demand for the grid
  TotGridDemand NUMBER,
  -- Cell with maximum/minimum power demand for the grid
  MaxCellDemand NUMBER,
  MinCellDemand NUMBER,
  -- Power grid: 10X10 array represented as Varray(100)
  -- using previously defined PowerGrid_Typ
  CellDemandValues PowerGrid_Typ,
  -- Date/time for power-demand samplings: Every hour,
  -- 100 areas transmit their power demand readings.
  SampleTime DATE,
  --
  -- Methods (Set...) for this type:
  -- Total demand for the entire power grid for a
  -- SampleTime: sets the value of TotGridDemand.
  Member Procedure SetTotalDemand,
  -- Maximum demand for the entire power grid for a
  -- SampleTime: sets the value of MaxCellDemand.
  Member Procedure SetMaxDemand,
  -- Minimum demand for the entire power grid for a
  -- SampleTime: sets the value of MinCellDemand.
  Member Procedure SetMinDemand
);
/

15.4.3 オブジェクト型のメソッドの定義

PowerDemand_Typオブジェクト型には、次のように型定義の最初の3つの属性を設定するメソッドがあります。

  • TotGridDemand。指定した時間(SampleTimeで識別)の電力グリッド全体の総需要。

  • MaxCellDemandSampleTimeにおける全セルで最大の電力需要値。

  • MinCellDemandSampleTimeにおける全セルで最小の電力需要値。

各プロシージャのロジックは複雑ではありません。SetTotDemandはセル値をループ処理し、累計を作成します。SetMaxDemandは、最初の2つのセルの値を比較して、大きい方の値を現在の最大値として保存します。その後、後続のセルを1つずつ調べて、現在の最大値と比較し、2つの値のうち大きい方を現在の最大値として保存するという操作を、セル値が終了するまで繰り返します。SetMinDemandは、SetMaxDemandと同じアプローチを使用しますが、比較時に小さい方の値を繰り返し保存することで、全体の最小値を導出します(例15-3参照)。

例15-3 電力需要カートリッジのオブジェクト型の実装

CREATE OR REPLACE TYPE BODY PowerDemand_Typ 
IS
  --
  -- Methods (Set...) for this type:
  -- Total demand for the entire power grid for a
  -- SampleTime: sets the value of TotGridDemand.
  Member Procedure SetTotalDemand 
  IS
  I BINARY_INTEGER;
  Total NUMBER;
  BEGIN
    Total :=0;
    I := CellDemandValues.FIRST;   
    WHILE I IS NOT NULL LOOP
   Total := Total + CellDemandValues(I);
        I := CellDemandValues.NEXT(I);
    END LOOP;
    TotGridDemand := Total;
  END;

  -- Maximum demand for the entire power grid for a
  -- SampleTime: sets the value of MaxCellDemand.
  Member Procedure SetMaxDemand 
  IS
  I BINARY_INTEGER;
  Temp NUMBER;
  BEGIN
    I := CellDemandValues.FIRST;   
    Temp := CellDemandValues(I);
    WHILE I IS NOT NULL LOOP
   IF Temp < CellDemandValues(I) THEN
      Temp := CellDemandValues(I);
   END IF;
        I := CellDemandValues.NEXT(I);
    END LOOP;
    MaxCellDemand := Temp;
  END;

  -- Minimum demand for the entire power grid for a
  -- SampleTime: sets the value of MinCellDemand.
  Member Procedure SetMinDemand 
  IS
  I BINARY_INTEGER;
  Temp NUMBER;
  BEGIN
    I := CellDemandValues.FIRST;   
    Temp := CellDemandValues(I);
    WHILE I IS NOT NULL LOOP
   IF Temp > CellDemandValues(I) THEN
      Temp := CellDemandValues(I);
   END IF;
        I := CellDemandValues.NEXT(I);
    END LOOP;
    MinCellDemand := Temp;
  END;
END;
/

15.4.4 ファンクションおよび演算子について

電力需要カートリッジは、ユーザーが電力グリッドにequalitygreaterthanまたはlessthanの関係を問い合せることができるように設計されています。ただし、セルの需要データの格納方法の関係で、標準演算子(=><)は使用できません。かわりに、新しい演算子を作成し、ファンクションを作成して、新しい各演算子の実装(つまり、Oracleによる演算子の解析方法)を定義する必要があります。

このカートリッジの場合、3つの関係をそれぞれ2つの方法でチェックできます。

  • グリッドの特定のセルが関係を満たしているかどうか。(たとえば、セル(3,7)の需要が25のグリッドがあるかどうか)

    これらの演算子の名前はPower_EqualsSpecific()などのPower_XxxxxSpecific()形式で、実装用ファンクションの名前はPower_XxxxxSpecific_Func()形式です。

  • グリッドに関係を満たしているセルがあるかどうか。たとえば、需要が25のセルを含むグリッドがあるかどうか。

    これらの演算子の名前はPower_EqualsAny()などのPower_XxxxxAny()形式で、実装用ファンクションの名前はPower_XxxxxAny_Func()形式です。

演算子とファンクションのペアごとに、最初にファンクションを定義し、そのファンクションを使用して演算子を定義します。ファンクションは、索引が定義されていない場合に使用される実装です。この実装は、Oracleオプティマイザでコストを判別し、索引の使用が必要かどうかを決定し、実行計画を作成できるように指定する必要があります。

表15-3に、演算子と実装用ファンクションを示します。

表15-3 演算子と実装用ファンクション

演算子 実装用ファンクション
Power_EqualsSpecific()
Power_EqualsSpecific_Func()
Power_EqualsAny()
Power_EqualsAny_Func()
Power_LessThanSpecific()
Power_LessThanSpecific_Func()
Power_LessThanAny()
Power_LessThanAny_Func()
Power_GreaterThanSpecific()
Power_GreaterThanSpecific_Func()
Power_GreaterThanAny()
Power_GreaterThanAny_Func()

各ファンクションと演算子は、条件がTRUEの場合(指定したセルが指定した値と等しい場合など)は数値1を戻し、条件がTRUEでない場合は0(ゼロ)を戻し、指定したセル値が無効な場合はNULLを戻します。

例15-4の文では、実装用ファンクションPower_xxx_Func()を、最初はspecific実装用、次はany実装用に作成します。

例15-5で説明する文では、演算子(Power_xxx)を作成します。それぞれの文で実装用ファンクションを1つ指定しています。

15.4.4.1 ファンクションおよび演算子の作成

例15-4および例15-5は、前の手順で定義した電力需要カートリッジのオブジェクト型のファンクションと演算子を実装する方法を示しています。

例15-4 電力需要カートリッジのPower_XXX_Func()ファンクションの実装

CREATE FUNCTION Power_EqualsSpecific_Func(
  object PowerDemand_Typ, cell NUMBER, value NUMBER)
RETURN NUMBER AS
  BEGIN
  IF cell <= object.CellDemandValues.LAST
  THEN
     IF (object.CellDemandValues(cell) = value) THEN
   RETURN 1;
     ELSE
   RETURN 0;
     END IF;
  ELSE
     RETURN NULL;
  END IF;
  END;
/
CREATE FUNCTION Power_GreaterThanSpecific_Func(
  object PowerDemand_Typ, cell NUMBER, value NUMBER)
RETURN NUMBER AS
  BEGIN
  IF cell <= object.CellDemandValues.LAST
  THEN
     IF (object.CellDemandValues(cell) > value) THEN
   RETURN 1;
     ELSE
   RETURN 0;
     END IF;
  ELSE
     RETURN NULL;
  END IF;
  END;
/
CREATE FUNCTION Power_LessThanSpecific_Func(
  object PowerDemand_Typ, cell NUMBER, value NUMBER)
RETURN NUMBER AS
  BEGIN
  IF cell <= object.CellDemandValues.LAST
  THEN
     IF (object.CellDemandValues(cell) < value) THEN
   RETURN 1;
     ELSE
   RETURN 0;
     END IF;
  ELSE
     RETURN NULL;
  END IF;
  END;
/
CREATE FUNCTION Power_EqualsAny_Func(
  object PowerDemand_Typ, value NUMBER)
RETURN NUMBER AS
   idx NUMBER;
  BEGIN
    FOR idx IN object.CellDemandValues.FIRST..object.CellDemandValues.LAST LOOP
      IF (object.CellDemandValues(idx) = value) THEN
   RETURN 1;
      END IF;
    END LOOP;
   RETURN 0;
  END;
/
CREATE FUNCTION Power_GreaterThanAny_Func(
  object PowerDemand_Typ, value NUMBER)
RETURN NUMBER AS
   idx NUMBER;
  BEGIN
    FOR idx IN object.CellDemandValues.FIRST..object.CellDemandValues.LAST LOOP
      IF (object.CellDemandValues(idx) > value) THEN
   RETURN 1;
      END IF;
    END LOOP;
   RETURN 0;
  END;
/
CREATE FUNCTION Power_LessThanAny_Func(
  object PowerDemand_Typ, value NUMBER)
RETURN NUMBER AS
   idx NUMBER;
  BEGIN
    FOR idx IN object.CellDemandValues.FIRST..object.CellDemandValues.LAST LOOP
      IF (object.CellDemandValues(idx) < value) THEN
   RETURN 1;
      END IF;
    END LOOP;
   RETURN 0;
  END;
/

例15-5 電力需要カートリッジのPower_XXX()ファンクションの実装

CREATE OPERATOR Power_Equals BINDING(PowerDemand_Typ, NUMBER, NUMBER)
  RETURN NUMBER USING Power_EqualsSpecific_Func;
CREATE OPERATOR Power_GreaterThan BINDING(PowerDemand_Typ, NUMBER, NUMBER)
  RETURN NUMBER USING Power_GreaterThanSpecific_Func;
CREATE OPERATOR Power_LessThan BINDING(PowerDemand_Typ, NUMBER, NUMBER)
  RETURN NUMBER USING Power_LessThanSpecific_Func;
  
CREATE OPERATOR Power_EqualsAny BINDING(PowerDemand_Typ, NUMBER)
  RETURN NUMBER USING Power_EqualsAny_Func;
CREATE OPERATOR Power_GreaterThanAny BINDING(PowerDemand_Typ, NUMBER)
  RETURN NUMBER USING Power_GreaterThanAny_Func;
CREATE OPERATOR Power_LessThanAny BINDING(PowerDemand_Typ, NUMBER)
  RETURN NUMBER USING Power_LessThanAny_Func;

15.4.5 索引タイプ実装メソッドの作成

電力需要カートリッジでは、ドメイン索引用のメソッドを指定する索引タイプのオブジェクト型が作成されます。これらのメソッドはODCIIndex(Oracle Data Cartridge Interface Index)インタフェースの一部であり、索引の定義、操作、スキャンおよびエクスポートに使用するメソッドについて、索引の動作をまとめて定義します。

表15-4に、電力需要カートリッジ用に作成されるメソッドのファンクションを示します(1つを除きすべてODCIIndexで始まります)。

表15-4 索引タイプのメソッド

メソッド 説明

ODCIGetInterfaces()

型により実装されるインタフェースの名前のリストを戻します。

ODCIIndexCreate()

索引データを格納するための表を作成します。索引付けするデータを含む実表が空でない場合は、既存のデータの索引が作成されます。

このメソッドは、索引タイプを参照するCREATE INDEX文の発行時にコールされます。コール時には、PARAMETERS句に指定されたパラメータが索引の説明とともに渡されます。

ODCIIndexDrop()

索引データを格納する表を削除します。このメソッドは、DROP INDEX文で索引が指定されている場合にコールされます。

ODCIIndexStart()

演算子述語に関する索引のスキャンを初期化します。このメソッドは、ドメイン索引を使用して実行できる演算子関連の問合せの発行時にコールされます。

ODCIIndexFetch()

演算子述語を満たす各行のROWIDを戻します。

ODCIIndexClose()

索引の現在の使用を終了します。このメソッドでは、必要なクリーン・アップを実行できます。

ODCIIndexInsert()

索引タイプにより索引付けされる列またはオブジェクト属性を含んだ表にレコードが挿入されるときに、索引構造をメンテナンスします。

ODCIIndexDelete()

索引タイプにより索引付けされる列またはオブジェクト属性を含んだ表からレコードが削除されるときに、索引構造をメンテナンスします。

ODCIIndexUpdate()

索引タイプにより索引付けされる列またはオブジェクト属性を含んだ表のレコードが更新(変更)されるときに、索引構造をメンテナンスします。

ODCIIndexGetMetadata()

索引に関連付けられている実装固有のメタデータのエクスポートとインポートを可能にします。

15.4.6 型の定義

例15-6では、power_idxtype_imオブジェクト型を作成します。この型のメソッドは、ドメイン索引を定義、操作およびスキャンするためのODCIメソッドです。curnum属性は、ODCIIndexStart()ODCIIndexFetch()、およびODCIIndexClose()というスキャン・ルーチンのコンテキストとして使用されるカーソル番号です。

CREATE TYPE文の後に、各メンバー・ファンクションの実装を指定するCREATE TYPE BODY文があります。

CREATE OR REPLACE TYPE BODY power_idxtype_im 
IS
...

すべてのメソッド定義(VARCHAR2文字列を戻すODCIIndexGetMetadata()を除く)は、次の一般的な形式になっています。

  STATIC FUNCTION function-name (...) 
    RETURN NUMBER
  IS
  ...
  END;

例15-6 電力需要カートリッジのpower_idxtype_imオブジェクト型の作成

CREATE OR REPLACE TYPE power_idxtype_im AS OBJECT
(
  curnum NUMBER,
  STATIC FUNCTION ODCIGetInterfaces(ifclist OUT sys.ODCIObjectList)
     RETURN NUMBER,
  STATIC FUNCTION ODCIIndexCreate (ia sys.ODCIIndexInfo, parms VARCHAR2, 
     env sys.ODCIEnv) RETURN NUMBER,
  STATIC FUNCTION ODCIIndexDrop(ia sys.ODCIIndexInfo, env sys.ODCIEnv)
     RETURN NUMBER,
  STATIC FUNCTION ODCIIndexStart(sctx IN OUT power_idxtype_im,
                                 ia sys.ODCIIndexInfo,
                                 op sys.ODCIPredInfo, qi sys.ODCIQueryInfo,
                                 strt NUMBER, stop NUMBER,
                                 cmppos NUMBER, cmpval NUMBER, env sys.ODCIEnv) 
     RETURN NUMBER,
  STATIC FUNCTION ODCIIndexStart(sctx IN OUT power_idxtype_im,
                                 ia sys.ODCIIndexInfo,
                                 op sys.ODCIPredInfo, qi sys.ODCIQueryInfo,
                                 strt NUMBER, stop NUMBER,
                                 cmpval NUMBER, env sys.ODCIEnv)
     RETURN NUMBER,
  MEMBER FUNCTION ODCIIndexFetch(nrows NUMBER, rids OUT sys.ODCIRidList, 
     env sys.ODCIEnv) RETURN NUMBER,
  MEMBER FUNCTION ODCIIndexClose (env sys.ODCIEnv) RETURN NUMBER,
  STATIC FUNCTION ODCIIndexInsert(ia sys.ODCIIndexInfo, rid VARCHAR2,
                                  newval PowerDemand_Typ, env sys.ODCIEnv)
     RETURN NUMBER,
  STATIC FUNCTION ODCIIndexDelete(ia sys.ODCIIndexInfo, rid VARCHAR2,
                                  oldval PowerDemand_Typ, env sys.ODCIEnv)
     RETURN NUMBER,
  STATIC FUNCTION ODCIIndexUpdate(ia sys.ODCIIndexInfo, rid VARCHAR2,
                                  oldval PowerDemand_Typ, 
                                  newval PowerDemand_Typ, env sys.ODCIEnv) 
     RETURN NUMBER,
  STATIC FUNCTION ODCIIndexGetMetadata(ia sys.ODCIIndexInfo, 
                                       expversion VARCHAR2, 
                                       newblock OUT PLS_INTEGER, 
                                       env sys.ODCIEnv) 
     RETURN VARCHAR2
);
/
15.4.6.1 ODCIGetInterfaces()

ODCIGetInterfaces()ファンクションは、型により実装されるインタフェースの名前のリストを戻します。これらのインタフェースの現行のバージョンを指定するには、例15-7で示されるように、ODCIGetInterfaces()ルーチンがOUTパラメータで'SYS.ODCIINDEX2'を戻す必要があります。

例15-7 電力需要カートリッジのインタフェース・ファンクションおよび索引ファンクションの登録

STATIC FUNCTION ODCIGetInterfaces(
  ifclist OUT sys.ODCIObjectList)
RETURN NUMBER IS
BEGIN
  ifclist := sys.ODCIObjectList(sys.ODCIObject('SYS','ODCIINDEX2'));
  return ODCIConst.Success;
END ODCIGetInterfaces;
15.4.6.2 ODCIIndexCreate()

ODCIIndexCreate()ファンクションでは、索引データを格納する表を作成します。索引付けするデータを含む実表が空でない場合は、既存のデータの索引データ・エントリが挿入されます。

このファンクションは、SYS.ODCIINDEXINFO型のオブジェクト・パラメータとして索引情報を取ります。型の属性は、索引名、所有者名などです。CREATE INDEX文に指定されているPARAMETERS文字列も、例15-8で示されるように、このファンクションにパラメータとして渡されます。

例15-8 電力需要カートリッジのODCIIndexCreate()の登録

STATIC FUNCTION ODCIIndexCreate (
  ia sys.ODCIIndexInfo,
  parms VARCHAR2,
  env sys.ODCIEnv)
RETURN NUMBER IS
  i INTEGER;
  r ROWID;
  p NUMBER;
  v NUMBER;
  stmt1 VARCHAR2(1000);
  stmt2 VARCHAR2(1000);
  stmt3 VARCHAR2(1000);
  cnum1 INTEGER;
  cnum2 INTEGER;
  cnum3 INTEGER;
junk NUMBER;

索引データの表を作成するためのSQL文が構成され、実行されます。表には、実表rROWID、グリッド内のセルの位置を示す1から100の番号(cpos)およびそのセル内の電力需要値(cval)が含まれます。

BEGIN
  -- Construct the SQL statement.
  stmt1 := 'CREATE TABLE ' || ia.IndexSchema || '.' || ia.IndexName ||'_pidx' ||
      '( r ROWID, cpos NUMBER, cval NUMBER)';

  -- Dump the SQL statement.
  dbms_output.put_line('ODCIIndexCreate>>>>>');
  sys.ODCIIndexInfoDump(ia);
  dbms_output.put_line('ODCIIndexCreate>>>>>'||stmt1);

  -- Execute the statement.
  cnum1 := dbms_sql.open_cursor;
  dbms_sql.parse(cnum1, stmt1, dbms_sql.native);
  junk := dbms_sql.execute(cnum1);
  dbms_sql.close_cursor(cnum1);

このファンクションにより表に行が挿入され、索引が移入されます。また、VARRAY属性のネストが解除され、セルごとに表に1行が挿入されます。そのため、10x10の各グリッド(10行で行ごとに10個の値)が、表の100行(セルごとに1行)になります。

  -- Now populate the table.
  stmt2 := ' INSERT INTO '|| ia.IndexSchema || '.' || ia.IndexName || '_pidx' ||
      ' SELECT :rr, ROWNUM, column_value FROM THE' || ' (SELECT CAST (P.'||
      ia.IndexCols(1).ColName||'.CellDemandValues AS NumTab_Typ)'|| ' FROM ' ||
      ia.IndexCols(1).TableSchema || '.' || ia.IndexCols(1).TableName || ' P' ||
      ' WHERE P.ROWID = :rr)';
 
  -- Execute the statement.
  dbms_output.put_line('ODCIIndexCreate>>>>>'||stmt2);
 
  -- Parse the statement.
  cnum2 := dbms_sql.open_cursor;
  dbms_sql.parse(cnum2, stmt2, dbms_sql.native);
 
  stmt3 := 'SELECT ROWID FROM '|| ia.IndexCols(1).TableSchema || '.' ||
      ia.IndexCols(1).TableName;
  dbms_output.put_line('ODCIIndexCreate>>>>>'||stmt3);
  cnum3 := dbms_sql.open_cursor;
  dbms_sql.parse(cnum3, stmt3, dbms_sql.native);
  dbms_sql.define_column_rowid(cnum3, 1, r);   
  junk := dbms_sql.execute(cnum3);
 
   WHILE dbms_sql.fetch_rows(cnum3) > 0 LOOP
    -- Get column values of the row. --
    dbms_sql.column_value_rowid(cnum3, 1, r);
    -- Bind the row into the cursor for the next insert. --
    dbms_sql.bind_variable_rowid(cnum2, ':rr', r);
    junk := dbms_sql.execute(cnum2);
  END LOOP;

最後に、このファンクションはカーソルをクローズし、成功ステータスを戻して終了します。

  dbms_sql.close_cursor(cnum2);
  dbms_sql.close_cursor(cnum3);
  RETURN ODCICONST.SUCCESS;
END ODCIInexCreate;
15.4.6.3 ODCIIndexDrop()

ODCIIndexDrop()ファンクションは、例15-9で示されるように、索引データを格納する表を削除します。このメソッドは、DROP INDEX文の発行時にコールされます。

例15-9 電力需要カートリッジのODCIIndexDrop()の登録

STATIC FUNCTION ODCIIndexDrop(ia sys.ODCIIndexInfo, env sys.ODCIEnv)
RETURN NUMBER IS
  stmt VARCHAR2(1000);
  cnum INTEGER;
  junk INTEGER;
BEGIN
  -- Construct the SQL statement.
  stmt := 'drop table ' || ia.IndexSchema || '.' || ia.IndexName || '_pidx';
  
  dbms_output.put_line('ODCIIndexDrop>>>>>');
  sys.ODCIIndexInfoDump(ia);
  dbms_output.put_line('ODCIIndexDrop>>>>>'||stmt);
  
  -- Execute the statement.
  cnum := dbms_sql.open_cursor;
  dbms_sql.parse(cnum, stmt, dbms_sql.native);
  junk := dbms_sql.execute(cnum);
  dbms_sql.close_cursor(cnum);
  
  RETURN ODCICONST.SUCCESS;
END ODCIIndexDrop;
15.4.6.4 ODCIIndexStart()(Specific問合せ)

ODCIIndexStart()ファンクションの最初の定義により、演算子述語を満たす行をすべて戻すように索引のスキャンが初期化されます。たとえば、問合せでセル(3,7)の値が25であるインスタンスをすべて要求すると、ファンクションは指定の値であるセルの索引構成表の行をすべて戻すようにスキャンを初期化します。ODCIIndexStart()のこの定義は、「ODCIIndexStart()(Any問合せ)」で示した定義とは、セルの位置を示すcmpposパラメータを含むという点で異なります。

selfパラメータは、ODCIIndexFetch()およびODCIIndexClose()ファンクションと共有されるコンテキストです。iaパラメータには索引情報としてSYS.ODCIINDEXINFO型のオブジェクト・インスタンスが含まれ、opパラメータには演算子情報としてSYS.ODCIOPERINFO型のオブジェクト・インスタンスが含まれています。strtおよびstopパラメータは、演算子の戻り値の上限と下限の境界ポイントです。cmpposパラメータはセル位置で、cmpvalは演算子Power_XxxxxSpecific()で指定されたセルの値です。これは例15-10で示されています。

例15-10 電力需要カートリッジのODCIIndexStart()の登録

STATIC FUNCTION ODCIIndexStart(
  sctx IN OUT power_idxtype_im, 
  ia sys.ODCIIndexInfo,
  op sys.ODCIPredInfo, 
  qi sys.ODCIQueryInfo,
  strt NUMBER, stop NUMBER,
  cmppos NUMBER, 
  cmpval NUMBER, 
  env sys.ODCIEnv ) 
RETURN NUMBER IS
  cnum INTEGER;
  rid ROWID;
  nrows INTEGER;
  relop VARCHAR2(2);
  stmt VARCHAR2(1000);
BEGIN
  dbms_output.put_line('ODCIIndexStart>>>>>');
  sys.ODCIIndexInfoDump(ia);
  sys.ODCIPredInfoDump(op);
  dbms_output.put_line('start key : '||strt);
  dbms_output.put_line('stop key : '||stop);
  dbms_output.put_line('compare position : '||cmppos);
  dbms_output.put_line('compare value : '||cmpval);

このファンクションは、述語のエラーをチェックします。

  -- Take care of some error cases.
  -- The only predicates in which btree operators can appear are
  --    op() = 1     OR    op() = 0
  if (strt != 1) and (strt != 0) then
    raise_application_error(-20101, 'Incorrect predicate for operator');
  END if;
 
  if (stop != 1) and (stop != 0) then
    raise_application_error(-20101, 'Incorrect predicate for operator');
  END if;

次に、実行するSQL文が生成されます。演算子名と索引値の上限および下限(開始キーと終了キー)が判別されます。開始キーと終了キーは、両方とも1(= TRUE)または両方とも0(ゼロ)(= FALSE)に設定できます。

  -- Generate the SQL statement to be executed.
  -- First, figure out the relational operator needed for the statement.
  -- Take into account the operator name and the start and stop keys. For now, 
  -- the start and stop keys can both be 1 (= TRUE) or both be 0 (= FALSE).
  if op.ObjectName = 'POWER_EQUALS' then
    if strt = 1 then 
      relop := '=';
    else
      relop := '!=';
    end if;
  elsif op.ObjectName = 'POWER_LESSTHAN' then
    if strt = 1 then 
      relop := '<';
    else
      relop := '>=';
    end if;
  elsif op.ObjectName = 'POWER_GREATERTHAN' then
    if strt = 1 then 
      relop := '>';
    else
      relop := '<=';
    end if;
  else
    raise_application_error(-20101, 'Unsupported operator');
  end if;
 
  stmt := 'select r from '||ia.IndexSchema||'.'||ia.IndexName||'_pidx'||
      ' where cpos '|| '=' ||''''||cmppos||''''|| ' and cval ' ||relop||''''||
      cmpval||'''';
 
  dbms_output.put_line('ODCIIndexStart>>>>>' || stmt);
   cnum := dbms_sql.open_cursor;
  dbms_sql.parse(cnum, stmt, dbms_sql.native);
  dbms_sql.define_column_rowid(cnum, 1, rid);   
  nrows := dbms_sql.execute(cnum);

カーソル番号がODCIIndexFetchファンクションで使用されるコンテキストに格納され、成功を示す戻りステータスが設定されます。

  -- Set context as the cursor number.
  stcx := power_idxtype_im(cnum);
 
  -- Return success.
  RETURN ODCICONST.SUCCESS;
END ODCIIndexStart;
15.4.6.5 ODCIIndexStart()(Any問合せ)

ODCIIndexStart()ファンクションのこの定義により、演算子述語を満たす行をすべて戻すように索引のスキャンが初期化されます。たとえば、問合せで任意のセルの値が25であるインスタンスをすべて要求すると、ファンクションは指定の値であるセルの索引構成表の行をすべて戻すようにスキャンを初期化します。この定義は、「ODCIIndexStart()(Specific問合せ)」に示した定義とは、cmpposパラメータを含まないという点で異なります。

selfパラメータは、ODCIIndexFetch()およびODCIIndexClose()ファンクションと共有されるコンテキストです。iaパラメータには索引情報としてSYS.ODCIINDEXINFO型のオブジェクト・インスタンスが含まれ、opパラメータには演算子情報としてSYS.ODCIOPERINFO型のオブジェクト・インスタンスが含まれています。strtおよびstopパラメータは、演算子の戻り値の上限と下限の境界ポイントです。cmpvalパラメータは、演算子Power_Xxxx()で指定されたセルの値です。

例15-11 電力需要カートリッジのODCIIndexStart()(Any問合せ)の登録

STATIC FUNCTION ODCIIndexStart(
  sctx IN OUT power_idxtype_im, 
  ia sys.ODCIIndexInfo,
  op sys.ODCIPredInfo, 
  qi sys.ODCIQueryInfo,
  strt NUMBER, 
  stop NUMBER,
  cmpval NUMBER, 
  env sys.ODCIEnv ) 
RETURN NUMBER IS
  cnum INTEGER;
  rid ROWID;
  nrows INTEGER;
  relop VARCHAR2(2);
  stmt VARCHAR2(1000);
BEGIN
  dbms_output.put_line('ODCIIndexStart>>>>>');
  sys.ODCIIndexInfoDump(ia);
  sys.ODCIPredInfoDump(op);
  dbms_output.put_line('start key : '||strt);
  dbms_output.put_line('stop key : '||stop);
  dbms_output.put_line('compare value : '||cmpval);

このファンクションは、述語のエラーをチェックします。

  -- Take care of some error cases.
  -- The only predicates in which btree operators can appear are
  --    op() = 1     OR    op() = 0
  if (strt != 1) and (strt != 0) then
    raise_application_error(-20101, 'Incorrect predicate for operator');
  END if;
 
  if (stop != 1) and (stop != 0) then
    raise_application_error(-20101, 'Incorrect predicate for operator');
  END if;

次に、実行するSQL文が生成されます。演算子名と索引値の上限および下限(開始キーと終了キー)が判別されます。開始キーと終了キーは、両方とも1(= TRUE)または両方とも0(ゼロ)(= FALSE)に設定できます。

  -- Generate the SQL statement to be executed.
  -- First, figure out the relational operator needed for the statement.
  -- Take into account the operator name and the start and stop keys. For now, 
  -- the start and stop keys can both be 1 (= TRUE) or both be 0 (= FALSE).
  if op.ObjectName = 'POWER_EQUALSANY' then
    relop := '=';
  elsif op.ObjectName = 'POWER_LESSTHANANY' then
    relop := '<';
  elsif op.ObjectName = 'POWER_GREATERTHANANY' then
    relop := '>';
  else
    raise_application_error(-20101, 'Unsupported operator');
  end if;
 
  -- This statement returns the qualifying rows for the TRUE case.
  stmt := 'select distinct r from '||ia.IndexSchema||'.'||ia.IndexName||'_pidx'||'
      where cval '||relop||''''||cmpval||'''';
  -- In the FALSE case, we must find the  complement of the rows.
  if (strt = 0) then
    stmt := 'select distinct r from '||ia.IndexSchema||'.'||ia.IndexName||
          '_pidx'||' minus '||stmt;
  end if;
 
  dbms_output.put_line('ODCIIndexStart>>>>>' || stmt);
  cnum := dbms_sql.open_cursor;
  dbms_sql.parse(cnum, stmt, dbms_sql.native);
  dbms_sql.define_column_rowid(cnum, 1, rid);   
  nrows := dbms_sql.execute(cnum);

カーソル番号がODCIIndexFetch()ファンクションで使用されるコンテキストに格納され、成功を示す戻りステータスが設定されます。

  -- Set context as the cursor number.
  self := power_idxtype_im(cnum);
 
  -- Return success.
  RETURN ODCICONST.SUCCESS;
END ODCIIndexStart;
15.4.6.6 ODCIIndexFetch()

ODCIIndexFetch()ファンクションは、例15-12で示されるように、演算子述語を満たす行に対してROWIDのバッチを戻します。ODCIIndexFetch()がコールされるたびに、演算子述語を満たす行(ridsパラメータ、SYS.ODCIRIDLIST型のコレクション)の次のバッチが戻されます。各コールで戻すことのできる最大行数は、nrowsパラメータで指定されます。

Oracleは、演算子述語を満たす行がすべて戻されるまで、ODCIIndexFetch()を繰り返しコールします。

例15-12 電力需要カートリッジのODCIIndexFetch()の登録

MEMBER FUNCTION ODCIIndexFetch(
  nrows NUMBER, 
  rids OUT sys.ODCIRidList, 
  env sys.ODCIEnv)
RETURN NUMBER IS
  cnum INTEGER;
  idx INTEGER := 1;
  rlist sys.ODCIRidList := sys.ODCIRidList();
  done boolean := FALSE;

このファンクションは、ODCIIndexStart()ファンクションと同じカーソル番号cnumを使用して、ODCIIndexStart()ファンクションで選択された行のコレクションをループ処理し、ROWIDを戻します。

BEGIN
  dbms_output.put_line('ODCIIndexFetch>>>>>');
  dbms_output.put_line('Nrows : '||round(nrows));
 
  cnum := self.curnum;
 
  WHILE not done LOOP
    if idx > nrows then
       done := TRUE;
    else
      rlist.extEND;
      if dbms_sql.fetch_rows(cnum) > 0 then
        dbms_sql.column_value_rowid(cnum, 1, rlist(idx));
        idx := idx + 1;
      else
        rlist(idx) := null;
        done := TRUE;
      END if;
    END if;   
  END LOOP;
 
  rids := rlist;
  RETURN ODCICONST.SUCCESS;
END ODCIIndexFetch;
15.4.6.7 ODCIIndexClose()

ODCIIndexClose()ファンクションは、例15-13に示されるように、ODCIIndexStart()およびODCIIndexFetch()ファンクションで使用されたカーソルをクローズします。

例15-13 電力需要カートリッジのODCIIndexStart()の登録

MEMBER FUNCTION ODCIIndexClose (env sys.ODCIEnv) 
RETURN NUMBER IS 
  cnum INTEGER;
BEGIN
  dbms_output.put_line('ODCIIndexClose>>>>>');
 
  cnum := self.curnum;
  dbms_sql.close_cursor(cnum);
  RETURN ODCICONST.SUCCESS;
END ODCIIndexClose;
15.4.6.8 ODCIIndexInsert()

ODCIIndexInsert()ファンクションは、例15-14に示されるように、索引タイプによって索引付けられたOBJECT属性または列を含む表にレコードが挿入されるときにコールされます。索引列の新規の値が、対応するROWIDとともに引数として渡されます。

例15-14 電力需要カートリッジのODCIIndexInsert()の登録

STATIC FUNCTION ODCIIndexInsert(
  ia sys.ODCIIndexInfo, 
  rid VARCHAR2, 
  newval PowerDemand_Typ, 
  env sys.ODCIEnv) 
RETURN NUMBER AS 
  cid INTEGER; 
  i BINARY_INTEGER;
  nrows INTEGER;
  stmt VARCHAR2(1000);
BEGIN 
  dbms_output.put_line(' ');
  dbms_output.put_line('ODCIIndexInsert>>>>>'||' TotGridDemand= '||
      newval.TotGridDemand ||' MaxCellDemand= '||newval.MaxCellDemand ||
      ' MinCellDemand= '||newval.MinCellDemand) ;
  sys.ODCIIndexInfoDump(ia); 
      
  -- Construct the statement.
  stmt := ' INSERT INTO '|| ia.IndexSchema || '.' || ia.IndexName || '_pidx' ||
      ' VALUES (:rr, :pos, :val)';
  
  -- Execute the statement.
  dbms_output.put_line('ODCIIndexInsert>>>>>'||stmt);
  -- Parse the statement.
  cid := dbms_sql.open_cursor;
  dbms_sql.parse(cid, stmt, dbms_sql.native);
  dbms_sql.bind_variable_rowid(cid, ':rr', rid);
      
  -- Iterate over the rows of the Varray and insert them.
  i := newval.CellDemandValues.FIRST;   
  WHILE i IS NOT NULL LOOP
    -- Bind the row into the cursor for insert.
    dbms_sql.bind_variable(cid, ':pos', i);   
    dbms_sql.bind_variable(cid, ':val', newval.CellDemandValues(i));
    -- Execute.
    nrows := dbms_sql.execute(cid);
    dbms_output.put_line('ODCIIndexInsert>>>>>('||'RID'||' , '||i|| ' , '||
           newval.CellDemandValues(i)|| ')');
    i := newval.CellDemandValues.NEXT(i);
  END LOOP;

  dbms_sql.close_cursor(cid);
  RETURN ODCICONST.SUCCESS;
END ODCIIndexInsert;
15.4.6.9 ODCIIndexDelete()

ODCIIndexDelete()ファンクションは、例15-15に示されるように、索引タイプにより索引付けされた列またはオブジェクト属性を含んだ表からレコードが削除されるときにコールされます。索引列の古い値が、対応するROWIDとともに引数として渡されます。

例15-15 電力需要カートリッジのODCIIndexDelete()の登録

STATIC FUNCTION ODCIIndexDelete(
  ia sys.ODCIIndexInfo, 
  rid VARCHAR2,
  oldval PowerDemand_Typ,
  env sys.ODCIEnv) 
RETURN NUMBER AS 
  cid INTEGER; 
  stmt VARCHAR2(1000);
  nrows INTEGER; 
BEGIN 
  dbms_output.put_line(' ');
  dbms_output.put_line('ODCIIndexDelete>>>>>'||' TotGridDemand= '||
      oldval.TotGridDemand ||' MaxCellDemand= '||oldval.MaxCellDemand ||
      ' MinCellDemand= '||oldval.MinCellDemand) ;
  sys.ODCIIndexInfoDump(ia); 
 
  -- Construct the statement.
  stmt := ' DELETE FROM '|| ia.IndexSchema || '.' ||ia.IndexName|| '_pidx' || 
      ' WHERE r=:rr';
  dbms_output.put_line('ODCIIndexDelete>>>>>'||stmt);
 
  -- Parse and execute the statement.
  cid := dbms_sql.open_cursor;
  dbms_sql.parse(cid, stmt, dbms_sql.native);
  dbms_sql.bind_variable_rowid(cid, ':rr', rid);
  nrows := dbms_sql.execute(cid);     
  dbms_sql.close_cursor(cid);
 
  RETURN ODCICONST.SUCCESS;
END ODCIIndexDelete;
15.4.6.10 ODCIIndexUpdate()

ODCIIndexUpdate()ファンクションは、例15-16に示されるように、索引タイプにより索引付けされる列またはオブジェクト属性を含んだ表のレコードが更新されるときにコールされます。索引列の古い値と新しい値が、ROWIDとともに引数として渡されます。

例15-16 電力需要カートリッジのODCIIndexUpdate()の登録

STATIC FUNCTION ODCIIndexUpdate(
  ia sys.ODCIIndexInfo, 
  rid VARCHAR2, 
  oldval PowerDemand_Typ, 
  newval PowerDemand_Typ, 
  env sys.ODCIEnv) 
RETURN NUMBER AS 
  cid INTEGER; 
  cid2 INTEGER; 
  stmt VARCHAR2(1000);
  stmt2 VARCHAR2(1000);
  nrows INTEGER; 
  i NUMBER;
BEGIN 
  dbms_output.put_line(' ');
  dbms_output.put_line('ODCIIndexUpdate>>>>> Old'||' TotGridDemand= '||
      oldval.TotGridDemand||' MaxCellDemand= '||oldval.MaxCellDemand ||
      ' MinCellDemand= '||oldval.MinCellDemand) ;
  dbms_output.put_line('ODCIIndexUpdate>>>>> New'||' TotGridDemand= '||
      newval.TotGridDemand ||' MaxCellDemand= '||newval.MaxCellDemand ||
      ' MinCellDemand= '||newval.MinCellDemand) ;
  sys.ODCIIndexInfoDump(ia); 

  -- Delete old entries.
  stmt := ' DELETE FROM '||ia.IndexSchema ||'.'||ia.IndexName||'_pidx'|| 
      ' WHERE r=:rr';
  dbms_output.put_line('ODCIIndexUpdate>>>>>'||stmt);
  
  -- Parse and execute the statement.
  cid := dbms_sql.open_cursor;
  dbms_sql.parse(cid, stmt, dbms_sql.native);
  dbms_sql.bind_variable_rowid(cid, ':rr', rid);
  nrows := dbms_sql.execute(cid);     
  dbms_sql.close_cursor(cid);
 
  -- Insert new entries.
  stmt2 := ' INSERT INTO '||ia.IndexSchema||'.'||ia.IndexName||'_pidx'||
      ' VALUES (:rr, :pos, :val)';
  dbms_output.put_line('ODCIIndexUpdate>>>>>'||stmt2);
 
  -- Parse and execute the statement.
  cid2 := dbms_sql.open_cursor;
  dbms_sql.parse(cid2, stmt2, dbms_sql.native);
  dbms_sql.bind_variable_rowid(cid2, ':rr', rid);
     
  -- Iterate over the rows of the Varray and insert them.
  i := newval.CellDemandValues.FIRST;   
  WHILE i IS NOT NULL LOOP
    -- Bind the row into the cursor for insert.
    dbms_sql.bind_variable(cid2, ':pos', i);   
    dbms_sql.bind_variable(cid2, ':val', newval.CellDemandValues(i));
    nrows := dbms_sql.execute(cid2);
    dbms_output.put_line('ODCIIndexUpdate>>>>>('||'RID'||' , '||i ||' , '||
         newval.CellDemandValues(i)|| ')');
    i := newval.CellDemandValues.NEXT(i);
  END LOOP;
  dbms_sql.close_cursor(cid2);
 
  RETURN ODCICONST.SUCCESS;
END ODCIIndexUpdate;

ODCIIndexUpdateは、次のように終了するCREATE TYPE BODY文で定義されている最後のメソッドです。

END;
/
15.4.6.11 ODCIIndexGetMetadata()

オプションのODCIIndexGetMetadata()ファンクションが存在する場合は、例15-17で示されるように、実装固有の(システム・カタログに格納されない)メタデータをエクスポート・ダンプ・ファイルに書き込むために、エクスポート・ユーティリティによりコールされます。このメタデータは、ポリシー情報、バージョン情報、ユーザー設定などです。このメタデータは、関連索引が作成される直前のインポート時に実行される無名PL/SQLブロックとしてダンプ・ファイルに書き込まれます。

このメソッドは、PL/SQLブロックのコードで構成されるエクスポート・ユーティリティに文字列を戻します。エクスポート・ユーティリティは、長さ0(ゼロ)の文字列が戻されるまで、このメソッドを繰り返しコールするため、任意の複雑度を持つ必要な数のPL/SQLブロックを作成できます。通常、このメソッドは、カーソルや反復カウンタなど、エクスポートによる複数のコール間で状態を維持するパッケージ・レベルの変数を使用するために、PL/SQLパッケージ内のファンクションをコールします。

電力需要カートリッジでは、渡されるメタデータはバージョン文字列V1.0のみです。このメタデータは、ドメイン索引の基になっている索引構成表の現在の形式を識別するものです。power_pkg.getversionファンクションは、power_pkgcheckversionプロシージャへのコールを生成し、インポート時にバージョン文字列がV1.0であることを確認するために、このプロシージャが実行されるようにします。

例15-17 電力需要カートリッジのODCIIndexGetMetadata()の登録

STATIC FUNCTION ODCIIndexGetMetadata(
  ia sys.ODCIIndexInfo, 
  expversion VARCHAR2, 
  newblock OUT PLS_INTEGER, 
  env sys.ODCIEnv) 
RETURN VARCHAR2 IS 
 
BEGIN 
  -- Let getversion do all the work since it has to maintain state across calls. 
 
  RETURN power_pkg.getversion (ia.IndexSchema, ia.IndexName, newblock); 
 
  EXCEPTION 
    WHEN OTHERS THEN 
      RAISE; 
 
END ODCIIndexGetMetaData; 
 

power_pkgパッケージの定義は次のとおりです。

例15-18 電力需要カートリッジのパッケージpower_pkgの作成

CREATE OR REPLACE PACKAGE power_pkg AS  
  FUNCTION getversion(
    idxschema IN VARCHAR2, 
    idxname IN VARCHAR2,
    newblock OUT PLS_INTEGER) 
  RETURN VARCHAR2; 

  PROCEDURE checkversion (
  version IN VARCHAR2);  
END power_pkg; 
/ 
SHOW ERRORS; 
 
CREATE OR REPLACE PACKAGE BODY power_pkg AS  
  -- iterate is a package-level variable used to maintain state across calls 
  -- by Export in this session. 
  
  iterate NUMBER := 0;  
  
  FUNCTION getversion(
    idxschema IN VARCHAR2, 
    idxname IN VARCHAR2,  
    newblock OUT PLS_INTEGER) 
  RETURN VARCHAR2 IS  
  
  BEGIN  
  
  -- We are generating only one PL/SQL block consisting of one line of code.
    newblock := 1; 
  
    IF iterate = 0 THEN  
      -- Increment iterate so we'll know we're done next time we're called. 
      iterate := iterate + 1;  
  
      -- Return a string that calls checkversion with a version 'V1.0' 
      -- Note that export adds the surrounding BEGIN/END pair to form the anon. 
      -- block... we don't have to. 
  
      RETURN 'power_pkg.checkversion(''V1.0'');';  
    ELSE  
      -- reset iterate for next index  
      iterate := 0;  
      -- Return a 0-length string; we won't be called again for this index. 
      RETURN '';  
    END IF;  
  END getversion;  
  
  PROCEDURE checkversion (version IN VARCHAR2) 
  IS  
    wrong_version EXCEPTION; 
 
  BEGIN  
    IF version != 'V1.0' THEN 
      RAISE wrong_version; 
    END IF; 
  END checkversion;  

END power_pkg;

関連項目:

エクスポート・ユーティリティとインポート・ユーティリティの詳細は、『Oracle Databaseユーティリティ』を参照してください。

15.4.7 索引タイプの作成

電力需要カートリッジでは、ドメイン索引の索引タイプが作成されます。指定には、例15-19で示されるように、索引タイプでサポートされている演算子のリストが含まれます。また、OCDI索引ルーチンを含む実装タイプも識別されます。

例15-19 電力需要カートリッジの索引タイプpower_idxtypeの作成

CREATE OR REPLACE INDEXTYPE power_idxtype
FOR
  Power_Equals(PowerDemand_Typ, NUMBER, NUMBER),
  Power_GreaterThan(PowerDemand_Typ, NUMBER, NUMBER),
  Power_LessThan(PowerDemand_Typ, NUMBER, NUMBER),
  Power_EqualsAny(PowerDemand_Typ, NUMBER),
  Power_GreaterThanAny(PowerDemand_Typ, NUMBER),
  Power_LessThanAny(PowerDemand_Typ, NUMBER)
USING power_idxtype_im;

15.5 拡張可能最適化に使用する型とメソッドの定義

電力需要カートリッジのうち、拡張可能最適化に関連する部分について考えます。

15.5.1 統計表PowerCartUserStatsの作成

PowerCartUserStatsを使用して、例15-20で示されるように、1時間当たりの電力グリッドの測定値に関する統計が格納されます。メソッドODCIStatsSelectivity()は、これらの統計を使用して、演算子述語の選択性を推測します。収集される統計のタイプの関係で、Oracleに統計を格納させるよりも個別の表を使用する方が便利です。

PowerCartUserStats表には、次の列が含まれています。

  • 統計が収集される表および列

  • 統計が収集されるセル

  • すべての電力グリッドの測定値にわたる特定のセルの最小および最大電力需要

  • すべての電力グリッドの測定値にわたる特定のセルのNULL以外の測定値の数

例15-20 電力需要カートリッジの統計表PowerCartUserStatsの作成

CREATE TABLE PowerCartUserStats (
  -- Table for which statistics are collected
  tab VARCHAR2(30),
  -- Column for which statistics are collected
  col VARCHAR2(30),
  -- Cell position
  cpos NUMBER,
  -- Minimum power demand for the given cell
  lo NUMBER,
  -- Maximum power demand for the given cell
  hi NUMBER,
  -- Number of (non-null) power demands for the given cell
  nrows NUMBER
);
/

15.5.2 拡張可能オプティマイザ・メソッドの作成

電力需要カートリッジでは、拡張可能オプティマイザにより使用されるメソッドを指定するオブジェクト型が作成されます。これらのメソッドはODCIStatsインタフェースの一部であり、DBMS_STATSパッケージのメソッドによりコールされるメソッドを集合的に定義するか、または、オプティマイザが問合せに最適の実行計画を決定するときに集合的に定義します。

表15-5に、電力需要カートリッジ用に作成されるメソッドのファンクションを示します。1つを除くすべてのファンクション名は文字列ODCIStatsで始まります。

表15-5 拡張可能オプティマイザ・メソッド

メソッド 説明

ODCIGetInterfaces()

型により実装されるインタフェースの名前のリストを戻します。

ODCIStatsCollect()

PowerDemand_Typ型の列または索引タイプpower_idxtypeのドメイン索引の統計を収集します。

このメソッドは、PowerDemand_Typ型の列またはpower_idxtype索引タイプの索引のいずれかを参照する文が発行されるときにコールされます。コール時に、指定されたオプションが列または索引の説明とともに渡されます。

ODCIStatsDelete()

PowerDemand_Typ型の列または索引タイプpower_idxtypeのドメイン索引の統計を削除します。

このメソッドは、該当する型の列または該当する索引タイプの索引の統計を削除する文の発行時にコールされます。

ODCIStatsSelectivity()

演算子またはファンクション実装に関係する述語の選択性を計算します。

問合せのWHERE句に該当する型の条件が指定されている場合に、オプティマイザによりコールされます。

ODCIStatsIndexCost()

ドメイン索引のアクセス・パスのコストを計算します。

索引を問合せに使用できる場合にドメイン索引のアクセス・パスのコストを取得するために、オプティマイザによりコールされます。

ODCIStatsFunctionCost()

ファンクションのコストを計算します。

ファンクション実行のコストを取得するために、オプティマイザによりコールされます。ファンクションは、演算子の実装でない場合もあります。

15.5.2.1 型定義の作成

例15-21では、power_statisticsオブジェクト型を作成します。このオブジェクト型のODCIメソッドは、列および索引に関する統計の収集および削除、演算子またはファンクションによる述語の選択性の計算、ドメイン索引およびファンクションのコストの計算に使用されます。curnum属性は使用されません。

CREATE TYPE文の後に、各メンバー・ファンクションの実装を指定するCREATE TYPE BODY文があります。

CREATE OR REPLACE TYPE BODY power_statistics
IS
...

すべてのファンクション定義は、次の一般的な形式で指定されます。

    STATIC FUNCTION function-name (...)
      BEGIN
        RETURN NUMBER IS
      END;

例15-21 電力需要カートリッジのpower_statisticsオブジェクト型定義の作成

CREATE OR REPLACE TYPE power_statistics AS OBJECT
(
  curnum NUMBER,
  STATIC FUNCTION ODCIGetInterfaces(ifclist OUT sys.ODCIObjectList) 
    RETURN NUMBER,
  STATIC FUNCTION ODCIStatsCollect(col sys.ODCIColInfo, 
      options sys.ODCIStatsOptions, rawstats OUT RAW, env sys.ODCIEnv) 
    RETURN NUMBER,
  STATIC FUNCTION ODCIStatsDelete(col sys.ODCIColInfo, env sys.ODCIEnv) 
      RETURN NUMBER,
  STATIC FUNCTION ODCIStatsCollect(ia sys.ODCIIndexInfo,
      options sys.ODCIStatsOptions, rawstats OUT RAW, env sys.ODCIEnv) 
    RETURN NUMBER,
  STATIC FUNCTION ODCIStatsDelete(ia sys.ODCIIndexInfo, env sys.ODCIEnv) 
      RETURN NUMBER,
  STATIC FUNCTION ODCIStatsSelectivity(pred sys.ODCIPredInfo,
      sel OUT NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER,
      object PowerDemand_Typ, cell NUMBER, value NUMBER, env sys.ODCIEnv) 
    RETURN NUMBER,
  STATIC FUNCTION ODCIStatsSelectivity(pred sys.ODCIPredInfo, sel OUT NUMBER, 
      args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, object PowerDemand_Typ,
      value NUMBER, env sys.ODCIEnv) 
    RETURN NUMBER,
  STATIC FUNCTION ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, 
      cost OUT sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, 
      args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, cmppos NUMBER, 
      cmpval NUMBER, env sys.ODCIEnv)
    RETURN NUMBER,
  STATIC FUNCTION ODCIStatsIndexCost(ia sys.ODCIIndexInfo, sel NUMBER, 
      cost OUT sys.ODCICost, qi sys.ODCIQueryInfo, pred sys.ODCIPredInfo, 
      args sys.ODCIArgDescList, strt NUMBER, stop NUMBER, cmpval NUMBER, 
      env sys.ODCIEnv) 
    RETURN NUMBER,
  STATIC FUNCTION ODCIStatsFunctionCost(func sys.ODCIFuncInfo, 
      cost OUT sys.ODCICost, args sys.ODCIArgDescList, object PowerDemand_Typ,
      cell NUMBER, value NUMBER, env sys.ODCIEnv) 
    RETURN NUMBER,
  STATIC FUNCTION ODCIStatsFunctionCost(func sys.ODCIFuncInfo,
      cost OUT sys.ODCICost, args sys.ODCIArgDescList, object PowerDemand_Typ,
      value NUMBER, env sys.ODCIEnv) 
    RETURN NUMBER,
  STATIC FUNCTION ODCIStatsFunctionCost(func sys.ODCIFuncInfo,
      cost OUT sys.ODCICost, args sys.ODCIArgDescList, object PowerDemand_Typ,
      cell NUMBER, value NUMBER, env sys.ODCIEnv)
    RETURN NUMBER
);
/
15.5.2.2 ODCIGetInterfaces()

ODCIGetInterfaces()ファンクションは、例15-22で示されるように、型により実装されるインタフェースの名前のリストを戻します。拡張可能オプティマイザ・インタフェース・ルーチンはSYS.ODCISTATSの1組のみですが、サーバーでは下位互換性を維持するために複数のバージョンがサポートされます。ルーチンの現行バージョンを指定するには、ファンクションODCIGetInterfaces()OUTODCIObjectListパラメータでSYS.ODCISTATS2を指定する必要があります。

例15-22 電力需要カートリッジのインタフェース・ファンクションおよび統計ファンクションの登録

STATIC FUNCTION ODCIGetInterfaces(
  ifclist OUT sys.ODCIObjectList)
RETURN NUMBER IS
BEGIN
  ifclist := sys.ODCIObjectList(sys.ODCIObject('SYS','ODCISTATS2'));
  RETURN ODCIConst.Success;
END ODCIGetInterfaces;
15.5.2.3 PowerDemand_Typ列のODCIStatsCollect()メソッド

ODCIStatsCollect()ファンクションは、例15-23で示されるように、データ型がPowerDemand_Typオブジェクト型である列の統計を収集します。統計は、すべての電力グリッド測定値にわたって列のセルごとに収集されます。特定のセルについて統計として収集されるのは、電力グリッドの最小および最大測定値と、NULL以外の測定値の数です。

このファンクションは、SYS.ODCICOLINFO型のオブジェクト・パラメータとして列情報を取ります。型の属性は、表名、列名などです。列統計の収集に使用するDBMS_STATSパッケージのコマンドで指定されたオプションも、パラメータとして渡されます。電力需要カートリッジでは統計が表に格納されるため、出力パラメータrawstatsは使用されません。

例15-23 電力需要カートリッジのODCIStatsCollect()の登録

STATIC FUNCTION ODCIStatsCollect(
  col sys.ODCIColInfo,
  options sys.ODCIStatsOptions,
  rawstats OUT RAW, 
  env sys.ODCIEnv)
RETURN NUMBER IS
  cnum                INTEGER;
  stmt                VARCHAR2(1000);
  junk                INTEGER;
  cval                NUMBER;
  colname             VARCHAR2(30) := rtrim(ltrim(col.colName, '"'), '"');
  statsexists         BOOLEAN := FALSE;
  pdemands            PowerDemand_Tab%ROWTYPE;
  user_defined_stats  PowerCartUserStats%ROWTYPE;

  CURSOR c1(tname VARCHAR2, cname VARCHAR2) IS
    SELECT * FROM PowerCartUserStats
    WHERE tab = tname AND col = cname;
  CURSOR c2 IS
    SELECT * FROM PowerDemand_Tab;

  BEGIN
    sys.ODCIColInfoDump(col);
    sys.ODCIStatsOptionsDump(options);

    IF (col.TableSchema IS NULL OR col.TableName IS NULL OR col.ColName IS NULL)
    THEN
      RETURN ODCIConst.Error;
    END IF;

    dbms_output.put_line('ODCIStatsCollect>>>>>');
    dbms_output.put_line('**** Analyzing column '||col.TableSchema|| '.' ||
        col.TableName|| '.' || col.ColName);

    -- Check if statistics exist for this column
    FOR user_defined_stats IN c1(col.TableName, colname) LOOP
      statsexists := TRUE;
      EXIT;
    END LOOP;

ファンクションでは、この列の統計が存在するかどうかがチェックされます。存在する場合は統計がNULLに初期化され、存在しない場合は100個のセルについて統計が個別に作成され、NULLに初期化されます。

    IF not statsexists THEN
      -- column statistics don't exist; create entries for each of the 100 cells
      cnum := dbms_sql.open_cursor;
      FOR i in 1..100 LOOP
        stmt := 'INSERT INTO PowerCartUserStats VALUES( '||''''|| col.TableName ||
            ''', '||''''||colname||''', '||to_char(i)||', '||'NULL, NULL, NULL)';
        dbms_sql.parse(cnum, stmt, dbms_sql.native);
        junk := dbms_sql.execute(cnum);
      END LOOP;
      dbms_sql.close_cursor(cnum);
    ELSE
      -- column statistics exist; initialize to NULL
      cnum := dbms_sql.open_cursor;
      stmt := 'UPDATE PowerCartUserStats'||
          ' SET lo = NULL, hi = NULL, nrows = NULL'||' WHERE tab = '||
          col.TableName||' AND col = '||colname;
      dbms_sql.parse(cnum, stmt, dbms_sql.native);
      junk := dbms_sql.execute(cnum);
      dbms_sql.close_cursor(cnum);
    END IF;

ファンクションにより分析対象の表から行が読み取られ、列の統計が収集されます。そのために、SQL文が構成されて実行されます。

    -- For each cell position, the following statistics are collected:
    --   maximum value
    --   minimum value
    --   number of rows (excluding NULLs)
    cnum := dbms_sql.open_cursor;
    FOR i in 1..100 LOOP
      FOR pdemands IN c2 LOOP
        IF i BETWEEN pdemands.sample.CellDemandValues.FIRST AND
            pdemands.sample.CellDemandValues.LAST THEN
          cval := pdemands.sample.CellDemandValues(i);
          stmt := 'UPDATE PowerCartUserStats SET '|| 'lo = least(' || 'NVL(' ||
              to_char(cval)||', lo), '||'NVL('||'lo, '||to_char(cval)||')), '||
              'hi = greatest('||'NVL('||to_char(cval)||', hi), '||'NVL('||
              'hi, '||to_char(cval)||')), '||
              'nrows = decode(nrows, NULL, decode('||to_char(cval)||
              ', NULL, NULL, 1), decode('||to_char(cval)|| 
              ', NULL, nrows, nrows+1)) '||'WHERE cpos = '||to_char(i)||
              ' AND tab = '''||col.TableName||''''||' AND col = '''||colname||
              '''';
          dbms_sql.parse(cnum, stmt, dbms_sql.native);
          junk := dbms_sql.execute(cnum);
        END IF;
      END LOOP;
    END LOOP;

最後に、このファンクションはカーソルをクローズし、成功ステータスを戻して終了します。

    dbms_sql.close_cursor(cnum);
    rawstats := NULL;
    return ODCIConst.Success;

  END ODCIStatsCollect;
15.5.2.4 PowerDemand_Typ列のODCIStatsDelete()メソッド

ODCIStatsDelete()ファンクションは、例15-24で示されるように、データ型がPowerDemand_Typオブジェクト型である列の統計を削除します。このファンクションは、SYS.ODCICOLINFO型のオブジェクト・パラメータとして列情報を取ります。型の属性は、表名、列名などです。

例15-24 電力需要カートリッジのODCIStatsDelete()の登録

STATIC FUNCTION ODCIStatsDelete(
  col sys.ODCIColInfo, 
  env sys.ODCIEnv)
RETURN NUMBER IS
  cnum                INTEGER;
  stmt                VARCHAR2(1000);
  junk                INTEGER;
  colname             VARCHAR2(30) := rtrim(ltrim(col.colName, '"'), '"');
  statsexists         BOOLEAN := FALSE;
  user_defined_stats  PowerCartUserStats%ROWTYPE;

  CURSOR c1(tname VARCHAR2, cname VARCHAR2) IS
    SELECT * FROM PowerCartUserStats
    WHERE tab = tname AND col = cname;
  BEGIN
  sys.ODCIColInfoDump(col);

  IF (col.TableSchema IS NULL OR col.TableName IS NULL OR col.ColName IS NULL)
  THEN
    RETURN ODCIConst.Error;
  END IF;

  dbms_output.put_line('ODCIStatsDelete>>>>>');
  dbms_output.put_line('**** Analyzing (delete) column '|| col.TableSchema|| 
      '.' ||col.TableName||'.'||col.ColName);

このファンクションは、統計表をチェックして列の統計が存在することを確認します。統計が収集されていない場合は、何も実行されません。ただし、統計が存在する場合は、SQL文が構成されて実行され、統計表から関連する行が削除されます。

  -- Check if statistics exist for this column
  FOR user_defined_stats IN c1(col.TableName, colname) LOOP
    statsexists := TRUE;
    EXIT;
  END LOOP;

  -- If user-defined statistics exist, delete them
  IF statsexists THEN
    stmt := 'DELETE FROM PowerCartUserStats'||' WHERE tab = '''||col.TableName||
        ''''|| ' AND col = ''' || colname || '''';
    cnum := dbms_sql.open_cursor;
    dbms_output.put_line('ODCIStatsDelete>>>>>');
    dbms_output.put_line('ODCIStatsDelete>>>>>' || stmt);
    dbms_sql.parse(cnum, stmt, dbms_sql.native);
    junk := dbms_sql.execute(cnum);
    dbms_sql.close_cursor(cnum);
  END IF;

  RETURN ODCIConst.Success;
END ODCStatsDelete;
15.5.2.5 power_idxtypeドメイン索引のODCIStatsCollect()メソッド

ODCIStatsCollect()ファンクションは、例15-25で示されるように、索引タイプがpower_idxtypeであるドメイン索引の統計を収集します。電力需要カートリッジの場合、このファンクションは単に索引データを格納する索引構成表を分析します。

このファンクションは、SYS.ODCIINDEXINFO型のオブジェクト・パラメータとして索引情報を取ります。型の属性は、索引名、所有者名などです。索引統計の収集に使用するDBMS_STATSパッケージで指定されたオプションも、パラメータとして渡されます。出力パラメータrawstatsは使用されません。

例15-25 電力需要カートリッジのODCIStatsCollect()の登録

STATIC FUNCTION ODCIStatsCollect (
  ia sys.ODCIIndexInfo,
  options sys.ODCIStatsOptions, 
  rawstats OUT RAW, 
  env sys.ODCIEnv)
RETURN NUMBER IS
  stmt                VARCHAR2(1000);
 
BEGIN
  -- To analyze a domain index, analyze the table that implements the index
  sys.ODCIIndexInfoDump(ia);
  sys.ODCIStatsOptionsDump(options);

  stmt := 'dbms_stats.gather_table_stats('
         || '''' || ia.IndexSchema || ''', '
         || '''' || ia.IndexName || '_pidx' || ''');';
  dbms_output.put_line('**** Analyzing index '
         || ia.IndexSchema || '.' || ia.IndexName);
  dbms_output.put_line('SQL Statement: ' || stmt); 
  EXECUTE IMMEDIATE 'BEGIN ' || stmt || ' END;';
  rawstats := NULL;

  RETURN ODCIConst.Success;
END ODCIStatsCollect;
15.5.2.6 power_idxtypeドメイン索引のODCIStatsDelete()メソッド

ODCIStatsDelete()ファンクションは、例15-26で示されるように、索引タイプがpower_idxtypeであるドメイン索引の統計を削除します。電力需要カートリッジの場合、このファンクションは単に索引データを格納する索引構成表の統計を削除します。

このファンクションは、SYS.ODCIINDEXINFO型のオブジェクト・パラメータとして索引情報を取ります。型の属性は、索引名、所有者名などです。

例15-26 電力需要カートリッジのドメイン索引のODCIStatsDelete()の登録

STATIC FUNCTION ODCIStatsDelete(
  ia sys.ODCIIndexInfo,
  env sys.ODCIEnv)
RETURN NUMBER IS
  stmt                VARCHAR2(1000);
BEGIN
  -- To delete statistics for a domain index, delete the statistics for the
  -- table implementing the index
  sys.ODCIIndexInfoDump(ia);
  stmt := 'dbms_stats.delete_table_stats('|| '''' || ia.IndexSchema || ''', '
      || '''' || ia.IndexName || '_pidx' || ''');';
  dbms_output.put_line('**** Analyzing (delete) index '||ia.IndexSchema||'.'||
      ia.IndexName);
  dbms_output.put_line('SQL Statement: ' || stmt);

  EXECUTE IMMEDIATE 'BEGIN ' || stmt || ' END;';
  RETURN ODCIConst.Success;
END ODCIStatsDelete;
15.5.2.7 ODCIStatsSelectivity()メソッド(Specific問合せの場合)

ODCIStatsSelectivity()ファンクションの最初の定義では、Specific問合せに対する演算子またはファンクションの述語の選択性が見積られます。たとえば、セル(3,7)の値が25であるすべてのインスタンスを問合せによって検索する場合、このファンクションは指定されたセルが特定の値である行のパーセンテージを推測します。

predパラメータには、ファンクションの詳細(演算子述語内の演算子のファンクション実装)が含まれます。このパラメータは、SYS.ODCIPREDINFO型のオブジェクト・インスタンスです。選択性は、sel出力パラメータでパーセンテージとして戻されます。argsパラメータ(SYS.ODCIARGDESCLIST型のオブジェクト・インスタンス)には、ファンクションの各引数の記述子と、ファンクションの開始値および終了値が含まれます。たとえば、引数が列である場合、引数記述子には、表名、列名などが含まれます。strtおよびstopパラメータは、ファンクションの戻り値の上限と下限の境界ポイントです。述語内のファンクションにPowerDemand_Typ型のリテラルが含まれている場合、objectパラメータには、オブジェクト・コンストラクタの形式の値が含まれます。cellパラメータはセルの位置、valueパラメータはファンクション(PowerXxxxxSpecific_Func)で指定されるセルの値です。

選択性は、単純な範囲述語に使用されるものと同様の方法で見積られます。たとえば、述語の選択性の単純な見積りは次のようになります。

  c > v

これが(M-v)/(M-m)であるとします。mおよびMは、それぞれ値vmMの間にある場合の列c(列統計から判別)の最小値と最大値です。

get_selectivityファンクションは、例15-27で示されるように、述語で指定された列の最小値と最大値について、単純な範囲述語の選択性を計算します。この場合、表の列値は最小値と最大値の間に均一に分布しているものとみなされます。

例15-27 電力需要カートリッジの選択性ファンクションの実装

CREATE FUNCTION get_selectivity(relop VARCHAR2, value NUMBER,
                                lo NUMBER, hi NUMBER, ndv NUMBER)
  RETURN NUMBER AS
  sel NUMBER := NULL;
  ndv NUMBER;
BEGIN
  -- This function computes the selectivity (as a percentage)
  -- of a predicate
  --             col <relop> <value>
  -- where <relop> is one of: =, !=, <, <=, >, >=
  --       <value> is one of: 0, 1
  -- lo and hi are the minimum and maximum values of the column in
  -- the table. This function performs a simplistic estimation of the
  -- selectivity by assuming that the range of distinct values of
  -- the column is distributed uniformly in the range lo..hi and that
  -- each distinct value occurs nrows/(hi-lo+1) times (where nrows is
  -- the number of rows).

  IF ndv IS NULL OR ndv <= 0 THEN
    RETURN 0;
  END IF;

  -- col != <value>
  IF relop = '!=' THEN
    IF value between lo and hi THEN
      sel := 1 - 1/ndv;
    ELSE
      sel := 1;
    END IF;

  -- col = <value>
  ELSIF relop = '=' THEN
    IF value between lo and hi THEN
      sel := 1/ndv;
    ELSE
      sel := 0;
    END IF;

  -- col >= <value>
  ELSIF relop = '>=' THEN
    IF lo = hi THEN
      IF value <= lo THEN
        sel := 1;
      ELSE
        sel := 0;
      END IF;
    ELSIF value between lo and hi THEN
      sel := (hi-value)/(hi-lo) + 1/ndv;
    ELSIF value < lo THEN
      sel := 1;
    ELSE
      sel := 0;
    END IF;

  -- col < <value>
  ELSIF relop = '<' THEN
    IF lo = hi THEN
      IF value > lo THEN
        sel := 1;
      ELSE
        sel := 0;
      END IF;
    ELSIF value between lo and hi THEN
      sel := (value-lo)/(hi-lo);
    ELSIF value < lo THEN
      sel := 0;
    ELSE
      sel := 1;
    END IF;

  -- col <= <value>
  ELSIF relop = '<=' THEN
    IF lo = hi THEN
      IF value >= lo THEN
        sel := 1;
      ELSE
        sel := 0;
      END IF;
    ELSIF value between lo and hi THEN
      sel := (value-lo)/(hi-lo) + 1/ndv;
    ELSIF value < lo THEN
      sel := 0;
    ELSE
      sel := 1;
    END IF;

  -- col > <value>
  ELSIF relop = '>' THEN
    IF lo = hi THEN
      IF value < lo THEN
        sel := 1;
      ELSE
        sel := 0;
      END IF;
    ELSIF value between lo and hi THEN
      sel := (hi-value)/(hi-lo);
    ELSIF value < lo THEN
      sel := 1;
    ELSE
      sel := 0;
    END IF;

  END IF;

  RETURN least(100, ceil(100*sel));

END;
/

ODCIStatsSelectivity()ファンクションは、例15-28で示されるように、一定の開始値と終了値を持つファンクションの述語の選択性を見積ります。さらに、述語内のファンクションの最初の因数にはPowerDemand_Typ型の列を指定し、残りの引数を指定する必要があります。

例15-28 電力需要カートリッジの問合せのODCIStatsSelectivity()の登録

  STATIC FUNCTION ODCIStatsSelectivity(pred sys.ODCIPredInfo,
     sel OUT NUMBER, args sys.ODCIArgDescList, strt NUMBER, stop NUMBER,
     object PowerDemand_Typ, cell NUMBER, value NUMBER, env sys.ODCIEnv)
     RETURN NUMBER IS
     fname               varchar2(30);
     relop               varchar2(2);
     lo                  NUMBER;
     hi                  NUMBER;
     nrows               NUMBER;
     colname             VARCHAR2(30);
     statsexists         BOOLEAN := FALSE;
     stats               PowerCartUserStats%ROWTYPE;
     CURSOR c1(cell NUMBER, tname VARCHAR2, cname VARCHAR2) IS
       SELECT * FROM PowerCartUserStats
       WHERE cpos = cell
         AND tab = tname
         AND col = cname;
  BEGIN
    -- compute selectivity only when predicate is of the form:
    --      fn(col, <cell>, <value>) <relop> <val>
    -- In all other cases, return an error and let the optimizer
    -- make a guess. We also assume that the function "fn" has
    -- a return value of 0, 1, or NULL.

    -- start value
    IF (args(1).ArgType != ODCIConst.ArgLit AND
        args(1).ArgType != ODCIConst.ArgNull) THEN
      RETURN ODCIConst.Error;
    END IF;

    -- stop value
    IF (args(2).ArgType != ODCIConst.ArgLit AND
        args(2).ArgType != ODCIConst.ArgNull) THEN
      RETURN ODCIConst.Error;
    END IF;

    -- first argument of function
    IF (args(3).ArgType != ODCIConst.ArgCol) THEN
      RETURN ODCIConst.Error;
    END IF;

    -- second argument of function
    IF (args(4).ArgType != ODCIConst.ArgLit AND
        args(4).ArgType != ODCIConst.ArgNull) THEN
      RETURN ODCIConst.Error;
    END IF;

    -- third argument of function
    IF (args(5).ArgType != ODCIConst.ArgLit AND
        args(5).ArgType != ODCIConst.ArgNull) THEN
      RETURN ODCIConst.Error;
    END IF;

    colname := rtrim(ltrim(args(3).colName, '"'), '"');

述語にあるファンクションの最初の(列)引数は、収集済の統計を指定する必要があります。統計が収集されていない場合、ODCIStatsSelectivity()はエラー・ステータスを戻します。

    -- Check if the statistics table exists (we are using a
    -- user-defined table to store the user-defined statistics).
    -- Get user-defined statistics: MIN, MAX, NROWS
    FOR stats IN c1(cell, args(3).TableName, colname) LOOP
      -- Get user-defined statistics: MIN, MAX, NROWS
      lo := stats.lo;
      hi := stats.hi;
      nrows := stats.nrows;
      statsexists := TRUE;
      EXIT;
    END LOOP;

    -- If no user-defined statistics were collected, return error
    IF not statsexists THEN
      RETURN ODCIConst.Error;
    END IF;

Specificファンクションは、等価の範囲述語に対応する述語を指定します。たとえば、述語Power_EqualsSpecific_Func(col, 21, 25) = 0(セル21の測定値が25ではないことをチェックする)は、等価の範囲述語col[21] != 25に対応します。

ODCIStatsSelectivity()ファンクションは、Specificファンクションの述語ごとに、対応する範囲述語を検索します。選択性を即時に判断できる複数の境界事例があります。

    -- selectivity is 0 for "fn(col, <cell>, <value>) < 0"
    IF (stop = 0 AND
        bitand(pred.Flags, ODCIConst.PredIncludeStop) = 0) THEN
      sel := 0;
      RETURN ODCIConst.Success;
    END IF;

    -- selectivity is 0 for "fn(col, <cell>, <value>) > 1"
    IF (strt = 1 AND
        bitand(pred.Flags, ODCIConst.PredIncludeStart) = 0) THEN
      sel := 0;
      RETURN ODCIConst.Success;
    END IF;

    -- selectivity is 100% for "fn(col, <cell>, <value>) >= 0"
    IF (strt = 0 AND
        bitand(pred.Flags, ODCIConst.PredExactMatch) = 0 AND
        bitand(pred.Flags, ODCIConst.PredIncludeStart) > 0) THEN
      sel := 100;
      RETURN ODCIConst.Success;
    END IF;

    -- selectivity is 100% for "fn(col, <cell>, <value>) <= 1"
    IF (stop = 1 AND
        bitand(pred.Flags, ODCIConst.PredExactMatch) = 0 AND
        bitand(pred.Flags, ODCIConst.PredIncludeStop) > 0) THEN
      sel := 100;
      RETURN ODCIConst.Success;
    END IF;

    -- get function name
    IF bitand(pred.Flags, ODCIConst.PredObjectFunc) > 0 THEN
      fname := pred.ObjectName;
    ELSE
      fname := pred.MethodName;
    END IF;

    -- convert prefix relational operator to infix:
    -- "Power_EqualsSpecific_Func(col, <cell>, <value>) = 1"
    -- becomes "col[<cell>] = <value>"

    --   Power_EqualsSpecific_Func(col, <cell>, <value>) = 0
    --   Power_EqualsSpecific_Func(col, <cell>, <value>) <= 0
    --   Power_EqualsSpecific_Func(col, <cell>, <value>) < 1
    -- can be transformed to
    --   col[<cell>] != <value>
    IF (fname LIKE upper('Power_Equals%') AND
        (stop = 0 OR
         (stop = 1 AND
          bitand(pred.Flags, ODCIConst.PredIncludeStop) = 0))) THEN
      relop := '!=';

    --   Power_LessThanSpecific_Func(col, <cell>, <value>) = 0
    --   Power_LessThanSpecific_Func(col, <cell>, <value>) <= 0
    --   Power_LessThanSpecific_Func(col, <cell>, <value>) < 1
    -- can be transformed to
    --   col[<cell>] >= <value>
    ELSIF (fname LIKE upper('Power_LessThan%') AND
           (stop = 0 OR
            (stop = 1 AND
             bitand(pred.Flags, ODCIConst.PredIncludeStop) = 0))) THEN
      relop := '>=';

    --   Power_GreaterThanSpecific_Func(col, <cell>, <value>) = 0
    --   Power_GreaterThanSpecific_Func(col, <cell>, <value>) <= 0
    --   Power_GreaterThanSpecific_Func(col, <cell>, <value>) < 1
    -- can be transformed to
    --   col[<cell>] <= <value>
    ELSIF (fname LIKE upper('Power_GreaterThan%') AND
           (stop = 0 OR
            (stop = 1 AND
             bitand(pred.Flags, ODCIConst.PredIncludeStop) = 0))) THEN
      relop := '<=';

    --   Power_EqualsSpecific_Func(col, <cell>, <value>) = 1
    --   Power_EqualsSpecific_Func(col, <cell>, <value>) >= 1
    --   Power_EqualsSpecific_Func(col, <cell>, <value>) > 0
    -- can be transformed to
    --   col[<cell>] = <value>
    ELSIF (fname LIKE upper('Power_Equals%') AND
           (strt = 1 OR
            (strt = 0 AND
             bitand(pred.Flags, ODCIConst.PredIncludeStart) = 0))) THEN
      relop := '=';

    --   Power_LessThanSpecific_Func(col, <cell>, <value>) = 1
    --   Power_LessThanSpecific_Func(col, <cell>, <value>) >= 1
    --   Power_LessThanSpecific_Func(col, <cell>, <value>) > 0
    -- can be transformed to
    --   col[<cell>] < <value>
    ELSIF (fname LIKE upper('Power_LessThan%') AND
           (strt = 1 OR
            (strt = 0 AND
             bitand(pred.Flags, ODCIConst.PredIncludeStart) = 0))) THEN
      relop := '<';

    --   Power_GreaterThanSpecific_Func(col, <cell>, <value>) = 1
    --   Power_GreaterThanSpecific_Func(col, <cell>, <value>) >= 1
    --   Power_GreaterThanSpecific_Func(col, <cell>, <value>) > 0
    -- can be transformed to
    --   col[<cell>] > <value>
    ELSIF (fname LIKE upper('Power_GreaterThan%') AND
           (strt = 1 OR
            (strt = 0 AND
             bitand(pred.Flags, ODCIConst.PredIncludeStart) = 0))) THEN
      relop := '>';

    ELSE
      RETURN ODCIConst.Error;

    END IF;

Specificファンクションの述語が単純な範囲述語に変換された後、ODCIStatsSelectivity()get_selectivityをコールして範囲述語(つまりSpecificファンクションの述語)の選択性を計算します。成功ステータスが戻されます。

    sel := get_selectivity(relop, value, lo, hi, nrows);
    RETURN ODCIConst.Success;
  END;
15.5.2.8 ODCIStatsIndexCost()メソッド(Specific問合せの場合)

ODCIStatsIndexCost()ファンクションの最初の定義では、例15-29で示されるように、Specific問合せに対するドメイン索引のコストが見積られます。たとえば、問合せでセル(3,7)の値が25であるインスタンスをすべて要求すると、このファンクションはドメイン索引のアクセス・パスのコストを見積って、この問合せを評価します。ODCIStatsIndexCost()のこの定義は、Any問合せのODCIStatsIndexCost()メソッドに関する項に示した定義とは、セルの位置を示すcmpposパラメータを含むという点で異なります。

iaパラメータには、索引情報としてSYS.ODCIINDEXINFO型のオブジェクト・インスタンスが含まれています。selパラメータは、Specific問合せに関してODCIStatsSelectivity()ファンクションにより見積られた演算子述語の選択性です。見積られたコストはcost出力パラメータで戻されます。qiパラメータには、オプティマイザ・モードALL_ROWSおよびFIRST_ROWSのどちらが使用されているかなどの問合せとその環境に関する情報が含まれています。predパラメータには、演算子情報としてSYS.ODCIPREDINFO型のオブジェクト・インスタンスが含まれています。argsパラメータには、演算子の値引数の記述子、および開始値と終了値が含まれています。strtおよびstopパラメータは、演算子の戻り値の上限と下限の境界ポイントです。cmpposパラメータはセル位置で、cmpvalは演算子Power_XxxxxSpecific()で指定されたセルの値です。

電力需要カートリッジの場合、Specific問合せに対するドメイン索引のコストはAny問合せに対するドメイン索引のコストと同じです。そのため、Any問合せのODCIStatsIndexCost()メソッドに関する項で説明されているように、このバージョンのODCIStatsIndexCost()ファンクションは単にファンクションの第2の定義をコールします。

例15-29 電力需要カートリッジの問合せのODCISIndexCost()の登録

  STATIC FUNCTION ODCIStatsIndexCost(ia sys.ODCIIndexInfo,
     sel NUMBER, cost OUT sys.ODCICost, qi sys.ODCIQueryInfo,
     pred sys.ODCIPredInfo, args sys.ODCIArgDescList,
     strt NUMBER, stop NUMBER, cmppos NUMBER, cmpval NUMBER, env sys.ODCIEnv)
     RETURN NUMBER IS
  BEGIN
    -- This is the cost for queries on a specific cell; simply
    -- use the cost for queries on any cell.
    RETURN ODCIStatsIndexCost(ia, sel, cost, qi, pred, args,
                              strt, stop, cmpval, env);
  END;
15.5.2.9 ODCIStatsIndexCost()メソッド(Any問合せの場合)

ODCIStatsIndexCost()ファンクションの最初の定義では、例15-30で示されるように、Any問合せに対するドメイン索引のコストが見積られます。たとえば、問合せで任意のセルの値が25であるインスタンスをすべて要求すると、このファンクションはドメイン索引のアクセス・パスのコストを見積って、この問合せを評価します。ODCIStatsIndexCost()のこの定義はSpecific問合せのODCIStatsIndexCost()メソッドに関する項に示した定義とは、cmpposパラメータを含まないという点で異なります。

iaパラメータには、索引情報としてSYS.ODCIINDEXINFO型のオブジェクト・インスタンスが含まれています。selパラメータは、Any問合せに関してODCIStatsSelectivity()ファンクションにより見積られた演算子述語の選択性です。見積られたコストはcost出力パラメータで戻されます。qiパラメータには、オプティマイザ・モードALL_ROWSおよびFIRST_ROWSのどちらが使用されているかなどの問合せとその環境に関する情報が含まれています。predパラメータには、演算子情報としてSYS.ODCIPREDINFO型のオブジェクト・インスタンスが含まれています。argsパラメータには、演算子の値引数の記述子、および開始値と終了値が含まれています。strtおよびstopパラメータは、演算子の戻り値の上限と下限の境界ポイントです。cmpvalパラメータは、演算子Power_XxxxxAny()で指定されたセルの値です。

索引のコストは、演算子述語の選択性を定数の係数で乗算し、その値で索引を実装する索引構成表のブロック数を乗算した値として見積られます。

例15-30 電力需要カートリッジの問合せのODCIStatsIndexCost()の登録

  STATIC FUNCTION ODCIStatsIndexCost(ia sys.ODCIIndexInfo,
     sel NUMBER, cost OUT sys.ODCICost, qi sys.ODCIQueryInfo,
     pred sys.ODCIPredInfo, args sys.ODCIArgDescList,
     strt NUMBER, stop NUMBER, cmpval NUMBER, env sys.ODCIEnv)
     RETURN NUMBER IS
     ixtable             VARCHAR2(40);
     numblocks           NUMBER := NULL;
     get_table           user_tables%ROWTYPE;
     CURSOR c1(tab VARCHAR2) IS
       SELECT * FROM user_tables WHERE table_name = tab;
  BEGIN
    -- This is the cost for queries on any cell.

    -- To compute the cost of a domain index, multiply the
    -- number of blocks in the table implementing the index
    -- with the selectivity

    -- Return if we don't have predicate selectivity
    IF sel IS NULL THEN
      RETURN ODCIConst.Error;
    END IF;

    cost := sys.ODCICost(NULL, NULL, NULL, NULL);

    -- Get name of table implementing the domain index
    ixtable := ia.IndexName || '_pidx';

    -- Get number of blocks in domain index
    FOR get_table IN c1(upper(ixtable)) LOOP
      numblocks := get_table.blocks;
      EXIT;
    END LOOP;

    IF numblocks IS NULL THEN
      -- Exit if there are no user-defined statistics for the index
      RETURN ODCIConst.Error;
    END IF;

    cost.CPUCost := ceil(400*(sel/100)*numblocks);
    cost.IOCost := ceil(1.5*(sel/100)*numblocks);
    RETURN ODCIConst.Success;
  END;
15.5.2.10 ODCIStatsFunctionCost()メソッド

ODCIStatsFunctionCost()ファンクションは、例15-31で示されるように、Power_XxxxxSpecific_Func()ファンクションまたはPower_XxxxxAny_Func()ファンクションの評価コストを見積ります。

funcパラメータにはファンクション情報が含まれます。このパラメータはSYS.ODCIFUNCINFO型のオブジェクト・インスタンスです。見積られたコストはcost出力パラメータで戻されます。SYS.ODCIARGDESCLIST型のオブジェクト・インスタンスであるargsパラメータには、ファンクションの各引数の記述子が含まれます。ファンクションに最初の引数としてPowerDemand_Typ型のリテラルが含まれている場合、objectパラメータにはオブジェクト・コンストラクタの形式の値が含まれます。valueパラメータはファンクションPowerXxxxxSpecific_Func()またはPower_XxxxxAny_Func()で指定されるセルの値です。

ファンクションのコストは、ファンクション名に応じて単にデフォルト値として見積られます。ファンクションはディスクからデータを読み取らないため、I/Oコストは0(ゼロ)に設定されます。

例15-31 電力需要カートリッジのODCIStatsFunctionCost()の登録

  STATIC FUNCTION ODCIStatsFunctionCost(func sys.ODCIFuncInfo,
     cost OUT sys.ODCICost, args sys.ODCIArgDescList,
     object PowerDemand_Typ, value NUMBER, env sys.ODCIEnv)
     RETURN NUMBER IS
     fname               VARCHAR2(30);
  BEGIN
    cost := sys.ODCICost(NULL, NULL, NULL, NULL);

    -- Get function name
    IF  bitand(func.Flags, ODCIConst.ObjectFunc) > 0 THEN
      fname := func.ObjectName;
    ELSE
      fname := func.MethodName;
    END IF;

    IF fname LIKE upper('Power_LessThan%') THEN
      cost.CPUCost := 5000;
      cost.IOCost := 0;
      RETURN ODCIConst.Success;
    ELSIF fname LIKE upper('Power_Equals%') THEN
      cost.CPUCost := 7000;
      cost.IOCost := 0;
      RETURN ODCIConst.Success;
    ELSIF fname LIKE upper('Power_GreaterThan%') THEN
      cost.CPUCost := 5000;
      cost.IOCost := 0;
      RETURN ODCIConst.Success;
    ELSE
      RETURN ODCIConst.Error;
    END IF;
  END;

15.5.3 拡張可能オプティマイザ・メソッドとデータベース・オブジェクトの関連付け

power_statisticsオブジェクト型で定義されたメソッドをオプティマイザで使用するには、例15-32で示されるように、適切なデータベース・オブジェクトに関連付ける必要があります。

例15-32 電力需要カートリッジのデータベース・オブジェクトでの統計メソッドの使用

  Associate statistics type with types, indextypes, and functions
ASSOCIATE STATISTICS WITH TYPES PowerDemand_Typ USING power_statistics;
ASSOCIATE STATISTICS WITH INDEXTYPES power_idxtype USING power_statistics
  WITH SYSTEM MANAGED STORAGE TABLES;
ASSOCIATE STATISTICS WITH FUNCTIONS
  Power_EqualsSpecific_Func,
  Power_GreaterThanSpecific_Func,
  Power_LessThanSpecific_Func,
  Power_EqualsAny_Func,
  Power_GreaterThanAny_Func,
  Power_LessThanAny_Func
  USING power_statistics;

15.5.4 データベース・オブジェクトの分析

表、列および索引の分析により、各種アクセス・パスのコストを正確に見積って適切な計画を選択するための関連統計がオプティマイザに存在することを確認できます。また、power_statisticsオブジェクト型で定義されている選択性ファンクションとコスト・ファンクションも、統計の存在に依存します。例15-33に、データベース・オブジェクトを分析し、統計が実際に収集されたかどうかを検証する文を示します。

例15-33 電力需要カートリッジのデータベース・オブジェクトの分析

-- Analyze the table
EXECUTE dbms_stats.gather_table_stats(
    'POWERCARTUSER', 'POWERDEMAND_TAB', cascade => TRUE);

-- Verify that user-defined statistics were collected
SELECT tab tablename, col colname, cpos, lo, hi, nrows
FROM PowerCartUserStats
WHERE nrows IS NOT NULL
ORDER BY cpos;

-- Delete the statistics
EXECUTE dbms_stats.delete_table_stats('POWERCARTUSER', 'POWERDEMAND_TAB');

-- Verify that user-defined statistics were deleted
SELECT tab tablename, col colname, cpos, lo, hi, nrows
FROM PowerCartUserStats
WHERE nrows IS NOT NULL
ORDER BY cpos;

-- Re-analyze the table
EXECUTE dbms_stats.gather_table_stats(
   'POWERCARTUSER', 'POWERDEMAND_TAB',cascade => TRUE);

-- Verify that user-defined statistics were re-collected
SELECT tab tablename, col colname, cpos, lo, hi, nrows
FROM PowerCartUserStats
WHERE nrows IS NOT NULL
ORDER BY cpos;

15.6 ドメイン索引のテスト

電力需要の例のうち、ドメイン索引の簡単なテストを実行する場合について考えるとともに、ドメイン索引をテストする方法、および索引を使用したほうが索引を使用しない場合よりも問合せが効率的に実行されるかどうかを確認する方法について考えます。これらのテストは、次の操作で構成されます。

  • 電力需要表(PowerDemand_Tab)を作成し、少量のデータを移入します。

  • 索引の作成前になんらかの問合せを実行(および、索引を使用しない実行計画を表示)します。

    実行計画は、それぞれのケースで全表スキャンが実行されることを示します。

  • グリッドに索引を作成します。

  • 索引の作成後に同じ問合せを実行(および、索引を使用した実行計画を表示)します。

    実行計画は、Oracleが索引を使用しており、全表スキャンを実行していないため、実行効率が高いことを示します。

ここで説明する文は、サンプル・ファイル(tkqxpwr.sql)内でオンラインで使用可能です。

15.6.1 電力需要表の作成と移入

次の2列を使用して、例15-34で示されるように、電力需要表を作成します。

  • regionを使用することで、電気会社は、複数の地域または州でグリッド・スキームを使用できるようになります。ニューヨーク、ニュージャージー、ペンシルベニアなどの各地域が、10x10のグリッドで表されます。

  • samplePowerDemand_Typオブジェクト型を使用して定義されたサンプリング、またはグリッドの各セルからの電力需要測定値のコレクションです。

数時間のタイムスタンプについて、2つの地域(1および2)の電力需要データを表す数行が挿入されます。単純化するために、値は各グリッドの最初の5つの位置にのみ挿入され、例15-35で示されるように、残りの95個の値はNULLに設定されます。

最後に、例15-36で示されるように、TotGridDemandMaxCellDemandおよびMinCellDemandの値が計算され、新規に挿入された各行に設定され、これらの値が表示されます。

例15-34 電力需要カートリッジのPowerDemand_Tab表の作成

CREATE TABLE PowerDemand_Tab (
  -- Region for which these power demand readings apply
  region NUMBER,
  -- Values for each "sampling" time (for a given hour)
  sample PowerDemand_Typ
);

例15-35 電力需要カートリッジのPowerDemand_Tab表の移入

-- The next INSERT statements "cheats" by supplying only 5 grid values
  
-- First 5 INSERT statements are for region 1 (1 AM to 5 AM on 01-Feb-1998).
 
INSERT INTO PowerDemand_Tab VALUES(1,
   PowerDemand_Typ(NULL, NULL, NULL, PowerGrid_Typ(55,8,13,9,5),
   to_date('02-01-1998 01','MM-DD-YYYY HH'))
);
 
INSERT INTO PowerDemand_Tab VALUES(1,
   PowerDemand_Typ(NULL, NULL, NULL, PowerGrid_Typ(56,8,13,9,3),
   to_date('02-01-1998 02','MM-DD-YYYY HH'))
);
 
INSERT INTO PowerDemand_Tab VALUES(1,
   PowerDemand_Typ(NULL, NULL, NULL, PowerGrid_Typ(55,8,13,9,3),
   to_date('02-01-1998 03','MM-DD-YYYY HH'))
);
  
INSERT INTO PowerDemand_Tab VALUES(1,
   PowerDemand_Typ(NULL, NULL, NULL, PowerGrid_Typ(54,8,13,9,3),
   to_date('02-01-1998 04','MM-DD-YYYY HH'))
);
 
INSERT INTO PowerDemand_Tab VALUES(1,
   PowerDemand_Typ(NULL, NULL, NULL, PowerGrid_Typ(54,8,12,9,3),
   to_date('02-01-1998 05','MM-DD-YYYY HH'))
);
 
-- Also insert some rows for region 2.
 
INSERT INTO PowerDemand_Tab VALUES(2,
   PowerDemand_Typ(NULL, NULL, NULL, PowerGrid_Typ(9,8,11,16,5),
   to_date('02-01-1998 01','MM-DD-YYYY HH'))
);
  
INSERT INTO PowerDemand_Tab VALUES(2,
   PowerDemand_Typ(NULL, NULL, NULL, PowerGrid_Typ(9,8,11,20,5),
   to_date('02-01-1998 02','MM-DD-YYYY HH'))
);

例15-36 電力需要カートリッジのグリッドおよびセルの需要の計算

DECLARE
CURSOR c1 IS SELECT Sample, Region FROM PowerDemand_Tab FOR UPDATE;
s PowerDemand_Typ;
r NUMBER;
BEGIN
  OPEN c1;
  LOOP
     FETCH c1 INTO s,r;
     EXIT WHEN c1%NOTFOUND;
     s.SetTotalDemand;
     s.SetMaxDemand;
     s.SetMinDemand;
     dbms_output.put_line(s.TotGridDemand);
     dbms_output.put_line(s.MaxCellDemand);
     dbms_output.put_line(s.MinCellDemand);
     UPDATE PowerDemand_Tab SET Sample = s WHERE CURRENT OF c1;
  END LOOP;
  CLOSE c1;
END;
/

-- Examine the values. 
SELECT region, P.Sample.TotGridDemand, P.Sample.MaxCellDemand,
   P.Sample.MinCellDemand,
   to_char(P.sample.sampletime, 'MM-DD-YYYY HH') 
 FROM PowerDemand_Tab P;

15.6.2 索引を使用しない問合せ

ここで使用されている問合せは、表の各行に基礎となるファンクションPowerEqualsSpecific_Func()を適用することで実行されます。これは、索引が未定義であるためです。

サンプル・ファイルには、特定のセル番号、および指定した値と等しいセル番号、それより大きいセル番号、それより小さいセル番号についてチェックする問合せが含まれています。たとえば、等価の問合せについては、例15-37で説明しています。

実行計画は、それぞれのケースで全表スキャンが実行されることを示します。

OPERATIONS       OPTIONS         OBJECT_NAME    
---------------  --------------- ---------------
SELECT STATEMENT                                
TABLE ACCESS     FULL            POWERDEMAND_TAB

例15-37 電力需要カートリッジの等価性問合せの実行

SET SERVEROUTPUT ON
-------------------------------------------------------------------
-- Query, referencing the operators (without index)
-------------------------------------------------------------------
explain plan for
SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     P.Sample.MinCellDemand
   FROM PowerDemand_Tab P
   WHERE Power_Equals(P.Sample,2,10) = 1;
@tkoqxpll
 
SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     P.Sample.MinCellDemand
   FROM PowerDemand_Tab P
   WHERE Power_Equals(P.Sample,2,10) = 1;

explain plan for
SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
     P.Sample.MinCellDemand
   FROM PowerDemand_Tab P
   WHERE Power_Equals(P.Sample,1,25) = 1;
@tkoqxpll

SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
     P.Sample.MinCellDemand
   FROM PowerDemand_Tab P
   WHERE Power_Equals(P.Sample,1,25) = 1;
 
explain plan for
SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     P.Sample.MinCellDemand
   FROM PowerDemand_Tab P
   WHERE Power_Equals(P.Sample,2,8) = 1;
@tkoqxpll
 
SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     P.Sample.MinCellDemand
   FROM PowerDemand_Tab P
   WHERE Power_Equals(P.Sample,2,8) = 1;
 
explain plan for
SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     P.Sample.MinCellDemand
   FROM PowerDemand_Tab P
   WHERE Power_EqualsAny(P.Sample,9) = 1;
@tkoqxpll
 
SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     P.Sample.MinCellDemand
   FROM PowerDemand_Tab P
   WHERE Power_EqualsAny(P.Sample,9) = 1;

15.6.3 索引の作成

索引は、例15-38で示されるように、電力需要表のSample列に作成されます。

例15-38 電力需要カートリッジのPowerDemand_Tab表の索引の作成

CREATE INDEX PowerIndex ON PowerDemand_Tab(Sample) 
   INDEXTYPE IS power_idxtype;

15.6.4 索引を使用した問合せ

ここで使用されている問合せは、索引を使用しない問合せの問合せと同じですが、今回は索引が使用されています。

実行計画は、例15-39で示されるように、Oracleがドメイン索引を使用しており、全表スキャンを実行していないため、実行効率が高いことを示します。

例15-39 電力需要カートリッジの索引の等価性問合せの実行

SQLPLUS> -------------------------------------------------------------------
SQLPLUS> -- Query, referencing the operators (with index)
SQLPLUS> -------------------------------------------------------------------
SQLPLUS> explain plan for
     2> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     3>      P.Sample.MinCellDemand
     4>    FROM PowerDemand_Tab P
     5>    WHERE Power_Equals(P.Sample,2,10) = 1;
Statement processed.
SQLPLUS> @tkoqxpll
SQLPLUS> set echo off
Echo                            OFF
Charwidth                       15
OPERATIONS      OPTIONS         OBJECT_NAME    
--------------- --------------- ---------------
SELECT STATEMEN                                
TABLE ACCESS    BY ROWID        POWERDEMAND_TAB
DOMAIN INDEX                    POWERINDEX     
3 rows selected.
Statement processed.
Echo                            ON
SQLPLUS>  
SQLPLUS> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     2>      P.Sample.MinCellDemand
     3>    FROM PowerDemand_Tab P
     4>    WHERE Power_Equals(P.Sample,2,10) = 1;
REGION     SAMPLE.TOT SAMPLE.MAX SAMPLE.MIN
---------- ---------- ---------- ----------
0 rows selected.
ODCIIndexStart>>>>>
ODCIIndexInfo
Index owner : POWERCARTUSER
Index name : POWERINDEX
Table owner : POWERCARTUSER
Table name : POWERDEMAND_TAB
Indexed column : "SAMPLE"
Indexed column type :POWERDEMAND_TYP
Indexed column type schema:POWERCARTUSER
ODCIPredInfo
Object owner : POWERCARTUSER
Object name : POWER_EQUALS
Method name : 
Predicate bounds flag :
     Exact Match
     Include Start Key
     Include Stop Key
start key : 1
stop key : 1
compare position : 2
compare value : 10
ODCIIndexStart>>>>>select r from POWERCARTUSER.POWERINDEX_pidx where cpos ='2' and cval ='10'
ODCIIndexFetch>>>>>
Nrows : 2000
ODCIIndexClose>>>>>
SQLPLUS>  
SQLPLUS> explain plan for
     2> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     3>      P.Sample.MinCellDemand
     4>    FROM PowerDemand_Tab P
     5>    WHERE Power_Equals(P.Sample,2,8) = 1;
Statement processed.
SQLPLUS> @tkoqxpll
SQLPLUS> set echo off
Echo                            OFF
Charwidth                       15
OPERATIONS      OPTIONS         OBJECT_NAME    
--------------- --------------- ---------------
SELECT STATEMEN                                
TABLE ACCESS    BY ROWID        POWERDEMAND_TAB
DOMAIN INDEX                    POWERINDEX     
3 rows selected.
Statement processed.
Echo                            ON
SQLPLUS>  
SQLPLUS> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     2>      P.Sample.MinCellDemand
     3>    FROM PowerDemand_Tab P
     4>    WHERE Power_Equals(P.Sample,2,8) = 1;
REGION     SAMPLE.TOT SAMPLE.MAX SAMPLE.MIN
---------- ---------- ---------- ----------
         1         90         55          5
         1         89         56          3
         1         88         55          3
         1         87         54          3
         1         86         54          3
         2         49         16          5
         2         53         20          5
7 rows selected.
ODCIIndexStart>>>>>
ODCIIndexInfo
Index owner : POWERCARTUSER
Index name : POWERINDEX
Table owner : POWERCARTUSER
Table name : POWERDEMAND_TAB
Indexed column : "SAMPLE"
Indexed column type :POWERDEMAND_TYP
Indexed column type schema:POWERCARTUSER
ODCIPredInfo
Object owner : POWERCARTUSER
Object name : POWER_EQUALS
Method name : 
Predicate bounds flag :
     Exact Match
     Include Start Key
     Include Stop Key
start key : 1
stop key : 1
compare position : 2
compare value : 8
ODCIIndexStart>>>>>select r from POWERCARTUSER.POWERINDEX_pidx where cpos ='2' and cval ='8'
ODCIIndexFetch>>>>>
Nrows : 2000
ODCIIndexClose>>>>>
SQLPLUS>  
SQLPLUS> explain plan for
     2> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     3>      P.Sample.MinCellDemand
     4>    FROM PowerDemand_Tab P
     5>    WHERE Power_EqualsAny(P.Sample,9) = 1;
Statement processed.
SQLPLUS> @tkoqxpll
SQLPLUS> set echo off
Echo                            OFF
Charwidth                       15
OPERATIONS      OPTIONS         OBJECT_NAME    
--------------- --------------- ---------------
SELECT STATEMEN                                
TABLE ACCESS    BY ROWID        POWERDEMAND_TAB
DOMAIN INDEX                    POWERINDEX     
3 rows selected.
Statement processed.
Echo                            ON
SQLPLUS>  
SQLPLUS> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand, 
     2>      P.Sample.MinCellDemand
     3>    FROM PowerDemand_Tab P
     4>    WHERE Power_EqualsAny(P.Sample,9) = 1;
REGION     SAMPLE.TOT SAMPLE.MAX SAMPLE.MIN
---------- ---------- ---------- ----------
         1         90         55          5
         1         89         56          3
         1         88         55          3
         1         87         54          3
         1         86         54          3
         2         49         16          5
         2         53         20          5
7 rows selected.
ODCIIndexStart>>>>>
ODCIIndexInfo
Index owner : POWERCARTUSER
Index name : POWERINDEX
Table owner : POWERCARTUSER
Table name : POWERDEMAND_TAB
Indexed column : "SAMPLE"
Indexed column type :POWERDEMAND_TYP
Indexed column type schema:POWERCARTUSER
ODCIPredInfo
Object owner : POWERCARTUSER
Object name : POWER_EQUALSANY
Method name : 
Predicate bounds flag :
     Exact Match
     Include Start Key
     Include Stop Key
start key : 1
stop key : 1
compare value : 9
ODCIIndexStart>>>>>select distinct r from POWERCARTUSER.POWERINDEX_pidx where cval ='9'
ODCIIndexFetch>>>>>
Nrows : 2000
ODCIIndexClose>>>>>
SQLPLUS>
SQLPLUS> explain plan for
     2> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
     3>      P.Sample.MinCellDemand
     4>    FROM PowerDemand_Tab P
     5>    WHERE Power_GreaterThanAny(P.Sample,50) = 1;
Statement processed.
SQLPLUS> @tkoqxpll
SQLPLUS> set echo off
Echo                            OFF
Charwidth                       15
OPERATIONS      OPTIONS         OBJECT_NAME    
--------------- --------------- ---------------
SELECT STATEMEN                                
TABLE ACCESS    BY ROWID        POWERDEMAND_TAB
DOMAIN INDEX                    POWERINDEX     
3 rows selected.
Statement processed.
Echo                            ON
SQLPLUS>
SQLPLUS> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
     2>      P.Sample.MinCellDemand
     3>    FROM PowerDemand_Tab P
     4>    WHERE Power_GreaterThanAny(P.Sample,50) = 1;
REGION     SAMPLE.TOT SAMPLE.MAX SAMPLE.MIN
---------- ---------- ---------- ----------
         1         90         55          5
         1         89         56          3
         1         88         55          3
         1         87         54          3
         1         86         54          3
5 rows selected.
ODCIIndexStart>>>>>
ODCIIndexInfo
Index owner : POWERCARTUSER
Index name : POWERINDEX
Table owner : POWERCARTUSER
Table name : POWERDEMAND_TAB
Indexed column : "SAMPLE"
Indexed column type :POWERDEMAND_TYP
Indexed column type schema:POWERCARTUSER
ODCIPredInfo
Object owner : POWERCARTUSER
Object name : POWER_GREATERTHANANY
Method name :
Predicate bounds flag :
     Exact Match
     Include Start Key
     Include Stop Key
start key : 1
stop key : 1
compare value : 50
ODCIIndexStart>>>>>select distinct r from POWERCARTUSER.POWERINDEX_pidx where cv
al >'50'
ODCIIndexFetch>>>>>
Nrows : 2000
ODCIIndexClose>>>>>
SQLPLUS>
SQLPLUS> explain plan for
     2> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
     3>      P.Sample.MinCellDemand
     4>    FROM PowerDemand_Tab P
     5>    WHERE Power_LessThanAny(P.Sample,50) = 0;
Statement processed.
SQLPLUS> @tkoqxpll
SQLPLUS> set echo off
Echo                            OFF
Charwidth                       15
OPERATIONS      OPTIONS         OBJECT_NAME    
--------------- --------------- ---------------
SELECT STATEMEN                                
TABLE ACCESS    BY ROWID        POWERDEMAND_TAB
DOMAIN INDEX                    POWERINDEX     
3 rows selected.
Statement processed.
Echo                            ON
SQLPLUS>
SQLPLUS> SELECT P.Region, P.Sample.TotGridDemand ,P.Sample.MaxCellDemand,
     2>      P.Sample.MinCellDemand
     3>    FROM PowerDemand_Tab P
     4>    WHERE Power_LessThanAny(P.Sample,50) = 0;
REGION     SAMPLE.TOT SAMPLE.MAX SAMPLE.MIN
---------- ---------- ---------- ----------
0 rows selected.
ODCIIndexStart>>>>>
ODCIIndexInfo
Index owner : POWERCARTUSER
Index name : POWERINDEX
Table owner : POWERCARTUSER
Table name : POWERDEMAND_TAB
Indexed column : "SAMPLE"
Indexed column type :POWERDEMAND_TYP
Indexed column type schema:POWERCARTUSER
ODCIPredInfo
Object owner : POWERCARTUSER
Object name : POWER_LESSTHANANY
Method name :
Predicate bounds flag :
     Exact Match
     Include Start Key
     Include Stop Key
start key : 0
stop key : 0
compare value : 50
ODCIIndexStart>>>>>select distinct r from POWERCARTUSER.POWERINDEX_pidx minus se
lect distinct r from POWERCARTUSER.POWERINDEX_pidx where cval <'50'
ODCIIndexFetch>>>>>
Nrows : 2000
ODCIIndexClose>>>>>