13.1 読取り一貫性のあるロケータ
Oracle DatabaseのLOBに対する読取り一貫性メカニズムは、その他すべてのスカラー量に対するデータベースの読取りおよび更新のための読取り一貫性メカニズムと同じです。
LOBロケータの場合、読取り一貫性にはいくつかの特別な用途があるため、正しく理解しておく必要があります。次の各項では、読取り一貫性について説明し、互いに関連付けながら参照する必要がある例を示します。
- 読取り一貫性のあるロケータになる、SELECTされたロケータ
読取り一貫性のあるロケータには、SELECT操作の時点のスナップショット環境が含まれます。 - LOBの更新および読取り一貫性の例
読取り一貫性のあるロケータでは、SELECTの発生時期に関係なく、同じLOB値が提供されます。次の例は、読取り一貫性とUPDATE操作の関係を示しています。 - 更新済ロケータを介したLOB更新の例
この項では、ロケータを介したLOB更新について説明します。 - SQL DMLおよびDBMS_LOBを使用したLOBの更新例
次の例では、print_media表を使用して、CLOBロケータをclob_selectedとして作成します。 - 同じLOB値を更新するために1つのロケータを使用する例
特定のLOB値の更新にロケータを1つのみ使用する場合は、多くの問題を回避できます。この項では、その詳細について学習します。 - PL/SQL (DBMS_LOB)バインド変数を使用したLOBの更新の例
この項では、PL/SQLバインド変数を使用したLOBの更新について説明します。 - ロケータを使用したLOBの削除の例
この項では、PL/SQLバインド変数を使用したLOBの削除について説明します。 - 読取り一貫性の保証
この項のスクリプトを使用すると、NOLOGGINGまたはFILESYSTEM_LIKE_LOGGINGLOBを持つ表のホット・バックアップを取得でき、読取り非一貫性なしでリカバリ・ポイントが既知であることを確認できます。
関連項目:
-
読取り一貫性の一般的な情報は、Oracle Database概要を参照してください
親トピック: 高度な設計時の考慮事項
13.1.1 読取り一貫性のあるロケータになる、SELECTされたロケータ
読取り一貫性のあるロケータには、SELECT操作の実行時点のスナップショット環境が含まれます。
FOR UPDATE句の有無にかかわらず、SELECTされたロケータは読取り一貫性のあるロケータとなり、LOB値がそのロケータによって更新されるまでは読取り一貫性のあるロケータとして存在します。
これには複雑な意味があります。たとえば、SELECT操作によって読取り一貫性のあるロケータ(L1)を作成したとします。L1によって永続LOB値を読み取るときは、次のことに注意してください。
-
SELECT文にFORUPDATEが含まれていても、SELECT文実行時点のLOBが読み取られます。 -
同じトランザクションの別のロケータ(
L2)によってLOB値が更新されても、L1ではL2の更新が認識されません。 -
L1では、別のトランザクションによってLOBにコミットされた更新も認識されません。 -
読取り一貫性のあるロケータ
L1が別のロケータL2にコピーされた場合(たとえば、2つのロケータ変数のPL/SQL割当てL2:= L1によって)、L2はL1と同様に読取り一貫性のあるロケータとなり、読み取られるデータはL1に対するSELECT実行時点のデータとなります。
複数のロケータが存在していることを利用して、変化するLOB値のそれぞれの値にアクセスできます。ただし、その場合は、どのロケータでどの値にアクセスしているかを常に理解しておく必要があります。
親トピック: 読取り一貫性のあるロケータ
13.1.2 LOBの更新および読取り一貫性の例
読取り一貫性のあるロケータでは、SELECT実行時期に関係なく同じLOB値が提供されます。次の例は、読取り一貫性とUPDATE操作の関係を示しています。
print_media表とPL/SQLを使用して、clob_selected、clob_updateおよびclob_copiedの3つのCLOBインスタンスをロケータとして作成します。
次のコード例では、t1からt6が次のように処理されます。
-
最初の
SELECTINTOの実行時(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と同じ値が引き続き参照されることを示します。
例13-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;
/親トピック: 読取り一貫性のあるロケータ
13.1.3 更新済ロケータを介したLOB更新の例
この項では、ロケータを介した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値を参照するロケータを再選択することになります。
親トピック: 読取り一貫性のあるロケータ
13.1.4 SQL DMLおよびDBMS_LOBを使用したLOBの更新例
次の例では、print_media表を使用し、CLOBロケータがclob_selectedとして作成されます。
この例では、t1からt3が次のように処理されます。
-
最初の
SELECTINTOの実行時(t1)に、ad_sourcetext内の値がロケータclob_selectedに対応付けられます。 -
次の操作(
t2)では、ロケータclob_selectedに影響を与えずに、SQLUPDATE文を介して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;
/親トピック: 読取り一貫性のあるロケータ
13.1.5 同じLOB値を更新するために1つのロケータを使用する例
特定のLOB値の更新にはロケータを1つのみ使用することで、多くの問題を回避できます。この項では、その詳細について学習します。
次の例では、表print_mediaを使用し、clob_updatedおよびclob_copiedの2つのCLOBをロケータとして作成します。
この例では、t1からt5が次のように処理されます。
-
最初の
SELECTINTOの実行時(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;
/親トピック: 読取り一貫性のあるロケータ
13.1.6 PL/SQL(DBMS_LOB)バインド変数を使用したLOBの更新の例
この項では、PL/SQLバインド変数を使用した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が次のように処理されます。
-
最初の
SELECTINTOの実行時(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;
/親トピック: 読取り一貫性のあるロケータ
13.1.7 ロケータを使用したLOBの削除の例
この項では、PL/SQLバインド変数を使用したLOBの削除について学習します。
次の例では、特定の時点で選択されたロケータを介したLOBコンテンツを、同じトランザクション内でそのLOBが削除されている場合でも使用可能であることを示します。
次の例では、print_media表を使用し、clob_selectedおよびclob_copiedという2つのCLOBをロケータとして作成します。
この例では、t1からt3が次のように処理されます。
- 最初の
SELECTINTOの実行時(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;
/親トピック: 読取り一貫性のあるロケータ
13.1.8 読取り一貫性の保証
この項のスクリプトを使用すると、NOLOGGINGまたはFILESYSTEM_LIKE_LOGGING LOBを持つ表のホット・バックアップを取得でき、読取り非一貫性なしでリカバリ・ポイントが既知であることを確認できます。
ALTER DATABASE FORCE LOGGING; SELECT CHECKPOINT_CHANGE# FROM V$DATABASE; --Start SCN
SCN(システム変更番号)は、トランザクションがコミットされた時点のデータベースのバージョンを定義するスタンプです。
バックアップを実行します。
次のスクリプトを実行します。
ALTER SYSTEM CHECKPOINT GLOBAL; SELECT CHECKPOINT_CHANGE# FROM V$DATABASE; --End SCN ALTER DATABASE NO FORCE LOGGING;
データベースで生成されたアーカイブ・ログをバックアップします。少なくとも、開始SCNと終了SCN(両方のSCN時点を含む)の間のアーカイブ・ログはバックアップする必要があります。
読取り非一貫性のない時点までリストアするため、不完全なリカバリの時点として終了SCNまでリストアします。終了SCNより後のSCNにリカバリを実行すると、NOLOGGING LOBで読取り非一貫性が発生する可能性があります。
SecureFilesでは、メディア・リカバリ中に読取り非一貫性が検出されると、データベースにより一貫性のないブロックがホールとして処理され、BLOBに0が、CLOBに充填文字が格納されます。
親トピック: 読取り一貫性のあるロケータ