6 構造化されていないデータ型の使用

Oracle Databaseにネイティブではない一部のデータ型は、Oracle Databaseでラージ・オブジェクト(LOB)として表すことができます。これらは、データ・カートリッジ用にPL/SQLおよびOCIで実装できます。

6.1 カートリッジおよび構造化されていないデータ型の概要

一部のデータ・カートリッジでは、グラフィック・イメージやサウンド波形などのoMultif RAWバイナリ・データ、またはテキストや数値ストリームなどの文字データを大量に処理する必要があります。Oracleは、この種のデータを処理するためのラージ・オブジェクト(LOB)をサポートしています。

  • 内部LOBは、領域を最適化して効率的なアクセスを提供するようにデータベース表領域に格納されます。内部LOBはサーバーのトランザクション・モデルに関与します。

    内部LOBは、バイナリ・データ(BLOB)、シングルバイト文字データ(CLOB)、固定幅のシングルバイトまたはマルチバイト文字データ(NCLOB)を格納できます。NCLOBは、Oracleデータベースに対して定義済の各国語キャラクタ・セットに対応する文字データで構成されます。可変幅の文字データは、Oracleではサポートされていません。

  • 外部LOBは、データベース表領域外部のオペレーティング・システム・ファイルにBFILE(バイナリ・データ)として格納されます。これらは、トランザクションに関与できません。

内部LOBBFILE形式のLOBはともに、大量データの処理に大幅な柔軟性をもたらします。

LOBに格納されているデータは、LOBと呼ばれます。Oracleサーバーにとって、LOB値は構造化されておらず、問合せできません。LOB値を解凍し、カートリッジ固有の方法で解析する必要があります。

LOBは、Oracle Call Interface(OCI)またはPL/SQLのDBMS_LOBパッケージを使用して操作できます。LOBの各部を操作するファンクション(LOBを格納できるオブジェクト型に対するメソッドなど)を記述できます。

関連項目:

LOBの詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。

6.2 LOBでのDDLの使用

LOB定義には、CREATE TYPEおよびCREATE TABLE文を使用できます。例6-1では、lob_typeというデータ型の中でCLOBが指定されています。

例6-2では、各行がlob_tableデータのインスタンスであるオブジェクト表lob_tableを作成しています。

例6-3では、LOB例6-2のようにオブジェクト表に格納するのではなく、標準の表に格納します。

表にLOBを作成するときに、LOBの格納、バッファリングおよびキャッシュ・プロパティを設定できます。

関連項目:

CREATE TABLEALTER TABLECREATE TYPEおよびALTER TYPEの各文でのLOBの使用の詳細は、『Oracle Database SQL言語リファレンス』マニュアルおよび『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。

6.2.1 型のLOB属性の作成

例6-1 型のCLOB属性の作成

CREATE OR REPLACE TYPE lob_type AS OBJECT ( 
  id  INTEGER, 
  data CLOB );

6.2.2 LOBオブジェクト表の作成

例6-2 LOBオブジェクト表の作成

CREATE TABLE lob_table OF lob_type;

6.2.3 LOB列の作成

例6-3 表のLOB列の作成

CREATE TABLE lob_table1  (
  id  INTEGER,
  b_lob   BLOB,
  c_lob   CLOB,
  nc_lob  NCLOB,
  b_file  BFILE );

6.3 LOBロケータ

LOBは、他の行データとともに、または行データとは別個に格納できます。格納場所に関係なく、各LOBには実際の位置へのハンドルまたはポインタとして表示できるロケータがあります。LOBを選択すると、LOB値のかわりにLOBロケータが戻されます。例6-4では、b_lobLOBロケータを選択してPL/SQLローカル変数image1に入れます。

APIファンクションを使用してLOB値を操作する場合は、ロケータを使用してLOBを参照します。例6-5のように、PL/SQLのDBMS_LOBパッケージには、PUT_LINE()GETLENGTH()など、LOBの操作に役立つルーチンが含まれています。

OCIでは、LOBロケータはOCILobLocator *などのLOBLocatorPointersにマップされます。

