6 マルチメディア・データ型の使用
マルチメディア・データ型は、Oracleデータベースでラージ・オブジェクト(LOB)として表されます。データ・カートリッジではPL/SQLおよびOCIでマルチメディア・データ型を実装できます。
6.1 カートリッジとマルチメディア・データ型の概要
一部のデータ・カートリッジでは、グラフィック・イメージやサウンド波形などの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
を格納できるオブジェクト型に対するメソッドなど)を記述できます。
関連項目:
LOBs
の詳細は、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
を参照します。PL/SQLのDBMS_LOB
パッケージには、例6-5に示す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
値を指すロケータが含まれている必要があります。BLOB
列の値を空に初期化するには、INSERT
文のVALUES
句でファンクションEMPTY_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 OCIのLOB操作ファンクション
表6-1 OCIのLOB操作ファンクションの概要
ファンクション | 説明 |
---|---|
OCILobAppend() |
LOB値を別の |
OCILobArrayRead() |
1回のラウンドトリップで複数のロケータのLOBデータを読み取ります。 |
OCILobArrayWrite() |
1回のラウンドトリップで複数のロケータのLOBデータを書き込みます。 |
OCILobAssign() |
LOBロケータを別のLOBロケータに割り当てます。 |
OCILobCharSetForm() |
|
OCILobCharSetId() |
|
OCILobClose() |
オープンしているLOBまたは |
OCILobCopy2() |
LOBの全体または一部をコピーします(非推奨の |
OCILobCreateTemporary() |
一時LOBを作成します。 |
OCILobDisableBuffering() |
バッファリング・サブシステムを使用禁止にします。 |
OCILobEnableBuffering() |
|
OCILobErase2() |
LOBの全体または指定のオフセット以降の部分を消去します(非推奨の |
OCILobFileClose() |
オープンしている |
OCILobFileCloseAll() |
オープン |
OCILobFileExists() |
|
OCILobFileGetName() |
|
OCILobFileIsOpen() |
|
OCILobFileOpen() |
|
OCILobFileSetName() |
|
OCILobFlushBuffer() |
LOBバッファリング・サブシステムへの変更を、データベース(サーバー)へフラッシュします。 |
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() |
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() |
該当なし |
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
がコールされるまでこの更新は再開されません。
Open/Close
操作内ですべてのLOB
操作をラップする必要はありませんが、コード・ブロックは次の理由から非常に役立ちます。
-
Open/Close
コール内でLOB
操作をラップしないと、LOB
が変更されるたびにLOB
のオープンとクローズが暗黙的に実行されて、すべてのトリガーが起動されます。Open/Close
操作のペア内でLOB
操作をラップすると、LOB
が変更されるたびにトリガーが起動されることはありません。かわりに、Close
がコールされるときにトリガーが1回起動されます。同様に、拡張可能索引はClose
がコールされるまで更新されません。これは、Open/Close
のコールの間は、LOB
の拡張可能索引が有効でないことを意味します。 -
Open
操作とClose
操作の間はLOB
に対する変更を表す状態が保存されないため、この技術は注意して適用する必要があります。Open
をコールすると、LOB
値の変更された部分や、変更前後のLOB
の値が追跡されなくなります。跡されません。その間もLOB
値はOCILob*
操作またはDBMS_LOB
操作のたびに直接更新され、通常の読取り一貫性メカニズムが適用されます。LOB
が変更されるときには、LOB
の拡張可能索引の更新も必要です。拡張可能なLOB
索引は常に有効であり、いつでも使用できるためです。 -
APIにより、
LOB
がオープンされているかどうかを判別できます。オープンは、常にロケータではなくLOB
に関連付けられています。ロケータでは状態の情報は保存されません。
6.9.1 Open/Close操作に関するエラーと制限
オープンしていたLOB
をすべてクローズする前にトランザクションをコミットするとエラーになります。トランザクションのロールバック時には、オープンされているLOB
もすべて破棄されます。つまり、クローズされないためにトリガーが起動されます。
異なるロケータまたは同じロケータを持つ同じLOB
に対して2回Open/Close
を実行するとエラーになります。オープンされていないLOB
をクローズするとエラーになります。
例6-11では、loc1
がオープンされているLOB
を参照し、loc2
に割り当てられているとします。これ以降にloc2
を使用してLOB
値を変更しようとすると、その変更がloc1
による変更とグループ化されます。つまり、LOB
マネージャの状態のエントリは1つのみであり、ロケータごとに1つではありません。LOB
がloc1
またはloc2
を介してクローズされると、トリガーが起動されて、いずれかのロケータから行われたLOB
の更新がすべてコミットされます。LOB
のクローズ後に、ユーザーがいずれかのロケータを使用してLOB
を変更しようとすると、その操作で暗黙的なOpen()
およびClose()
がOpen() ...
operation
... Close()
として実行されます。読取り一貫性は引き続きロケータごとに維持されることに注意してください。単にロケータではなくLOB
がオープンされクローズされることを示しています。作成されるロケータのコピーの数に関係なく、LOB
のトリガーは最初のClose
のコールで1回のみ起動されます。
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 */