14 高度な設計時の考慮事項
より高度なアプリケーション開発時の問題について設計上の考慮事項があります。
内容は次のとおりです。
14.1 OPENおよびCLOSEインタフェースを使用した永続LOBのオープン
OPEN
およびCLOSE
インタフェースを使用すると、永続LOBインスタンスを明示的にオープンできます。
OPEN
インタフェースを使用してLOBインスタンスをオープンすると、そのインスタンスはCLOSE
インタフェースを使用してLOBを明示的にクローズするまでオープンされたままになります。ISOPEN
インタフェースを使用すると、永続LOBがオープンしているかどうかを確認できます。
LOBのオープン状態はLOBロケータではなくLOBインスタンスに関連付けられていることに注意してください。ロケータには、それが指すLOBインスタンスがオープンしているかどうかを示す情報は格納されません。
関連項目:
内容は次のとおりです。
14.1.1 LOBの明示的なオープンによる索引のパフォーマンス上のメリット
LOBインスタンスを明示的にオープンすると、索引付き列の永続LOBのパフォーマンスを改善できます。
LOBインスタンスを明示的にオープンしない場合、LOBに対する変更が行われるたびにLOBインスタンスが暗黙的にオープンおよびクローズされます。ドメイン索引に対するトリガーは、LOBがクローズされるたびに起動されます。この場合、LOBインスタンスに対する変更が行われるとすぐにLOB上のドメイン索引が更新されます。ドメイン索引は常に有効であり、いつでも使用できます。
LOBインスタンスを明示的にオープンすると、LOBを明示的にクローズするまで索引トリガーは起動されません。この方法を使用すると、LOBを明示的にクローズするまで不要な索引付けイベントが発生しなくなり、索引列のパフォーマンスを改善できます。LOB列のすべての索引は、そのLOBを明示的にクローズするまで無効であることに注意してください。
14.1.2 明示的にオープンしたLOBインスタンスのクローズ
LOBインスタンスを明示的にオープンした場合は、トランザクションをコミットする前にLOBをクローズする必要があります。
オープン状態にあるLOBインスタンスに対するトランザクションをコミットすると、エラーが発生します。このエラーが発生すると、LOBインスタンスが暗黙的にクローズされてLOBインスタンスに対する変更が保存され、トランザクションがコミットされますが、LOB列の索引は更新されません。この場合は、LOB列に対する索引を再作成する必要があります。
その後トランザクションをロールバックすると、LOBインスタンスは前の状態にロールバックされますが、LOBインスタンスは明示的にオープンされません。
次の状況で明示的にオープンしたLOBインスタンスは、すべてクローズする必要があります。
-
トランザクションを起動するDML文の間(
SELECT
...FOR
UPDATE
およびCOMMIT
を含む) -
自律型トランザクション・ブロック内
-
セッションの終了前(関係するトランザクションがないとき)
LOBインスタンスを明示的にクローズしない場合、セッションの終了時に暗黙的にクローズされ、索引トリガーは起動されません。
明示的にオープンしたLOBがオープン状態かクローズ状態かを追跡してください。次の場合にはエラーが発生します。
-
すでに明示的にオープンしているLOBインスタンスを明示的にオープンする場合。
-
すでに明示的にクローズしているLOBインスタンスを明示的にクローズする場合。
このエラーは、LOBインスタンスへのアクセスに同じロケータを使用しているかどうかに関係なく発生します。
14.2 読取り一貫性のあるロケータ
Oracle DatabaseのLOBに対する読取り一貫性メカニズムは、その他すべてのスカラー量に対するデータベースの読取りおよび更新のための読取り一貫性メカニズムと同じです。
LOBロケータの場合、読取り一貫性にはいくつかの特別な用途があるため、正しく理解しておく必要があります。次の各項では、読取り一貫性について説明し、互いに関連付けながら参照する必要がある例を示します。
関連項目:
-
読取り一貫性の一般的な情報は、Oracle Database概要を参照してください
内容は次のとおりです。
14.2.1 読取り一貫性のあるロケータになる、SELECTされたロケータ
FOR
UPDATE
句の有無にかかわらず、SELECTされたロケータは読取り一貫性のあるロケータとなり、LOB値がそのロケータによって更新されるまでは読取り一貫性のあるロケータとして存在します。
読取り一貫性のあるロケータには、SELECT
操作の実行時点のスナップショット環境が含まれます。
これには複雑な意味があります。たとえば、SELECT
操作によって読取り一貫性のあるロケータ(L1
)を作成したとします。L1
によって永続LOB値を読み取るときは、次のことに注意してください。
-
SELECT
文にFOR
UPDATE
が含まれていても、SELECT
文実行時点のLOBが読み取られます。 -
同じトランザクションの別のロケータ(
L2
)によってLOB値が更新されても、L1
ではL2
の更新が認識されません。 -
L1
では、別のトランザクションによってLOBにコミットされた更新も認識されません。 -
読取り一貫性のあるロケータ
L1
が別のロケータL2
にコピーされた場合(たとえば、2つのロケータ変数のPL/SQL割当てL2:= L1
によって)、L2
はL1
と同様に読取り一貫性のあるロケータとなり、読み取られるデータはL1
に対するSELECT
実行時点のデータとなります。
複数のロケータが存在していることを利用して、変化するLOB値のそれぞれの値にアクセスできます。ただし、その場合は、どのロケータでどの値にアクセスしているかを常に理解しておく必要があります。
14.2.2 LOBの更新および読取り一貫性の例
読取り一貫性のあるロケータでは、SELECT
実行時期に関係なく同じLOB値が提供されます。
次に、読取り一貫性と更新の関係を簡単な例で示します。「LOBの例の表: PMスキーマのprint_media表」で説明されているprint_media
表とPL/SQLを使用して、clob_selected
、clob_update
およびclob_copied
の3つのCLOB
インスタンスをロケータとして作成します。
次のコード例では、t1
からt6
が次のように処理されます。
-
最初の
SELECT
INTO
の実行時(t1
)に、ad_sourcetext
内の値がロケータclob_selected
に対応付けられます。 -
次の操作(
t2
)で、ad_sourcetext
内の値がロケータclob_updated
に対応付けられます。t1
とt2
の間ではad_sourcetext
の値が変わらないため、clob_selected
およびclob_updated
の両方のロケータは、異なる時点でのスナップショットを反映していても、同じ値を持つ読取り一貫性のあるロケータとなります。 -
3つ目の操作(
t3
)では、clob_selected
の値がclob_copied
にコピーされます。この時点で、3つのロケータが同じ値になります。例では、一連のDBMS_LOB
.READ()
コールがこれを示しています。 -
t4
で、プログラムはDBMS_LOB
.WRITE()
を使用してclob_updated
内の値を変更します。DBMS_LOB
.READ()
が新しい値を示します。 -
ただし、
clob_selected
を介した値のDBMS_LOB
.READ()
は(t5
)、これが読取り一貫性のあるロケータで、SELECT
の実行時と同じ値が引き続き参照されることを示します。 -
同様に、
clob_copied
を介した値のDBMS_LOB
.READ()
は(t6
)、これが読取り一貫性のあるロケータで、clob_selected
と同じ値が引き続き参照されることを示します。
例14-1
INSERT INTO PRINT_MEDIA VALUES (2056, 20020, EMPTY_BLOB(), 'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL); COMMIT; DECLARE num_var INTEGER; clob_selected CLOB; clob_updated CLOB; clob_copied CLOB; read_amount INTEGER; read_offset INTEGER; write_amount INTEGER; write_offset INTEGER; buffer VARCHAR2(20); BEGIN -- At time t1: SELECT ad_sourcetext INTO clob_selected FROM Print_media WHERE ad_id = 20020; -- At time t2: SELECT ad_sourcetext INTO clob_updated FROM Print_media WHERE ad_id = 20020 FOR UPDATE; -- At time t3: clob_copied := clob_selected; -- After the assignment, both the clob_copied and the -- clob_selected have the same snapshot as of the point in time -- of the SELECT into clob_selected -- Reading from the clob_selected and the clob_copied does -- return the same LOB value. clob_updated also sees the same -- LOB value as of its select: read_amount := 10; read_offset := 1; dbms_lob.read(clob_selected, read_amount, read_offset, buffer); dbms_output.put_line('clob_selected value: ' || buffer); -- Produces the output 'abcd' read_amount := 10; dbms_lob.read(clob_copied, read_amount, read_offset, buffer); dbms_output.put_line('clob_copied value: ' || buffer); -- Produces the output 'abcd' read_amount := 10; dbms_lob.read(clob_updated, read_amount, read_offset, buffer); dbms_output.put_line('clob_updated value: ' || buffer); -- Produces the output 'abcd' -- At time t4: write_amount := 3; write_offset := 5; buffer := 'efg'; dbms_lob.write(clob_updated, write_amount, write_offset, buffer); read_amount := 10; dbms_lob.read(clob_updated, read_amount, read_offset, buffer); dbms_output.put_line('clob_updated value: ' || buffer); -- Produces the output 'abcdefg' -- At time t5: read_amount := 10; dbms_lob.read(clob_selected, read_amount, read_offset, buffer); dbms_output.put_line('clob_selected value: ' || buffer); -- Produces the output 'abcd' -- At time t6: read_amount := 10; dbms_lob.read(clob_copied, read_amount, read_offset, buffer); dbms_output.put_line('clob_copied value: ' || buffer); -- Produces the output 'abcd' END; /
14.2.3 更新済ロケータを介したLOB更新の例
LOBロケータ(L1
)を介して永続LOBの値を更新する場合、現在のスナップショット環境が含まれるようL1
が更新されます。
このスナップショットは、ロケータL1
を介してLOB値に対する操作が完了した時点のものです。このため、L1
は更新済ロケータと呼ばれます。この操作によって、LOB値に対して行った変更を、同じロケータL1
による次の読取り時に参照できます。
ノート:
LOB値を読み取ることのみのためにロケータが使用される場合、ロケータのスナップショット環境は更新されません。更新されるのは、PL/SQL DBMS_LOB
パッケージまたはOCI LOB APIを使用して、ロケータを介してLOB値を変更した場合のみです。
別のトランザクションによってコミットされた更新は、そのトランザクションがコミット読取りトランザクションであり、他のトランザクションのコミット後にL1を使用してLOB値を更新する場合のみに、L1によって認識されます。
ノート:
永続LOBの値を更新すると、最新のLOB値が常に変更されます。
OCI LOB APIやPL/SQL DBMS_LOB
パッケージなどの使用可能なメソッドによって永続LOBの値を更新することは、LOB値を更新して、新しいLOB値を参照するロケータを再選択することになります。
14.2.4 SQL DMLおよびDBMS_LOBを使用したLOBの更新例
次の例では、Print_media
表を使用し、CLOB
ロケータがclob_selected
として作成されます。この例では、t1
からt3
が次のように処理されます。
-
最初の
SELECT
INTO
の実行時(t1
)に、ad_sourcetext
内の値がロケータclob_selected
に対応付けられます。 -
次の操作(
t2
)では、ロケータclob_selected
に影響を与えずに、SQL
UPDATE
文を介してad_sourcetext
内の値が変更されます。ロケータは元のSELECT
時点でのLOB値を参照します。つまり、ロケータはSQLUPDATE
文によって実行された更新を認識しません。例では、後続のDBMS_LOB
.READ()
コールがこれを示しています。 -
3つ目の操作(
t3
)では、LOB値が再選択されロケータclob_selected
に挿入されます。これによって、ロケータは最新のスナップショット環境に更新され、先のSQLUPDATE
文によって行われた変更を認識できるようになります。このため、次のDBMS_LOB
.READ()
では、LOB値が空である(データがない)ためエラーが戻されます。
INSERT INTO Print_media VALUES (3247, 20010, EMPTY_BLOB(), 'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL); COMMIT; DECLARE num_var INTEGER; clob_selected CLOB; read_amount INTEGER; read_offset INTEGER; buffer VARCHAR2(20); BEGIN -- At time t1: SELECT ad_sourcetext INTO clob_selected FROM Print_media WHERE ad_id = 20010; read_amount := 10; read_offset := 1; dbms_lob.read(clob_selected, read_amount, read_offset, buffer); dbms_output.put_line('clob_selected value: ' || buffer); -- Produces the output 'abcd' -- At time t2: UPDATE Print_media SET ad_sourcetext = empty_clob() WHERE ad_id = 20010; -- although the most current LOB value is now empty, -- clob_selected still sees the LOB value as of the point -- in time of the SELECT read_amount := 10; dbms_lob.read(clob_selected, read_amount, read_offset, buffer); dbms_output.put_line('clob_selected value: ' || buffer); -- Produces the output 'abcd' -- At time t3: SELECT ad_sourcetext INTO clob_selected FROM Print_media WHERE ad_id = 20010; -- the SELECT allows clob_selected to see the most current -- LOB value read_amount := 10; dbms_lob.read(clob_selected, read_amount, read_offset, buffer); -- ERROR: ORA-01403: no data found END; /
14.2.5 同じLOB値を更新するために1つのロケータを使用する例
次の例では、Print_media
表を使用し、clob_updatedおよびclob_copiedの2つのCLOB
をロケータとして作成します。
この例では、t1からt5が次のように処理されます。
-
最初の
SELECT
INTO
の実行時(t1
)に、ad_sourcetext
内の値がロケータclob_updated
に対応付けられます。 -
次の操作(
t2
)では、clob_updated
の値がclob_copied
にコピーされます。この時点では、両方のロケータが同じ値を参照します。例では、一連のDBMS_LOB
.READ()
コールがこれを示しています。 -
t3
で、プログラムはDBMS_LOB
.WRITE()
を使用してclob_updated
内の値を変更します。DBMS_LOB.READ()
が新しい値を示します。 -
ただし、
clob_copied
を介した値のDBMS_LOB
.READ()
は(t4
)、clob_updated
から割り当てられた時点(t2
)のLOBの値を参照しています。 -
clob_updated
がclob_copied
に割り当てられた時点(t5
)で初めて、clob_copied
はclob_updated
による更新があったことを認識します。
INSERT INTO PRINT_MEDIA VALUES (2049, 20030, EMPTY_BLOB(), 'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL); COMMIT; DECLARE num_var INTEGER; clob_updated CLOB; clob_copied CLOB; read_amount INTEGER; read_offset INTEGER; write_amount INTEGER; write_offset INTEGER; buffer VARCHAR2(20); BEGIN -- At time t1: SELECT ad_sourcetext INTO clob_updated FROM PRINT_MEDIA WHERE ad_id = 20030 FOR UPDATE; -- At time t2: clob_copied := clob_updated; -- after the assign, clob_copied and clob_updated see the same -- LOB value read_amount := 10; read_offset := 1; dbms_lob.read(clob_updated, read_amount, read_offset, buffer); dbms_output.put_line('clob_updated value: ' || buffer); -- Produces the output 'abcd' read_amount := 10; dbms_lob.read(clob_copied, read_amount, read_offset, buffer); dbms_output.put_line('clob_copied value: ' || buffer); -- Produces the output 'abcd' -- At time t3: write_amount := 3; write_offset := 5; buffer := 'efg'; dbms_lob.write(clob_updated, write_amount, write_offset, buffer); read_amount := 10; dbms_lob.read(clob_updated, read_amount, read_offset, buffer); dbms_output.put_line('clob_updated value: ' || buffer); -- Produces the output 'abcdefg' -- At time t4: read_amount := 10; dbms_lob.read(clob_copied, read_amount, read_offset, buffer); dbms_output.put_line('clob_copied value: ' || buffer); -- Produces the output 'abcd' -- At time t5: clob_copied := clob_updated; read_amount := 10; dbms_lob.read(clob_copied, read_amount, read_offset, buffer); dbms_output.put_line('clob_copied value: ' || buffer); -- Produces the output 'abcdefg' END; /
14.2.6 PL/SQL(DBMS_LOB)バインド変数を使用したLOBの更新の例
別の永続LOBを更新するためのソースとしてLOBロケータを使用する場合(SQLのINSERT
文またはUPDATE
文、DBMS_LOB
.COPY
ルーチンなど)、ソースLOBロケータ内のスナップショット環境によって、ソースとして使用されるLOB値が決まります。
ソース・ロケータ(たとえばL1
)が読取り一貫性のあるロケータの場合、L1
のSELECT
実行時点のLOB値が使用されます。ソース・ロケータ(たとえばL2
)が更新済ロケータの場合、更新時におけるL2
のスナップショット環境に対応付けられたLOB値が使用されます。
次の例では、clob_selected
、clob_updatedおよびclob_copiedの3つのCLOB
をロケータとして作成します。
この例では、t1
からt5
が次のように処理されます。
-
最初の
SELECT
INTO
の実行時(t1
)に、ad_sourcetext
内の値がロケータclob_updated
に対応付けられます。 -
次の操作(
t2
)では、clob_updated
の値がclob_copied
にコピーされます。この時点では、両方のロケータが同じ値を参照します。 -
この時点で(
t3
)、プログラムはDBMS_LOB
.WRITE()
を使用してclob_updated
内の値を変更します。DBMS_LOB
.READ()
が新しい値を示します。 -
ただし、
clob_copied
を介した値のDBMS_LOB
.READ()
は(t4
)、clob_copied
がclob_updated
による変更を参照しないことを示します。 -
このため(
t5
の時点で)、clob_copied
をINSERT
文の値のソースとして使用する場合、clob_copied
と対応付けられた値が挿入されます(clob_updated
による新規の変更は反映されません)。これは、その後の、挿入された直後の値に対するDBMS_LOB
.READ()
によってわかります。
INSERT INTO PRINT_MEDIA VALUES (2056, 20020, EMPTY_BLOB(), 'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL); COMMIT; DECLARE num_var INTEGER; clob_selected CLOB; clob_updated CLOB; clob_copied CLOB; read_amount INTEGER; read_offset INTEGER; write_amount INTEGER; write_offset INTEGER; buffer VARCHAR2(20); BEGIN -- At time t1: SELECT ad_sourcetext INTO clob_updated FROM PRINT_MEDIA WHERE ad_id = 20020 FOR UPDATE; read_amount := 10; read_offset := 1; dbms_lob.read(clob_updated, read_amount, read_offset, buffer); dbms_output.put_line('clob_updated value: ' || buffer); -- Produces the output 'abcd' -- At time t2: clob_copied := clob_updated; -- At time t3: write_amount := 3; write_offset := 5; buffer := 'efg'; dbms_lob.write(clob_updated, write_amount, write_offset, buffer); read_amount := 10; dbms_lob.read(clob_updated, read_amount, read_offset, buffer); dbms_output.put_line('clob_updated value: ' || buffer); -- Produces the output 'abcdefg' -- note that clob_copied does not see the write made before -- clob_updated -- At time t4: read_amount := 10; dbms_lob.read(clob_copied, read_amount, read_offset, buffer); dbms_output.put_line('clob_copied value: ' || buffer); -- Produces the output 'abcd' -- At time t5: -- the insert uses clob_copied view of the LOB value which does -- not include clob_updated changes INSERT INTO PRINT_MEDIA VALUES (2056, 20022, EMPTY_BLOB(), clob_copied, EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL) RETURNING ad_sourcetext INTO clob_selected; read_amount := 10; dbms_lob.read(clob_selected, read_amount, read_offset, buffer); dbms_output.put_line('clob_selected value: ' || buffer); -- Produces the output 'abcd' END; /
14.2.7 ロケータを使用したLOBの削除の例
この項では、PL/SQLバインド変数を使用したLOBの削除について学習します。
次の例では、特定の時点で選択されたロケータを介したLOBコンテンツを、同じトランザクション内でそのLOBが削除されている場合でも使用可能であることを示します。
次の例では、print_media
表を使用し、clob_selected
およびclob_copied
という2つのCLOBをロケータとして作成します。
この例では、t1
からt3
が次のように処理されます。
- 最初の
SELECT
INTO
の実行時(t1
)に、ad_id
の値が20020であるad_sourcetext
の値が、ロケータclob_selected
に対応付けられます。ad_id
の値が20021であるad_sourcetext
の値は、ロケータclob_copied
に関連付けられます。 - 2つ目の操作(
t2
)では、ad_id
の値が20020である行が削除されます。ただし、clob_selected
を介した値のDBMS_LOB.READ()
は(t1
)、これが読取り一貫性のあるロケータであり、SELECTの実行時と同じ値が引き続き参照されることを示します。 - 3つ目の操作(
t3
)では、clob_selected
を介して読み取られたLOBデータがLOBclob_copied
にコピーされます。clob_selected
およびclob_copied
を介した値のDBMS_LOB.READ()
はこれと同じになり、clob_selected
のSELECT
の実行時と同じ値を参照します。
INSERT INTO PRINT_MEDIA VALUES (2056, 20020, EMPTY_BLOB(), 'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL); INSERT INTO PRINT_MEDIA VALUES (2057, 20021, EMPTY_BLOB(), 'cdef', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL); DECLARE clob_selected CLOB; clob_copied CLOB; buffer VARCHAR2(20); read_amount INTEGER := 20; read_offset INTEGER := 1; BEGIN -- At time t1: SELECT ad_sourcetext INTO clob_selected FROM PRINT_MEDIA WHERE ad_id = 20020 FOR UPDATE; SELECT ad_sourcetext INTO clob_copied FROM PRINT_MEDIA WHERE ad_id = 20021 FOR UPDATE; dbms_lob.read(clob_selected, read_amount, read_offset,buffer); dbms_output.put_line(buffer); -- Produces the output 'abcd' dbms_lob.read(clob_copied, read_amount, read_offset,buffer); dbms_output.put_line(buffer); -- Produces the output 'cdef' -- At time t2: Delete the CLOB associated with clob_selected DELETE FROM PRINT_MEDIA WHERE ad_id = 20020; dbms_lob.read(clob_selected, read_amount, read_offset,buffer); dbms_output.put_line(buffer); -- Produces the output 'abcd' -- At time t3: -- Copy using clob_selected dbms_lob.copy(clob_copied, clob_selected, 4000, 1, 1); dbms_lob.read(clob_copied, read_amount, read_offset,buffer); dbms_output.put_line(buffer); -- Produces the output 'abcd' END; /
14.3 LOBロケータとトランザクション境界
14.3.1 LOBロケータとトランザクション境界について
LOBロケータとトランザクションについては、次のことに注意してください。
-
トランザクションを開始してからロケータを選択する場合: トランザクションを開始した後にロケータを選択すると、ロケータにトランザクションIDが含まれます。トランザクションを明示的に開始しなくてもトランザクションに暗黙的に入ることができます。たとえば、
SELECT
...FOR
UPDATE
によってトランザクションは暗黙的に開始されます。このような場合、ロケータにはトランザクションIDが含まれます。 -
ロケータがトランザクションIDを含まない場合
-
トランザクション外でロケータを選択する場合: 対照的に、トランザクションの外部でロケータを選択する場合、ロケータはトランザクションIDを含みません。
-
DML文の実行の前に選択した場合: トランザクションIDは、最初のDML文が実行されるまでは割り当てられません。このため、このようなDML文の前に選択されたロケータはIDを含みません。
-
14.3.2 ロケータを使用したLOBに対する読取りおよび書込み操作
ロケータがトランザクションIDを含んでいるかどうかにかかわらず、常にロケータを使用してLOBデータを読み取ることができます。
-
ロケータを使用して書込みができない場合:
ロケータがトランザクションIDを含む場合、その特定のトランザクション外でLOBに書き込むことはできません。
-
ロケータを使用して書込みができる場合:
ロケータがトランザクションIDを含まない場合、トランザクションを明示的または暗黙的に開始した後、LOBに書き込むことができます。
-
シリアライズ可能なトランザクションでロケータを使用して読取りまたは書込みができない場合:
ロケータが古いトランザクションのトランザクションIDを含み、現在のトランザクションがシリアライズ可能である場合、このロケータを使用して読取りまたは書込みを行うことはできません。
-
シリアライズ不能なトランザクションでロケータを使用して読取りはできるが書込みができない場合:
トランザクションがシリアライズ不能である場合、トランザクション外で読み取ることはできますが、書き込むことはできません。
「トランザクション境界外でのロケータの選択」、「トランザクション境界内でのロケータの選択」、「複数のトランザクションにまたがることはできないLOBロケータ」および「トランザクションにまたがらないロケータの例」の各例は、ロケータとシリアライズ可能でないトランザクション間の関係を示しています
14.3.3 トランザクション境界外でのロケータの選択
2つの使用例により、トランザクション外でロケータが選択された場合に、シリアライズ可能でないトランザクションでロケータを使用する方法について説明します。
シナリオ1:
-
現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。
-
トランザクションを開始します。
-
ロケータを使用してLOBからデータを読み取ります。
-
トランザクションをコミットまたはロールバックします。
-
ロケータを使用してLOBからデータを読み取ります。
-
トランザクションを開始します。ロケータはトランザクションIDを含みません。
-
ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。
シナリオ2:
- 現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。
- トランザクションを開始します。ロケータはトランザクションIDを含みません。
- ロケータを使用してLOBからデータを読み取ります。ロケータはトランザクションIDを含みません。
- ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。続けてLOBの読取りまたは書込みを行うことができます。
- トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
- ロケータを使用してLOBからデータを読み取ります。この操作は有効です。
- トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。
- ロケータを使用してデータをLOBに書き込みます。ロケータが現行トランザクションに一致するトランザクションIDを含まないため、この書込み操作は失敗します。
14.3.4 トランザクション境界内でのロケータの選択
2つの使用例により、トランザクション内でロケータが選択された場合に、シリアライズ可能でないトランザクションでロケータを使用する方法について説明します。
シナリオ1:
-
トランザクションの中でロケータを選択します。この時点では、ロケータはトランザクションIDを含んでいます。
-
トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。
-
ロケータを使用してLOBからデータを読み取ります。ロケータの中のトランザクションIDは現在のトランザクションに一致していませんが、この操作は有効です。
関連項目:
ロケータを使用したLOBデータの読取りの詳細は、「読取り一貫性のあるロケータ」を参照してください。
-
ロケータを使用してデータをLOBに書き込みます。ロケータの中のトランザクションIDが現在のトランザクションに一致していないため、この操作は失敗します。
シナリオ2:
- トランザクションを開始します。
- ロケータを選択します。ロケータがトランザクションの中で選択されたため、トランザクションIDが含まれています。
- ロケータを使用してLOBの読取りまたは書込みを行います。これらの操作は有効です。
- トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
- ロケータを使用してLOBからデータを読み取ります。ロケータの中にトランザクションIDがあり、そのトランザクションはすでにコミットまたはロールバックされていますが、この操作は有効です。
- ロケータを使用してデータをLOBに書き込みます。ロケータの中のトランザクションIDはすでにコミットまたはロールバックされたトランザクション用であるため、この操作は失敗します。
14.3.5 複数のトランザクションにまたがることはできないLOBロケータ
DBMS_LOB
、OCI、SQLのINSERT
文またはUPDATE
文を使用し、LOBロケータによって永続LOBの値を更新すると、読取り一貫性のあるロケータが更新済ロケータに変更されます。
INSERT
文やUPDATE
文によって、トランザクションが自動的に開始され、行がロックされます。一度これが発生すると、ロケータを現行トランザクション以外で使用して、LOB値を変更できなくなる可能性があります。データの書込みに使用するLOBロケータを複数のトランザクションにまたがって使用することはできません。ただし、シリアライズ可能なトランザクション内でない場合は、ロケータを使用してLOB値を読み取ることができます。
関連項目:
LOBとトランザクション境界の関係については、「LOBロケータとトランザクション境界」を参照してください。
「トランザクションにまたがらないロケータの例」では、CLOB
ロケータとしてclob_updated
が作成されています
-
最初の
SELECT
INTO
の実行時(t1)に、ad_sourcetext
内の値がロケータclob_updated
に対応付けられます。 -
次の操作で(t2)、
DBMS_LOB
.WRITE
ファンクションを使用してclob_updated
内の値を変更します。DBMS_LOB
.READ
が新しい値を示します。 -
COMMIT
文(t3)によって現行のトランザクションが終了します。 -
トランザクションを終了すると(t4)、
clob_updated
ロケータは別のトランザクション(コミット済)を参照することになるため、その後のDBMS_LOB
.WRITE
操作は失敗します。これは、戻されるエラーによって通知されます。このLOBロケータをさらにDBMS_LOB
(およびOCI)の変更操作で使用するには、再選択する必要があります。
14.3.6 トランザクションにまたがらないロケータの例
この例では、「LOBの例の表: PMスキーマのprint_media表」で説明されているprint_media
表を使用します
INSERT INTO PRINT_MEDIA VALUES (2056, 20010, EMPTY_BLOB(), 'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL); COMMIT; DECLARE num_var INTEGER; clob_updated CLOB; read_amount INTEGER; read_offset INTEGER; write_amount INTEGER; write_offset INTEGER; buffer VARCHAR2(20); BEGIN -- At time t1: SELECT ad_sourcetext INTO clob_updated FROM PRINT_MEDIA WHERE ad_id = 20010 FOR UPDATE; read_amount := 10; read_offset := 1; dbms_lob.read(clob_updated, read_amount, read_offset, buffer); dbms_output.put_line('clob_updated value: ' || buffer); -- This produces the output 'abcd' -- At time t2: write_amount := 3; write_offset := 5; buffer := 'efg'; dbms_lob.write(clob_updated, write_amount, write_offset, buffer); read_amount := 10; dbms_lob.read(clob_updated, read_amount, read_offset, buffer); dbms_output.put_line('clob_updated value: ' || buffer); -- This produces the output 'abcdefg' -- At time t3: COMMIT; -- At time t4: dbms_lob.write(clob_updated , write_amount, write_offset, buffer); -- ERROR: ORA-22990: LOB locators cannot span transactions END; /
14.4 オブジェクト・キャッシュ内のLOB
LOBロケータ属性を使用してオブジェクト・キャッシュ内でオブジェクトを別のオブジェクトにコピーする場合、LOBロケータのみがコピーされます。
つまり、これら2つの異なるオブジェクト内のLOB属性には、唯一かつ同一のLOB値を参照する厳密に同一のロケータが含まれます。ターゲット・オブジェクトがフラッシュされた場合のみ、異なるLOB値の物理コピーが作成されます。これは、ソースLOB値とは異なります。
関連項目:
ロケータの1つを使用して書込みが実行された場合、各オブジェクトでどのバージョンのLOB値が参照されるかについては、「LOBの更新および読取り一貫性の例」を参照してください。
したがって、コピーのターゲットであったLOBを変更する場合、ターゲット・オブジェクトをフラッシュし、ターゲット・オブジェクトをリフレッシュしてから、ロケータ属性を介してLOBに書き込む必要があります。
内部LOB属性および外部LOB属性には、次のようなオブジェクト・キャッシュの問題があります。
-
永続LOB属性: オブジェクト・キャッシュ内にオブジェクトを作成すると、LOB属性は空に設定されます。
オブジェクト・キャッシュ内に、永続LOB属性を持つオブジェクトを作成すると、そのLOB属性は暗黙的に空に設定されます。この空のLOBロケータを使用して、データをLOBに書き込むことはできません。最初にオブジェクトをフラッシュし、その後、表に行を挿入し、空のLOB(長さ0(ゼロ)のLOB)を作成する必要があります。オブジェクトがオブジェクト・キャッシュ内でリフレッシュされ(
OCI_PIN_LATEST
を使用)、実際のLOBロケータがLOB属性に読み取られると、OCI LOB APIをコールしてLOBにデータを書き込むことができます。 -
外部LOB(
BFILE
)属性: オブジェクト・キャッシュ内にオブジェクトを作成すると、BFILE
属性はNULL
に設定されます。外部LOB (
BFILE)
属性を使用してオブジェクトを作成する場合、BFILE
はNULL
に設定されます。これは、BFILE
から読み取られる前に有効なディレクトリ・オブジェクト名およびファイル名を使用して更新する必要があります。
14.5 サイズがTBのLOBのサポート
14.5.1 サイズがTBのLOBのサポートについて
サイズがTBのLOBは、次のAPIによってサポートされます。
-
JDBC(Java Database Connectivity)を使用したJava
-
DBMS_LOBパッケージを使用したPL/SQL
-
OCI(Oracle Call Interface)を使用したC
4GBを超えるサイズのLOBインスタンス(サイズがTBのLOB)は、次のプログラム環境では作成も使用もできません。
-
Pro*COBOLプリコンパイラを使用したCOBOL
-
Pro*C/C++プリコンパイラを使用したCまたはC++
ノート:
Oracle Databaseでは、どのプログラム環境であっても、2^64-1バイト(OCIではUB8MAXVAL
)を超えるBFILE
はサポートされません。BFILE
には、オペレーティング・システムに基づくその他のファイル・サイズ制限も適用されます。
14.5.2 サイズがTBのLOBの最大記憶域の制限
サポートされている環境では、データベース構成の最大記憶域サイズの上限までLOBを作成して操作できます。
Oracle Databaseを使用すると、データベースのブロック・サイズとは異なるブロック・サイズの表領域を作成できます。LOBの最大サイズは、表領域ブロックのサイズによって決まります。CHUNK
は、LOB記憶域のパラメータで、この値は、LOBが格納されている表領域のブロック・サイズによって制御されます。
ノート:
CHUNK
パラメータは、SecureFilesには適用されません。これは、BasicFiles LOBに対してのみ使用されます。
LOB列を作成する場合、CHUNK
の値を指定できます。これは、LOBの操作用に割り当てるバイト数です。この値は、表領域ブロック・サイズの倍数である必要があります。そうでない場合、Oracle Databaseによって次の倍数に切り上げられます。(表ブロック・サイズがデータベース・ブロック・サイズと同じ場合、CHUNK
は、データベース・ブロック・サイズの倍数でもあります。)
使用構成で許可される記憶域の最大制限は、表領域ブロック・サイズによって決まり、(4GB - 1)にDBMS_LOB.GETCHUNKSIZE
またはOCILobGetChunkSize()
から取得した値を乗算した値として計算されます。この値(BLOB
のバイト数またはCLOB
の文字数)は、内部記憶域のオーバーヘッドにより、実際にはCHUNK
パラメータのサイズより小さくなります。表領域ブロック・サイズで許可される現在の範囲は2Kから32Kであるため、記憶域の制限範囲は8TBから128TBです。
たとえば、データベース・ブロック・サイズが32KBの場合に、8KBの非標準のブロック・サイズの表領域を作成するとします。また、LOB列のある表を作成し、CHUNKサイズを16KB(表領域ブロック・サイズ8KBの倍数)に指定するとします。その場合、この列のLOBの最大サイズは、(4GB-1)×16KBです。
この記憶域制限は、サイズがTBのLOBをサポートしている環境では、すべてのLOB型に適用されます。ただし、CLOB
型とNCLOB
型のサイズは文字単位で、BLOB
型のサイズはバイト単位であることに注意してください。
14.5.4 DBMS_LOBパッケージでのサイズがTBのLOBの使用
DBMS_LOB PL/SQLパッケージに含まれるどのAPIでも、サイズがTBのLOBにアクセスできます。
LOBの読取りおよび書込みに使用される値を取得するには、DBMS_LOB.GETCHUNKSIZE
を使用します。チャンクに格納されるバイト数は、内部記憶域のオーバヘッドにより、実際にはCHUNK
パラメータのサイズより小さくなります。DBMS_LOB.GET_STORAGE_LIMIT
ファンクションは、データベース構成の記憶域制限を戻します。これは、LOBの最大許容記憶域の値です。BLOB
のサイズはバイト単位で、CLOB
およびNCLOB
のサイズは文字単位であることに注意してください。
関連項目:
データベース・インストレーションにあわせた初期化パラメータ設定の詳細は、Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンスを参照してください。
14.5.5 OCIでのサイズがTBのLOBの使用
Oracle Call Interface APIには、すべてのサイズのLOBを操作するための関数セットが用意されています。
OCILobGetChunkSize()
は、BLOB
の場合はバイト単位で、CLOB
の場合は文字単位で値を戻します。可変幅文字セットの場合、値は幅に応じたUnicode文字数となります。チャンクに格納されるバイト数は、内部記憶域のオーバヘッドにより、実際にはCHUNK
パラメータのサイズより小さくなります。OCILobGetStorageLimit()
関数は、現在のデータベース・インストレーションにおける内部LOBの最大許容サイズをバイト単位で戻します。LOB全体が読み取られるストリーム・モードが使用される場合は、チャンク・サイズを取得する必要はありません。
関連項目:
LOBをサポートするOCI関数の詳細は、Oracle Call Interfaceプログラマーズ・ガイドを参照してください
14.6 サイズがGBのLOB作成時のガイドライン
サポートされている環境でサイズがGBのLOBを作成するには、次のガイドラインに従って、LOB記憶域用の表領域で使用可能な領域をすべて使用します。
-
単一のデータ・ファイル・サイズの制限:
オペレーティング・システムごとに単一のデータ・ファイルのサイズに関する制限があります。たとえば、Solaris 2.5では、オペレーティング・システム・ファイルは2GB以下に制限されています。このため、Oracle Databaseが実行されているオペレーティング・システムのファイルの最大許容ファイル・サイズよりもLOBが大きくなった場合、表領域にデータファイルを追加する必要があります。
-
PCT INCREASEパラメータをゼロに設定:
LOB記憶域句の
PCTINCREASE
パラメータは、新規エクステント・サイズの増加率を指定します。LOBが表領域内にピース単位で格納されていくとき、そのプロセスで多数の新しいエクステントが作成されます。エクステント・サイズが毎回デフォルト値である50%ずつ増加し続けると、エクステントが管理不可能になり、最終的に表領域内の領域が無駄になります。そのため、PCTINCREASE
パラメータを0(ゼロ)または小さい値に設定する必要があります。 -
MAXEXTENTSを適切な値またはUNLIMITEDに設定:
MAXEXTENTS
パラメータは、LOB列で許可されるエクステント数を制限します。LOBのサイズが大きくなると、多数のエクステントが段階的に作成されます。そのため、このパラメータは、その列のすべてのLOBを保持できる十分大きい値に設定する必要があります。または、UNLIMITED
に設定できます。 -
大きいエクステント・サイズを使用:
新しいエクステントが作成されるたびに、そのエクステントに対してヘッダーとその他のメタデータのUNDO情報が生成されます。エクステントの数が多い場合、ロールバック・セグメントが容量オーバーになることがあります。これを避けるには、大きなエクステント・サイズ(100MBなど)を選択してエクステント作成の頻度を削減するか、またはより頻繁にトランザクションをコミットして、ロールバック・セグメントの領域を再利用します。
14.6.1 サイズがGBのLOBを格納する表領域および表の作成
次の例に、サイズがGBのLOBを格納する表領域と表の作成方法を示します。
CREATE TABLESPACE lobtbs1 DATAFILE '/your/own/data/directory/lobtbs_1.dat' SIZE 2000M REUSE ONLINE NOLOGGING DEFAULT STORAGE (MAXEXTENTS UNLIMITED); ALTER TABLESPACE lobtbs1 ADD DATAFILE '/your/own/data/directory/lobtbs_2.dat' SIZE 2000M REUSE; CREATE TABLE print_media_backup (product_id NUMBER(6), ad_id NUMBER(6), ad_composite BLOB, ad_sourcetext CLOB, ad_finaltext CLOB, ad_fltextn NCLOB, ad_textdocs_ntab textdoc_tab, ad_photo BLOB, ad_graphic BLOB, ad_header adheader_typ) NESTED TABLE ad_textdocs_ntab STORE AS textdocs_nestedtab5 LOB(ad_sourcetext) STORE AS (TABLESPACE lobtbs1 CHUNK 32768 PCTVERSION 0 NOCACHE NOLOGGING STORAGE(INITIAL 100M NEXT 100M MAXEXTENTS UNLIMITED PCTINCREASE 0));
この例では、次のことに注意してください。
-
この例では、
CREATE TABLESPACE
文にSTORAGE句が指定されています。 -
STORAGE句は、
CREATE TABLE
文に指定することも可能です。 -
CREATE TEMPORARY TABLESPACE
文にはSTORAGE句を指定できません。 -
サイズがGBのLOBの場合は、
PCTINCREASE
パラメータを0 (ゼロ)に設定することをお薦めします。サイズが小さいか中程度のLOBの場合は、エクステント割当て数を減らすために、PCTINCREASE
のデフォルト値である50に設定することをお薦めします。