BFILEの場合、LOB列には独自の個別ロケータがあり、サーバーのファイル・システムにある外部ファイルに格納されているLOB値を参照します。これは、BFILE列を含む表の2行で同じファイルまたは2つの個別ファイルを参照できることを意味します。PL/SQLまたはOCIプログラムにおけるBFILEロケータ変数の動作は、他の自動変数と同様です。ファイル操作の場合の動作は、ほとんどの従来型プログラム言語の標準I/Oライブラリの一部として使用可能なファイル・ディスクリプタと同様です。

6.3.1 LOBの選択およびローカル変数への割当て

例6-4 LOBロケータの選択およびローカル変数への割当て

DECLARE
       image1  BLOB;
       image_no  INTEGER := 101;
BEGIN
       SELECT b_lob  INTO image1 FROM lob_table
                  WHERE key_value = image_no;
             ...
END;

6.3.2 LOBの操作

例6-5 PUT_LINE()およびGETLENGTH()でのLOBの操作

BEGIN
     DBMS_OUTPUT.PUT_LINE('Size of the Image is: ', 
                       DBMS_LOB.GETLENGTH(image1));
END;

6.4 LOBを空にする

特殊ファンクションEMPTY_BLOBおよびEMPTY_CLOBをSQL DMLのINSERTまたはUPDATE文に使用すると、NULLまたはNULL以外の内部LOBを空に初期化できます。これらは、Oracle SQL DMLで特殊ファンクションとして使用可能であり、DBMS_LOBパッケージには含まれていません。

OCIまたはDBMS_LOBパッケージを使用して内部LOBにデータを書込む前に、LOB列をNULL以外の値に設定しておく必要があります。この列には、空または移入済のLOB値を指すロケータが含まれている必要があります。INSERT文のVALUES句でファンクションEMPTY_BLOBを使用すると、BLOB列の値を空に初期化できます。同様に、CLOB列またはNCLOB列の値を初期化する場合も、EMPTY_CLOB()ファンクションを使用します。これらのファンクションの構文は次のとおりです。

FUNCTION EMPTY_BLOB() RETURN BLOB;
FUNCTION EMPTY_CLOB() RETURN CLOB;

EMPTY_BLOBBLOB型の空のロケータを戻し、EMPTY_CLOBCLOB型の空のロケータを返します。これはNCLOBにも使用できます。これらのファンクションにはプラグマが関連付けられていません。

例外が発生するのは、これらのファンクションをSQL INSERT文のVALUES句のどこかで使用するか、SQL UPDATE文のSET句のソースとして使用した場合です。

例6-6は、SQL DMLで使用されているEMPTY_BLOB()を示しています。

例6-7では、PL/SQLプログラムでのEMPTY_CLOB()の使用方法を示します。

6.4.1 SQLでのEMPTY_BLOB()の使用

例6-6 SQL DMLでのEMPTY_BLOB()の使用

INSERT INTO lob_table VALUES (1001, EMPTY_BLOB(), 'abcde', NULL);
UPDATE lob_table SET c_lob = EMPTY_CLOB() WHERE key_value = 1001;
INSERT INTO lob_table VALUES (1002, NULL, NULL, NULL);

6.4.2 PL/SQLでのEMPTY_CLOB()の使用

例6-7 PL/SQLプログラムでのEMPTY_CLOB()の使用

DECLARE 
  lobb         CLOB; 
  read_offset  INTEGER; 
  read_amount  INTEGER; 
  rawbuf       RAW(20); 
  charbuf      VARCHAR2(20);
BEGIN
  read_amount := 10; read_offset := 1;
  UPDATE lob_table SET c_lob = EMPTY_CLOB() 
  WHERE key_value = 1002 RETURNING c_lob INTO lobb;
  dbms_lob.read(lobb, read_amount, read_offset, charbuf); 
  dbms_output.put_line('lobb value: ' || charbuf);
END

6.5 OCIによるLOBの操作

OCIには、BLOBCLOBNCLOBおよびBFILEに格納されたデータへのアクセスに使用できるファンクションが組み込まれています。これらのファンクションは表6-1で説明しています。

表6-2に、LOBアクセスに関するOCIおよびPL/SQL(DBMS_LOBパッケージ)のインタフェースの比較を示します。

