この章では、ラージ・オブジェクトとしてのOracle Databaseに示されているマルチメディア・データ型の動作方法を説明します。LOB型の簡潔な理論的概論を説明し、データ・カートリッジのPL/SQおよびOCI実装による実用的な使用の説明に焦点を絞っています。
この章の内容は、次のとおりです。
一部のデータ・カートリッジでは、グラフィック・イメージやサウンド波形などのRAWバイナリ・データ、またはテキストや数値ストリームなどの文字データを大量に処理する必要があります。Oracleは、この種のデータを処理するためのラージ・オブジェクト(LOB
)をサポートしています。
内部LOBは、領域を最適化して効率的なアクセスを提供するようにデータベース表領域に格納されます。内部LOB
はサーバーのトランザクション・モデルに関与します。
内部LOB
は、バイナリ・データ(BLOB
)、シングルバイト文字データ(CLOB
)、固定幅のシングルバイトまたはマルチバイト文字データ(NCLOB
)を格納できます。NCLOB
は、Oracleデータベースに対して定義済の各国語キャラクタ・セットに対応する文字データで構成されます。可変幅の文字データは、Oracleではサポートされていません。
外部LOB
は、データベース表領域外部のオペレーティング・システム・ファイルにBFILE
(バイナリ・データ)として格納されます。これらは、トランザクションに関与できません。
LOB
内部とBFILE
はともに、大量データの処理に大幅な柔軟性を提供します。
LOB
に格納されているデータは、LOB
値と呼ばれます。Oracleサーバーにとって、LOB
値は構造化されておらず、問合せできません。LOB
値を解凍し、カートリッジ固有の方法で解析する必要があります。
LOB
は、Oracle Call Interface(OCI)またはPL/SQLのDBMS_LOB
パッケージを使用して操作できます。LOB
の各部を操作するファンクション(LOB
を格納できるオブジェクト型に対するメソッドなど)を記述できます。
関連項目 LOB の詳細は、『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。 |
LOB定義には、CREATE
TYPE
およびCREATE
TABLE
文を使用できます。例6-1では、データ型lob_type
内のCLOB
が指定されます。
例6-2では、各行がlob_table
データのインスタンスであるオブジェクト表lob_table
を作成します。
例6-3では、LOB
を例6-2のようにオブジェクト表に格納するのではなく、標準の表に格納します。
例6-3 表のLOBオブジェクト列の作成方法
CREATE TABLE lob_table1 ( id INTEGER, b_lob BLOB, c_lob CLOB, nc_lob NCLOB, b_file BFILE );
表にLOBを作成するときに、LOBの格納、バッファリングおよびキャッシュ・プロパティを設定できます。
関連項目 CREATE TABLE 、ALTER TABLE 、CREATE TYPE およびALTER TYPE 文にLOBを使用する方法の詳細は、『Oracle Database SQL言語リファレンス』および『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』を参照してください。 |
LOB
は、他の行データとともに、または行データとは別個に格納できます。格納場所に関係なく、各LOB
には実際の位置へのハンドルまたはポインタとして表示できるロケータがあります。LOB
を選択すると、LOB
値のかわりにLOB
ロケータが戻されます。
例6-4では、b_lob
のLOB
ロケータを選択してPL/SQLローカル変数image1
に入れます。
例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;
APIファンクションを使用してLOB
値を操作する場合は、ロケータを使用してLOB
を参照します。例6-5のように、PL/SQLのDBMS_LOB
パッケージには、PUT_LINE
やGETLENGTH
など、LOB
の操作に役立つルーチンが含まれています。
例6-5 PUT_LINE and GETLENGTHでのLOBの操作方法
BEGIN DBMS_OUTPUT.PUT_LINE('Size of the Image is: ', DBMS_LOB.GETLENGTH(image1)); END;
OCIでは、LOB
ロケータはOCILobLocator *
などのLOBLocatorPointers
にマップされます。
この章では、OCIのLOB
インタフェースとPL/SQLのDBMS_LOB
パッケージの概要について説明します。
BFILE
の場合、LOB
列には独自の個別ロケータがあり、サーバーのファイル・システムにある外部ファイルに格納されているLOB
値を参照します。これは、BFILE
列を含む表の2行で同じファイルまたは2つの個別ファイルを参照できることを意味します。PL/SQLまたはOCIプログラムにおけるBFILE
ロケータ変数の動作は、他の自動変数と同様です。ファイル操作の場合の動作は、ほとんどの従来型プログラム言語の標準I/Oライブラリの一部として使用可能なファイル・ディスクリプタと同様です。
関連項目
|
特殊ファンクションEMPTY_BLOB
およびEMPTY_CLOBをSQL DMLのINSERT
またはUPDATE
文に使用すると、NULL
またはNULL
以外の内部LOB
を空に初期化できます。これらは、Oracle SQL DMLで特殊ファンクションとして使用可能であり、DBMS_LOB
パッケージには含まれていません。
OCIまたはDBMS_LOB
パッケージを使用して内部LOB
に対するデータの書込みを開始する前に、LOB
列を非NULLにする必要があります。つまり、空または移入済のLOB
値を指すロケータを含む必要があります。BLOB
列の値を空に初期化するには、INSERT
文のVALUES
句にファンクションEMPTY_BLOB
を使用します。同様に、CLOB
またはNCLOB
列の値は、ファンクションEMPTY_CLOB
を使用して初期化できます。ファンクションの構文は次に示します。
例6-6 EMPTY_CLOB()の構文および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-7では、SQL DMLを使用したEMPTY_BLOB
を示します。
例6-7 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-8では、PL/SQLプログラムでのEMPTY_CLOB()
の使用方法を示します。
例6-8 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
OCIには、BLOB
、CLOB
、NCLOB
およびBFILE
に格納されたデータへのアクセスに使用できるファンクションが組み込まれています。これらのファンクションは表6-1で説明しています。
関連項目 パラメータ、パラメータの型、戻り値およびコード例を含むドキュメントの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。 |
表6-1 OCIのLOB操作ファンクションの概要
関数 | 説明 |
---|---|
OCILobAppend() |
|
OCILobAssign() |
ある |
OCILobCharSetForm() |
|
OCILobCharSetId() |
|
OCILobCopy() |
|
OCILobDisableBuffering() |
バッファリング・サブシステムの使用を無効化します。 |
OCILobEnableBuffering() |
|
OCILobErase() |
|
OCILobFileClose() |
オープン |
OCILobFileCloseAll() |
オープン |
OCILobFileExists() |
|
OCILobFileGetName() |
|
OCILobFileIsOpen() |
|
OCILobFileOpen() |
|
OCILobFileSetName() |
ロケータ内で |
OCILobFlushBuffer() |
|
OCILobGetLength() |
|
OCILobIsEqual() |
2つの |
OCILobLoadFromFile() |
|
OCILobLocatorIsInit() |
|
OCILobLocatorSize() |
|
OCILobRead() |
NULLでない |
OCILobTrim() |
|
OCILobWrite() |
バッファから |
表6-2に、LOB
アクセスに関するOCIおよびPL/SQL(DBMS_LOB
パッケージ)のインタフェースの比較を示します。
表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() |
OCILobDisableBuffering() |
該当なし |
OCILobEnableBuffering() |
該当なし |
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() |
該当なし( |
OCILobFlushBuffer() |
該当なし |
OCILobGetLength() |
DBMS_LOB.GETLENGTH() |
OCILobIsEqual() |
該当なし[PL/SQL等価演算子を使用] |
OCILobLoadFromFile |
DBMS_LOB.LOADFROMFILE() |
OCILobLocatorIsInit |
該当なし[常に初期化] |
OCILobRead |
DBMS_LOB.READ() |
OCILobTrim |
DBMS_LOB.TRIM() |
OCILobWrite |
DBMS_LOB.WRITE() |
例6-9では、データベースからロケータへのLOB
の選択方法を示します。lob_type
型には2つの属性、INTEGER
型のid
およびCLOB
型のdata
があり、lob_type
型のlob_table
表がすでに存在しているとみなします。
例6-9 データベースからロケータへのLOBの選択方法
/*-----------------------------------------------------------------------*/ /* Select lob locators from a CLOB column */ /* We need the 'FOR UPDATE' clause because we need to write 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; }
サンプル・プログラムpopulate.c
では、OCIを使用してCLOB
にディスク上のファイルの内容が移入されます。
DBMS_LOB
パッケージを使用すると、PL/SQLからLOB
を操作できます。表6-3でこのルーチンを説明します。
関連項目 DBMS_LOB パッケージのルーチンの使用に関する詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。 |
表6-3 DBMS_LOBパッケージ・ルーチンの概要
ルーチン | 説明 |
---|---|
APPEND() |
ソース |
COPY() |
ソース |
ERASE() |
すべてまたは一部の |
LOADFROMFILE() |
|
TRIM() |
|
WRITE() |
指定されたオフセットから |
GETLENGTH |
|
INSTR() |
|
READ() |
|
SUBSTR() |
|
FILECLOSE() |
ファイルI/Oをクローズします。 |
FILECLOSEALL() |
前にオープンされたファイルをすべてクローズします。 |
FILEEXISTS() |
サーバー上でファイルの有無をテストして確認します。 |
FILEGETNAME() |
ディレクトリ別名とファイル名を取得します。 |
FILEISOPEN() |
ファイルが入力の |
FILEOPEN() |
ファイルをオープンします。 |
例6-10では、TRIM
プロシージャをコールしてCLOB
値を短い長さにトリミングします。lob_type
型には2つの属性、INTEGER
型のid
およびCLOB
型のdata
があり、lob_type
型のlob_table
表がすでに存在しているとみなします。この例ではCLOB
データを処理するため、DBMS_LOB
.TRIM
の第2引数(834004
)では文字数を指定します。この例でBLOB
データを処理する場合、この引数はバイト数として解析されます。
LOBロケータは、外部プロシージャに引数として渡すことができます。対応するCのファンクションは、OCILobLocator
*型の引数を取得します。表6-3に定義されたファンクションは、コール時に次のシグネチャを使用してcルーチン(c_findmin()
)を起動します。
int c_findmin(OCILobLocator *);
例6-11 PL/SQL外部プロシージャの定義方法
FUNCTION DS_Findmin(data CLOB) RETURN PLS_INTEGER IS EXTERNAL NAME "c_findmin" LIBRARY DS_Lib LANGUAGE C;
このルーチン(c_findmin
)は、DS_Lib
に関連した共有ライブラリにあります。ポインタOCILobLocator *
を使用してLOB
からデータを取得するには、コールバックを実行してデータベースに再接続する必要があります。
種類に関係なくトリガー内のLOB
(:old
または:new
値)には書き込めません。
標準的なトリガーの場合、:old
値は読み取れますが、:new
値は読み取れません。INSTEAD
OF
トリガーの場合は、:old
値と:new
値を読み取ることができます。
トリガーが定義されている基礎となる表を更新しなくてもBFILE
型を更新できるため、OF
句にはLOB
型の列を指定できません。
OCIファンクションまたはDBMS_LOB
パッケージを使用してオブジェクト列のLOB
値またはLOB
属性を更新する場合、その列または属性を含む表に定義されているトリガーは起動されません。
Open/Close
ファンクションを使用すると、一連のLOB操作の開始と終了を示すことができます。これにより、Close
ファンクションのコール後に索引の更新のような大規模な操作を実行できます。これは、Open
がコールされた後は、LOBが変更されるたびに索引が更新されることはなく、この種の更新はClose
コールまで再開されないことを意味します。
すべてのLOB
操作をOpen/Close
操作でラップする必要はありませんが、コード・ブロックは次の理由から非常に役立ちます。
LOB
操作をOpen/Close
コールでラップしなければ、LOB
は変更のたびに暗黙的にオープンおよびクローズされ、トリガーが起動されます。LOB
操作をOpen/Close
操作のペアでラップすると、LOB
が変更されるたびにトリガーが起動されることはありません。かわりに、Close
がコールされるときにトリガーが1つ起動されます。同様に、拡張可能索引は、Close
をコールするまで更新されません。これは、LOB
の拡張可能索引がOpen/Close
コールの間は無効であることを意味します。
Open
操作とClose
操作の間はLOB
の変更を反映する状態が保存されないため、この方法は慎重に適用する必要があります。Open
をコールすると、LOB
値のうち変更された部分も変更結果である新旧の値も追跡されなくなります。LOB
値は引き続きOCILob*
またはDBMS_LOB
操作のたびに直接更新され、通常の読取り一貫性メカニズムが有効です。さらに、LOB
の拡張可能索引はLOBの変更時に更新する必要があります。拡張可能LOB
索引は常に有効であり、いつでも使用できるためです。
APIにより、LOB
がオープンされているかどうかを判別できます。オープンは、常にロケータではなくLOB
に関連付けられています。ロケータでは状態の情報は保存されません。
前にオープンしたLOB
をすべてクローズする前にトランザクションをコミットするとエラーになります。トランザクションのロールバック時には、まだオープンされているLOB
がすべて破棄されます。つまり、クローズされてトリガーが起動されることはありません。
異なるロケータまたは同じロケータを持つ同じLOB
に対して2回Open/Close
を実行するとエラーになります。オープンされていないLOB
をクローズするとエラーになります。
例6-12では、loc1
がオープンされているLOB
を参照し、loc2
に割り当てられているとします。以降にLOB
値を変更するためにloc2
が使用されると、変更はloc1
による変更とともにグループ化されます。つまり、LOB
マネージャの状態のエントリは1つのみであり、ロケータごとに1つではありません。LOB
が(loc1
またはloc2
を介して)クローズされると、トリガーが起動され、いずれかのロケータを介したLOB
の更新がすべてコミットされます。LOB
のクローズ後、ユーザーがいずれかのロケータを使用してLOB
を変更しようとすると、その操作は暗黙的なOpen()
およびClose()
でOpen() ...
operation
... Close()
として実行されます。読取り一貫性は引き続きロケータごとに維持されることに注意してください。単にロケータではなくLOB
がオープンされクローズされることを示しています。作成されるロケータのコピー数に関係なく、LOB
のトリガーは最初のClose
コール時に1回のみ起動されます。
例6-12 コード・ブロックのオープンおよびクローズ方法
open (loc1); loc2 := loc1; write (loc1); write (loc2); open (loc2); /* error because the LOB is already open */ close (loc1); /* triggers are fired and all LOB updates made prior to this statement by any locator are incorporated in the extensible index */ write (loc2); /* implicit open, write, implicit close */