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ロケータの場合、読取り一貫性にはいくつかの特別な用途があるため、正しく理解しておく必要があります。次の各項では、読取り一貫性について説明し、互いに関連付けながら参照する必要がある例を示します。

関連項目:

内容は次のとおりです。

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によって)、L2L1と同様に読取り一貫性のあるロケータとなり、読み取られるデータはL1に対するSELECT実行時点のデータとなります。

複数のロケータが存在していることを利用して、変化するLOB値のそれぞれの値にアクセスできます。ただし、その場合は、どのロケータでどの値にアクセスしているかを常に理解しておく必要があります。

14.2.2 LOBの更新および読取り一貫性の例

読取り一貫性のあるロケータでは、SELECT実行時期に関係なく同じLOB値が提供されます。

次に、読取り一貫性と更新の関係を簡単な例で示します。LOBの例の表: PMスキーマのprint_media表で説明されているprint_media表とPL/SQLを使用して、clob_selectedclob_updateおよびclob_copiedの3つのCLOBインスタンスをロケータとして作成します。

次のコード例では、t1からt6が次のように処理されます。

  • 最初のSELECT INTOの実行時(t1)に、ad_sourcetext内の値がロケータclob_selectedに対応付けられます。

  • 次の操作(t2)で、ad_sourcetext内の値がロケータclob_updatedに対応付けられます。t1t2の間では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値を参照するロケータを再選択することになります。

ノート:

どのような手段でもLOBロケータを選択すればロケータから読み取れますが、書き込めません。

SQLを介したLOB値の更新は、単なるUPDATE文です。UPDATE文によって行われた変更をロケータが認識できるように、LOBロケータを再選択するか、UPDATE文でRETURNING句を使用するかは、ユーザーが決定します。LOBロケータを再選択するかRETURNING句を使用しないかぎり、最新の値を読み取っていない場合でも最新の値を読み取っていると判断する場合があります。このような理由により、SQL DMLをOCIおよびDBMS_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値を参照します。つまり、ロケータはSQL UPDATE文によって実行された更新を認識しません。例では、後続のDBMS_LOB.READ()コールがこれを示しています。

  • 3つ目の操作(t3)では、LOB値が再選択されロケータclob_selectedに挿入されます。これによって、ロケータは最新のスナップショット環境に更新され、先のSQL UPDATE文によって行われた変更を認識できるようになります。このため、次の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つのロケータを使用する例

ノート:

異なるロケータを使用して同じLOBを更新しないでください。特定の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_updatedclob_copiedに割り当てられた時点(t5)で初めて、clob_copiedclob_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)が読取り一貫性のあるロケータの場合、L1SELECT実行時点の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_copiedclob_updatedによる変更を参照しないことを示します。

  • このため(t5の時点で)、clob_copiedINSERT文の値のソースとして使用する場合、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データがLOB clob_copiedにコピーされます。clob_selectedおよびclob_copiedを介した値のDBMS_LOB.READ()はこれと同じになり、clob_selectedSELECTの実行時と同じ値を参照します。
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ロケータとトランザクション境界

LOBロケータは、トランザクションおよびトランザクションIDで使用できます。

関連項目:

LOBロケータの詳細は、「LOBロケータとBFILEロケータ」を参照してください

内容は次のとおりです。

14.3.1 LOBロケータとトランザクション境界について

LOBロケータとトランザクションについては、次のことに注意してください。

  • ロケータがトランザクションIDを含む場合

    トランザクションを開始してからロケータを選択する場合: トランザクションを開始した後にロケータを選択すると、ロケータにトランザクション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:

  1. 現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。

  2. トランザクションを開始します。

  3. ロケータを使用してLOBからデータを読み取ります。

  4. トランザクションをコミットまたはロールバックします。

  5. ロケータを使用してLOBからデータを読み取ります。

  6. トランザクションを開始します。ロケータはトランザクションIDを含みません。

  7. ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。

シナリオ2:

  1. 現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。
  2. トランザクションを開始します。ロケータはトランザクションIDを含みません。
  3. ロケータを使用してLOBからデータを読み取ります。ロケータはトランザクションIDを含みません。
  4. ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。続けてLOBの読取りまたは書込みを行うことができます。
  5. トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
  6. ロケータを使用してLOBからデータを読み取ります。この操作は有効です。
  7. トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。
  8. ロケータを使用してデータをLOBに書き込みます。ロケータが現行トランザクションに一致するトランザクションIDを含まないため、この書込み操作は失敗します。

14.3.4 トランザクション境界内でのロケータの選択

2つの使用例により、トランザクション内でロケータが選択された場合に、シリアライズ可能でないトランザクションでロケータを使用する方法について説明します。

シナリオ1:

  1. トランザクションの中でロケータを選択します。この時点では、ロケータはトランザクションIDを含んでいます。

  2. トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。

  3. ロケータを使用してLOBからデータを読み取ります。ロケータの中のトランザクションIDは現在のトランザクションに一致していませんが、この操作は有効です。

    関連項目:

    ロケータを使用したLOBデータの読取りの詳細は、読取り一貫性のあるロケータを参照してください。

  4. ロケータを使用してデータをLOBに書き込みます。ロケータの中のトランザクションIDが現在のトランザクションに一致していないため、この操作は失敗します。

シナリオ2:

  1. トランザクションを開始します。
  2. ロケータを選択します。ロケータがトランザクションの中で選択されたため、トランザクションIDが含まれています。
  3. ロケータを使用してLOBの読取りまたは書込みを行います。これらの操作は有効です。
  4. トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
  5. ロケータを使用してLOBからデータを読み取ります。ロケータの中にトランザクションIDがあり、そのトランザクションはすでにコミットまたはロールバックされていますが、この操作は有効です。
  6. ロケータを使用してデータを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)属性を使用してオブジェクトを作成する場合、BFILENULLに設定されます。これは、BFILEから読み取られる前に有効なディレクトリ・オブジェクト名およびファイル名を使用して更新する必要があります。

14.5 サイズがTBのLOBのサポート

TサイズがTBのLOBは、データベース・ブロック・サイズに応じて最大サイズが8TBから128TBになる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です。

関連項目:

  • データベースのインストールにあわせた初期化パラメータ設定の詳細は、『Oracle Database管理者ガイド』を参照してください

  • CHUNK

この記憶域制限は、サイズがTBのLOBをサポートしている環境では、すべてのLOB型に適用されます。ただし、CLOB型とNCLOB型のサイズは文字単位で、BLOB型のサイズはバイト単位であることに注意してください。

14.5.3 JDBCでのサイズがTBのLOBの使用

Oracle JDBCクラスに含まれるLOB APIを使用して、サイズがTBのLOBにアクセスできます。

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に設定することをお薦めします。