例6-8では、データベースからロケータへのLOBの選択方法を示します。lob_type型には、INTEGER型のidおよびCLOB型のdataという2つの属性があり、lob_type型のlob_table表が存在していると仮定します。

サンプル・プログラムpopulate.cでは、OCIを使用してCLOBにファイルの内容が移入されます。

関連項目:

詳細な資料(パラメータ、パラメータの型、戻り値、サンプル・コードなど)は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

6.5.1 LOBを操作するためのOCIファンクション

表6-1 OCIのLOB操作ファンクションの概要

ファンクション 説明
OCILobAppend()

LOB値を別のLOBに追加します。

OCILobArrayRead()

1回のラウンドトリップで複数のロケータのLOBデータを読み取ります。

OCILobArrayWrite()

1回のラウンドトリップで複数のロケータのLOBデータを書き込みます。

OCILobAssign()

LOBロケータを別のLOBロケータに割り当てます。

OCILobCharSetForm()

LOBのキャラクタ・セット形式を戻します。

OCILobCharSetId()

LOBのキャラクタ・セットIDを戻します。

OCILobClose()

オープンしているLOBまたはBFILEをクローズします。

OCILobCopy2()

LOB全体またはその一部を別のLOBにコピーします。非推奨メソッドOCILobCopy()のかわりに導入されています。

OCILobCreateTemporary()

一時LOBを作成します。

OCILobErase2()

LOB全体またはその一部(指定されたオフセット以降)を消去します。非推奨メソッドOCILobErase()のかわりに導入されています。

OCILobFileClose()

オープンしているBFILEをクローズします。

OCILobFileCloseAll()

オープンBFILEをすべてクローズします。

OCILobFileExists()

BFILEの有無を判断します。

OCILobFileGetName()

BFILEの名前を戻します。

OCILobFileIsOpen()

BFILEがオープンしているかどうかを判断します。

OCILobFileOpen()

BFILEをオープンします。

OCILobFileSetName()

BFILEの名前をロケータに設定します。

OCILobFreeTemporary()

一時LOBを解放します。

OCILobGetChunkSize()

LOBのチャンク・サイズを取得します。

OCILobGetContentType()

SecureFile内のデータのユーザー指定コンテンツ・タイプ文字列を取得します(設定されている場合)。

OCILobGetLength2()

LOBの長さを取得します。非推奨メソッドOCILobGetLength()のかわりに導入されています。

OCILobGetOptions()

指定されたSecureFile LOBについて指定された入力オプション・タイプに対応する、有効化された設定を取得します。

OCILobGetStorageLimit()

LOB (BLOBCLOBまたはNCLOB)の最大長(バイト単位)を取得します。

OCILobIsEqual()

2つのLOBロケータが同じLOBを参照しているかどうかを判断します。

OCILobIsOpen()

LOBまたはBFILEがオープンしているかどうかを判断します。

OCILobIsTemporary()

ロケータが一時LOBを指し示しているかどうかを判断します。

OCILobLoadFromFile2()

BFILEデータを内部LOBにロードします。非推奨メソッドOCILobLoadFromFile()のかわりに導入されています。

OCILobLocatorAssign()

LOBまたはBFILEロケータを別のロケータに割り当てます。

OCILobLocatorIsInit()

LOBロケータが初期化されているかどうかをテストして確認します。

OCILobOpen()

LOBを指定されたモードでオープンします。

OCILobRead2()

NULL以外のLOBまたはBFILEの指定された部分をバッファに読み取ります。非推奨メソッドOCILobRead()のかわりに導入されています。

OCILobSetContentType()

SecureFile LOB内のデータのコンテンツ・タイプ文字列値を設定します。

OCILobSetOptions()

SecureFile LOBのオプション設定を有効化します。

OCILobTrim2()

LOBを切り捨てます。非推奨メソッドOCILobTrim()のかわりに導入されています。

OCILobWrite2()

バッファのデータをLOBに書き込み、既存データを上書きします。非推奨メソッドOCILobWrite()のかわりに導入されています。

OCILobWriteAppend2()

LOBの現行の終了点以降のデータを書き込みます。非推奨メソッドOCILobWriteAppend()のかわりに導入されています。

