6 構造化されていないデータ型の使用
Oracle Databaseにネイティブではない一部のデータ型は、Oracle Databaseでラージ・オブジェクト(LOB)として表すことができます。これらは、データ・カートリッジ用にPL/SQLおよびOCIで実装できます。
- カートリッジおよび構造化されていないデータ型の概要
- LOBでのDDLの使用
- LOBロケータ
- LOBを空にする
- OCIによるLOBの操作
- DBMS_LOBパッケージを使用したLOBの操作
- 外部プロシージャ内のLOB
- LOBとトリガー
- パフォーマンス向上のためのカッコとしてのOpen/Closeの使用
親トピック: データ・カートリッジの作成
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 );
親トピック: LOBでのDDLの使用
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 );
親トピック: LOBでのDDLの使用
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_LOBAPIについては、『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;
親トピック: LOBロケータ
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;
親トピック: LOBロケータ
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);
親トピック: LOBを空にする
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親トピック: LOBを空にする
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の現行の終了点以降のデータを書き込みます。非推奨メソッド |
親トピック: OCIによる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() |
親トピック: OCIによるLOBの操作
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;
}親トピック: OCIによるLOBの操作
6.6 DBMS_LOBパッケージを使用したLOBの操作
関連項目:
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() |
ファイルをオープンします。 |
親トピック: DBMS_LOBパッケージを使用したLOBの操作
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;親トピック: DBMS_LOBパッケージを使用したLOBの操作
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;
親トピック: 外部プロシージャ内のLOB
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 */
親トピック: Open/Close操作に関するエラーと制限