8.6 一時型と汎用型

Oracleデータベースには、型の記述、データ・インスタンス、およびオブジェクト型やコレクション型を含むその他のSQL型のデータ・インスタンスの集合の動的なカプセル化およびアクセスを可能にする3つの汎用(つまり、汎用的にプログラムされた)SQLデータ型があります。この3つの型は、匿名のコレクション型を含めた匿名型の作成にも使用できます。

この3つのSQL型は、不透明型として実装されます。つまり、これらの型の内部構造は、データベースには認識されません。そのデータへの問合せは、目的に合ったファンクション(通常は3GLルーチン)を実装することによってのみ実行されます。Oracleデータベースは、そのようなファンクションを実装するためにOCIとPL/SQL APIの両方を提供しています。

3つの型で、ANYTYPEは一時型であり、ANYDATAおよびANYDATASETは一時型ではなく永続型です。構造がデータベースにとって不透明であるため、一時型は永続的に格納できません。一時型の列は作成できず、永続型の属性にできません。

Oracle Database 12cリリース12.2以降、次の場合にActive Data Guardで一時型を作成できます。

  1. リアルタイム適用がActive Data Guardで実行されている場合、および

  2. ロジカル・スタンバイがプライマリに(通常、秒レベルで)後れを取っていない場合。

表8-2で、この3つの汎用SQL型を説明します。

表8-2 汎用SQL型

説明

SYS.ANYTYPE

型記述型。SYS.ANYTYPEには、名前が付いているか付いていないかにかかわらず、オブジェクト型およびコレクション型を含めた、任意のSQL型の型記述を含めることができます。

ANYTYPEには、永続型の型の記述を含めることができますが、ANYTYPE自体は一時型です。つまり、ANYTYPE自体の値がデータベースに自動的に格納されることはありません。永続型を作成するには、SQLからCREATE TYPE文を使用します。

SYS.ANYDATA

自己記述的データ・インスタンスの型。SYS.ANYDATAには、与えられた型のインスタンス、データ、およびその型の記述が含まれています。SYS.ANYDATAは、この意味で自己記述的です。ANYDATAは、データベースに永続的に格納できます。

次のものはANYDATA列に格納できません。

  • XMLTYPEを除く別の不透明な型

  • LOB型(BLOB/CLOB/NCLOB)

  • 4Kより大きい最大サイズのVARRAY

  • 一時型

SYS.ANYDATASET

自己記述的なデータ集合の型。SYS.ANYDATASETには、与えられた型の記述、およびその型のデータ・インスタンスの集合が含まれています。ANYDATASETは、データベースに永続的に格納できます。

次のものはANYDATASET列に格納できません。

  • ANYDATAまたはXMLTYPEなどの別の不透明な型

  • LOB型(BLOB/CLOB/NCLOB)

  • 4Kより大きい最大サイズのVARRAY

  • 前述の型のいずれかを含むADT

  • 一時型

この3つの型はそれぞれ、データベースに対してネイティブな組込み型や、名前の有無にかかわらずオブジェクト型およびコレクション型と併用できます。型は、型記述、単独インスタンスおよび他の型のインスタンス・セットを動的に操作するための汎用的な方法を提供します。APIを使用すると、あらゆる種類の型について一時的なANYTYPE記述を作成できます。同様に、任意のSQL型のデータ値を作成またはANYDATAに変換(キャスト)することも、ANYDATAをSQL型に変換する(戻す)こともできます。さらに、値セットとANYDATASETの場合も同様です。

汎用型は、ストアド・プロシージャを使用した作業を簡単にします。汎用型を使用して、標準型の記述およびデータをカプセル化し、カプセル化された情報を汎用型のパラメータに渡せます。プロシージャ本体では、カプセル化されたデータおよび任意の型の型記述の処理方法を詳細に記述できます。