6.5.2 OCIおよびPL/SQLインタフェースの比較

表6-2 OCIおよびPL/SQL(DBMS_LOB)インタフェースの比較

OCI (ociap.h) PL/SQL DBMS_LOB(dbmslob.sql)

該当なし

DBMS_LOB.COMPARE() 

該当なし

DBMS_LOB.INSTR() 

該当なし

DBMS_LOB.SUBSTR() 
OCILobAppend()
DBMS_LOB.APPEND() 
OCILobAssign()

該当なし[PL/SQL代入演算子を使用]

OCILobCharSetForm()

該当なし

OCILobCharSetId()

該当なし

OCILobCopy()
DBMS_LOB.COPY() 
OCILobErase()
DBMS_LOB.ERASE()
OCILobFileClose()
DBMS_LOB.FILECLOSE()
OCILobFileCloseAll()
DBMS_LOB.FILECLOSEALL()
OCILobFileExists()
DBMS_LOB.FILEEXISTS()
OCILobFileGetName()
DBMS_LOB.FILEGETNAME()
OCILobFileIsOpen()
DBMS_LOB.FILEISOPEN()
OCILobFileOpen()
DBMS_LOB.FILEOPEN()
OCILobFileSetName()

該当なし(BFILENAME演算子を使用)

OCILobGetLength()
DBMS_LOB.GETLENGTH()
OCILobIsEqual()

該当なし

OCILobLocatorIsInit()

該当なし[常に初期化]

OCILobRead()
DBMS_LOB.READ() 
OCILobTrim()
DBMS_LOB.TRIM() 
OCILobWrite() 
DBMS_LOB.WRITE() 

6.5.3 格納されたLOBのロケータへの選択

例6-8 データベースからロケータへのLOBの選択

/*-----------------------------------------------------------------------*/ 
/* Select lob locators from a CLOB column                                */ 
/* Use the 'FOR UPDATE' clause for writing to the LOBs.                  */ 
/*-----------------------------------------------------------------------*/ 
static OCIEnv        *envhp;
static OCIServer     *srvhp;
static OCISvcCtx     *svchp;
static OCIError      *errhp;
static OCISession    *authp;
static OCIStmt       *stmthp;
static OCIDefine     *defnp1;
static OCIBind       *bndhp;
 
