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
(バイナリ・データ)として格納されます。これらは、トランザクションに関与できません。
内部LOB
とBFILE
形式の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 TABLE
、ALTER TABLE
、CREATE 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.3 LOBロケータ
LOB
は、他の行データとともに、または行データとは別個に格納できます。格納場所に関係なく、各LOB
には実際の位置へのハンドルまたはポインタとして表示できるロケータがあります。LOB
を選択すると、LOB
値のかわりにLOB
ロケータが戻されます。例6-4では、b_lob
のLOB
ロケータを選択して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ライブラリの一部として使用可能なファイル・ディスクリプタと同様です。
関連項目:
-
DBMS_LOB
APIについては、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。
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.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_BLOB
はBLOB
型の空のロケータを戻し、EMPTY_CLOB
はCLOB
型の空のロケータを返します。これは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には、BLOB
、CLOB
、NCLOB
および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値を別の |
OCILobArrayRead() |
1回のラウンドトリップで複数のロケータのLOBデータを読み取ります。 |
OCILobArrayWrite() |
1回のラウンドトリップで複数のロケータのLOBデータを書き込みます。 |
OCILobAssign() |
LOBロケータを別のLOBロケータに割り当てます。 |
OCILobCharSetForm() |
|
OCILobCharSetId() |
|
OCILobClose() |
オープンしているLOBまたは |
OCILobCopy2() |
LOB全体またはその一部を別のLOBにコピーします。非推奨メソッド |
OCILobCreateTemporary() |
一時LOBを作成します。 |
OCILobErase2() |
LOB全体またはその一部(指定されたオフセット以降)を消去します。非推奨メソッド |
OCILobFileClose() |
オープンしている |
OCILobFileCloseAll() |
オープン |
OCILobFileExists() |
|
OCILobFileGetName() |
|
OCILobFileIsOpen() |
|
OCILobFileOpen() |
|
OCILobFileSetName() |
|
OCILobFreeTemporary() |
一時LOBを解放します。 |
OCILobGetChunkSize() |
LOBのチャンク・サイズを取得します。 |
OCILobGetContentType() |
SecureFile内のデータのユーザー指定コンテンツ・タイプ文字列を取得します(設定されている場合)。 |
OCILobGetLength2() |
LOBの長さを取得します。非推奨メソッド |
OCILobGetOptions() |
指定されたSecureFile LOBについて指定された入力オプション・タイプに対応する、有効化された設定を取得します。 |
OCILobGetStorageLimit() |
LOB ( |
OCILobIsEqual() |
2つのLOBロケータが同じ |
OCILobIsOpen() |
LOBまたは |
OCILobIsTemporary() |
ロケータが一時LOBを指し示しているかどうかを判断します。 |
OCILobLoadFromFile2() |
|
OCILobLocatorAssign() |
LOBまたは |
OCILobLocatorIsInit() |
LOBロケータが初期化されているかどうかをテストして確認します。 |
OCILobOpen() |
LOBを指定されたモードでオープンします。 |
OCILobRead2() |
NULL以外のLOBまたは |
OCILobSetContentType() |
|
OCILobSetOptions() |
|
OCILobTrim2() |
LOBを切り捨てます。非推奨メソッド |
OCILobWrite2() |
バッファのデータをLOBに書き込み、既存データを上書きします。非推奨メソッド |
OCILobWriteAppend2() |
LOBの現行の終了点以降のデータを書き込みます。非推奨メソッド |
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() |
該当なし( |
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() |
ソース |
COPY() |
ソース |
ERASE() |
|
LOADBLOBFROMFILE() |
|
LOADCLOBFROMFILE() |
|
TRIM() |
|
WRITE() |
指定されたオフセットから |
GETLENGTH |
|
INSTR() |
|
READ() |
指定されたオフセット以降の |
SUBSTR() |
指定されたオフセット以降の |
FILECLOSE() |
ファイルをクローズします。 |
FILECLOSEALL() |
前にオープンされたファイルをすべてクローズします。 |
FILEEXISTS() |
サーバー上でファイルの有無をテストして確認します。 |
FILEGETNAME() |
ディレクトリ別名とファイル名を取得します。 |
FILEISOPEN() |
ファイルが入力の |
FILEOPEN() |
ファイルをオープンします。 |
6.7 外部プロシージャ内のLOB
LOBロケータは、例6-1で定義されているように、外部プロシージャに引数として渡すことができます。
対応するCのファンクションは、OCILobLocator
*型の引数を取得します。表6-10に定義されたファンクションは、コール時に、シグネチャint c_findmin(OCILobLocator*)
を使用して、cルーチン(c_findmin()
)を起動します。
このルーチン(c_findmin
)は、DS_Lib
に関連した共有ライブラリにあります。ポインタOCILobLocator*
を使用して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 */