また、基礎となる様々な型のカプセル化されたデータを、型ANYDATAまたはANYDATASETの単一の表の列に格納することもできます。たとえば、ANYDATAをアドバンスト・キューイングと併用して、異質な型のデータのキューイングをモデル化できます。基礎となるデータ型のデータは、他の任意のデータ同様に、問合せを実行できます。

例8-10では、SYS.ANYDATAに組み込まれたメソッドを使用して、SYS.ANYDATA表列に格納されているデータの情報にアクセスするPL/SQLプロシージャを定義して実行します。

例8-10 SYS.ANYDATAの使用

CREATE OR REPLACE TYPE dogowner AS OBJECT ( 
    ownerno NUMBER, ownername VARCHAR2(10) );
/
CREATE OR REPLACE TYPE dog AS OBJECT ( 
    breed VARCHAR2(10), dogname VARCHAR2(10) );
/
CREATE TABLE mytab ( id NUMBER, data SYS.ANYDATA );
INSERT INTO mytab VALUES ( 1, SYS.ANYDATA.ConvertNumber (5) );
INSERT INTO mytab VALUES ( 2, SYS.ANYDATA.ConvertObject (
    dogowner ( 5555, 'John') ) );
commit;

CREATE OR REPLACE procedure P IS
  CURSOR cur IS SELECT id, data FROM mytab;

  v_id mytab.id%TYPE;
  v_data mytab.data%TYPE;
  v_type SYS.ANYTYPE;
  v_typecode PLS_INTEGER;
  v_typename VARCHAR2(60);
  v_dummy PLS_INTEGER;
  v_n NUMBER;
  v_dogowner dogowner;
  non_null_anytype_for_NUMBER exception;
  unknown_typename exception;

BEGIN
  OPEN cur;
    LOOP
      FETCH cur INTO v_id, v_data;
      EXIT WHEN cur%NOTFOUND;
      v_typecode := v_data.GetType ( v_type /* OUT */ );
      CASE v_typecode
        WHEN Dbms_Types.Typecode_NUMBER THEN
          IF v_type IS NOT NULL
            THEN RAISE non_null_anytype_for_NUMBER; END IF;
          v_dummy := v_data.GetNUMBER ( v_n /* OUT */ );
          Dbms_Output.Put_Line (
            To_Char(v_id) || ': NUMBER = ' || To_Char(v_n) );
        WHEN Dbms_Types.Typecode_Object THEN
          v_typename := v_data.GetTypeName();
          IF v_typename NOT IN ( 'HR.DOGOWNER' )
            THEN RAISE unknown_typename; END IF;
          v_dummy := v_data.GetObject ( v_dogowner /* OUT */ );
          Dbms_Output.Put_Line (
            To_Char(v_id) || ': user-defined type = ' || v_typename ||
            '(' || v_dogowner.ownerno || ', ' || v_dogowner.ownername || ' )' );
      END CASE;
    END LOOP;
    CLOSE cur;

EXCEPTION
  WHEN non_null_anytype_for_NUMBER THEN
      RAISE_Application_Error ( -20000,
        'Paradox: the return AnyType instance FROM GetType ' ||
        'should be NULL for all but user-defined types' );
  WHEN unknown_typename THEN
      RAISE_Application_Error ( -20000,
        'Unknown user-defined type ' || v_typename ||
        ' - program written to handle only HR.DOGOWNER' );
END;
/

SELECT t.data.gettypename() FROM mytab t;
SET SERVEROUTPUT ON;
EXEC P;

前述にコード例の問合せおよびプロシージャPにより、次のような出力が生成されます。

T.DATA.GETTYPENAME()
-------------------------------------------------------------
SYS.NUMBER
HR.DOGOWNER
1: NUMBER = 5
2: user-defined type = HR.DOGOWNER(5555, John )

前述の3つの汎用SQL型に対応するのが、それらをモデル化するOCI型です。各OCI型は、それぞれの型の作成およびアクセスに使用する関数のセットを備えています。