sb4 select_locator(int rowind) 
{ 
  sword retval; 
  boolean flag; 
  int colc = rowind; 
  OCILobLocator *clob;
  text  *sqlstmt = (text *)"SELECT DATA FROM LOB_TABLE WHERE ID = :1 FOR UPDATE"; 

  if (OCIStmtPrepare(stmthp, errhp, sqlstmt, (ub4) strlen((char *)sqlstmt), 
      (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT)) 
  { 
    (void) printf("FAILED: OCIStmtPrepare() sqlstmt\n"); 
    return OCI_ERROR; 
  } 

  if (OCIStmtBindByPos(stmthp, bndhp, errhp, (ub4) 1, (dvoid *) &colc, 
      (sb4) sizeof(colc), SQLT_INT, (dvoid *) 0, (ub2 *)0, (ub2 *)0, (ub4) 0, 
      (ub4 *) 0, (ub4) OCI_DEFAULT)) 
  {
    (void) printf("FAILED: OCIStmtBindByPos()\n"); 
    return OCI_ERROR; 
  }
  
  if (OCIDefineByPos(stmthp, &defnp1, errhp, (ub4) 1, (dvoid *) &clob, (sb4) -1,
      (ub2) SQLT_CLOB, (dvoid *) 0, (ub2 *) 0, (ub2 *) 0, (ub4) OCI_DEFAULT)) 
  { 
    (void) printf("FAILED: OCIDefineByPos()\n"); 
    return OCI_ERROR; 
  } 

  /* Execute the select and fetch one row */ 
  if (OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0, 
      (CONST OCISnapshot*) 0, (OCISnapshot*) 0, (ub4) OCI_DEFAULT)) 
  { 
    (void) printf("FAILED: OCIStmtExecute() sqlstmt\n"); 
    report_error(); 
    return OCI_ERROR; 
  } 
 
  /* Now test to see if the LOB locator is initialized */ 
  retval = OCILobLocatorIsInit(envhp, errhp, clob, &flag); 
  if ((retval != OCI_SUCCESS) && (retval != OCI_SUCCESS_WITH_INFO)) 
  { 
    (void) printf("Select_Locator --ERROR: OCILobLocatorIsInit(), 
        retval = %d\n", retval); 
    report_error(); 
    checkerr(errhp, retval); 
    return OCI_ERROR; 
  } 

  if (!flag) 
  { 
    (void) printf("Select_Locator --ERROR: LOB Locator is not initialized.\n"); 
    return OCI_ERROR;  
  } 

  return OCI_SUCCESS; 
}

6.6 DBMS_LOBパッケージを使用したLOBの操作

DBMS_LOBパッケージを使用すると、PL/SQLからLOBを操作できます。表6-3でこのルーチンを説明します。

例6-9では、TRIMプロシージャをコールしてCLOB値を短い長さにトリミングします。lob_type型には、INTEGER型のidおよびCLOB型のdataという2つの属性があり、lob_type型のlob_table表が存在していると仮定します。この例ではCLOBデータを処理するため、DBMS_LOB.TRIMの第2引数(834004)では文字数を指定します。この例でBLOBデータを処理する場合、この引数はバイト数として解析されます。

関連項目:

DBMS_LOBパッケージのルーチンの使用に関する詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

6.6.1 DBMS_LOBパッケージのルーチン

表6-3 DBMS_LOBパッケージ・ルーチンの概要

ルーチン 説明
APPEND()

ソースLOBのコンテンツを宛先LOBに追加します。

COPY()

ソースLOBの全体または一部を宛先LOBにコピーします。

ERASE()

LOB全体または一部を消去します。

LOADBLOBFROMFILE()

BFILEデータを内部BLOBにロードします。

LOADCLOBFROMFILE()

BFILEデータを内部CLOBにロードします。

TRIM()

LOB値を指定された長さに切り捨てます。

WRITE()

指定されたオフセットからLOBにデータを書き込みます。

GETLENGTH

LOB値の長さを取得します。

INSTR()

LOBにあるパターンのn番目の一致のマッチング位置を戻します。

READ()

指定されたオフセット以降のLOBデータを読み込みます。

SUBSTR()

指定されたオフセット以降のLOB値の一部を戻します。

FILECLOSE()

ファイルをクローズします。

FILECLOSEALL()

前にオープンされたファイルをすべてクローズします。

FILEEXISTS()

サーバー上でファイルの有無をテストして確認します。

FILEGETNAME()

ディレクトリ別名とファイル名を取得します。

FILEISOPEN()

ファイルが入力のBFILEロケータを使用してオープンされたかどうかをテストして確認します。

FILEOPEN()

ファイルをオープンします。

6.6.2 CLOBのトリミング

例6-9 CLOBのトリミング

PROCEDURE Trim_Clob IS
        clob_loc  CLOB;
BEGIN
 -- get the LOB Locator
       SELECT data into clob_loc  FROM lob_table
       WHERE id  =  179 FOR UPDATE;
   -- call the TRIM Routine
       DBMS_LOB.TRIM(clob_loc, 834004);
       COMMIT;
END;

6.7 外部プロシージャ内のLOB

LOBロケータは、例6-1で定義されているように、外部プロシージャに引数として渡すことができます。

対応するCのファンクションは、OCILobLocator *型の引数を取得します。表6-10に定義されたファンクションは、コール時に、シグネチャint c_findmin(OCILobLocator*)を使用して、cルーチン(c_findmin())を起動します。

このルーチン(c_findmin)は、DS_Libに関連した共有ライブラリにあります。ポインタOCILobLocator*を使用してLOBからデータを取得するには、コールバックを実行してデータベースに再接続する必要があります。

6.7.1 外部プロシージャ(PL/SQL)の定義

例6-10 PL/SQL外部プロシージャの定義

FUNCTION DS_Findmin(data CLOB) RETURN PLS_INTEGER IS EXTERNAL 
                   NAME "c_findmin" LIBRARY DS_Lib LANGUAGE C;

6.8 LOBとトリガー

種類に関係なくトリガー内のLOB(:oldまたは:new値)には書き込めません。

標準的なトリガーの場合、:old値は読み取れますが、:new値は読み取れません。INSTEAD OFトリガーの場合は、:old値と:new値を読み取ることができます。

トリガーが定義されている基礎となる表を更新しなくてもBFILE型を更新できるため、OF句にはLOB型の列を指定できません。

OCIファンクションまたはDBMS_LOBパッケージを使用してオブジェクト列のLOB値またはLOB属性を更新する場合、その列または属性を含む表に定義されているトリガーは起動されません。

6.9 パフォーマンス向上のためのカッコとしてのOpen/Closeの使用

Open/Closeファンクションでは、一連のLOB操作の開始点と終了点を指定できるため、Closeファンクションのコール時に、索引の更新などの大規模な操作を実行できます。つまり、Openがコールされると、LOBが変更されるたびに索引が更新されることはなくなり、このような更新はCloseコールまで再開されません。

すべてのLOB操作をOpen/Close操作でラップする必要はありませんが、コード・ブロックは次の理由から非常に役立ちます。

  • LOB操作をOpen/Closeコールでラップしなければ、LOBが変更されるたびに暗黙的にLOBがオープンおよびクローズされるため、すべてのトリガーが起動されます。LOB操作をOpen...Close操作のペアでラップすると、LOBが変更されるたびにトリガーが起動されることはありません。かわりに、Closeがコールされるときにトリガーが1つ起動されます。同様に、拡張可能索引は、Closeをコールするまで更新されません。これは、LOBの拡張可能索引がOpen...Closeコールの間は無効であることを意味します。

  • Open操作とClose操作の間は、LOBに対する変更を反映する状態が保存されないため、このテクノロジは慎重に適用する必要があります。Openを呼び出すと、LOB値の変更部分や、変更によって生じたLOBの古い値と新しい値のOracleによる追跡が中止されます。ただし、LOB値は、OCILob*操作またはDBMS_LOB操作のたびに直接更新され、通常の読取り一貫性メカニズムは引き続き機能します。LOBの拡張可能索引もLOBが変更されるたびに更新されるようにしておくと、拡張可能LOB索引が常に有効になり、いつでも使用できるようになります。

  • APIにより、LOBがオープンされているかどうかを判別できます。オープンは、常にロケータではなくLOBに関連付けられています。ロケータでは状態の情報は保存されません。

6.9.1 Open/Close操作に関するエラーと制限

オープンしていたLOBをすべてクローズする前に、トランザクションをCOMMITするとエラーになります。トランザクションのロールバック時に、まだオープン状態であるすべてのLOBは破棄されます。これらのLOBはクローズされていないため、トリガーが起動されます。

異なるロケータまたは同じロケータを持つ同じLOBに対して2回Open/Closeを実行するとエラーになります。オープンされていないLOBをクローズするとエラーになります。

例6-11では、loc1がオープンされているLOBを参照し、loc2に割り当てられているとします。以降にLOB値を変更するためにloc2が使用されると、変更はloc1による変更とともにグループ化されます。つまり、LOBマネージャの状態のエントリは1つのみであり、ロケータごとに1つではありません。LOBが(loc1またはloc2を介して)クローズされると、トリガーが起動され、いずれかのロケータを介したLOBの更新がすべてコミットされます。LOBのクローズ後、ユーザーがいずれかのロケータを使用してLOBを変更しようとすると、暗黙的なOpen()およびClose()Open() ... operation ... Close()として実行されます。読取り一貫性は引き続きロケータごとに維持されることに注意してください。単にロケータではなくLOBがオープンされクローズされることを示しています。作成されるロケータのコピー数に関係なく、LOBのトリガーは最初のCloseコール時に一度のみ起動されます。

6.9.1.1 Open()およびClose()コード・ブロックの操作

例6-11 Open()およびClose()コード・ブロックの使用

open (loc1);
loc2 := loc1;
write (loc1);
write (loc2); 
open (loc2);  /* error because the LOB is open */
close (loc1); /* triggers are fired and all LOB updates made before this 
                 statement by any locator are incorporated in the extensible
                 index */
write (loc2); /* implicit open, write, implicit close */