ヘッダーをスキップ
Oracle® Database Oracleプリコンパイラのためのプログラマーズ・ガイド
11g リリース2(11.2)
B61344-01
  目次
目次
索引へ
索引

前へ
前へ
 
次へ
次へ
 

7 トランザクションの定義および制御

この章では、トランザクション処理の実行方法について説明します。Oracleデータへの変更を確定するかまたは取り消すかの制御方法など、データベースの一貫性を保護する基本的な方法を学習します。内容は次のとおりです。

基本用語

トランザクションの本題に入る前に、この項で定義されている用語を理解する必要があります。

Oracleが管理するジョブまたはタスクは、セッションと呼ばれます。アプリケーション・プログラムまたはOracle Formsなどのツールを実行してOracleに接続すると、ユーザー・セッションが開始されます。Oracleでは、ユーザー・セッションを同時に機能させ、コンピュータ・リソースを共有できます。そのために、Oracleでは同時実効性、つまり多数のユーザーによる同じデータベースへのアクセスを制御する必要があります。同時実効性の制御が十分でないと、データの整合性が失われる可能性があります。つまり、データや構造への変更が誤って行われる可能性があります。

Oracleでは、ロックを使用して、データへの同時アクセスを制御します。ロックを使用すると、データの表または行などのデータベース・リソースを一時的に専有できます。その結果、そのユーザーがデータへのアクセスを終了するまで、他のユーザーはデータに変更を加えることはできません。デフォルトのロック機能がOracleのデータおよび構造を保護するため、明示的にリソースをロックする必要はありません。ただし、デフォルトのロックをオーバーライドした方が有利な場合は、表または行に対するデータ・ロックを要求できます。行の共有排他など、数種類のロック・モードから選択できます。

複数のユーザーが同じデータベース・オブジェクトへのアクセスを試みると、デッドロックが発生する可能性があります。たとえば、同じ表を更新するユーザーが2人いる場合、それぞれ相手が現在ロックしている行の更新を試みると待機状態になります。それぞれのユーザーが、相手が使用中のリソースを待つことになるため、Oracleによりデッドロックが解除されるまで、どちらも処理を続行できません。Oracleからは、最小量の作業を完了した関連トランザクションに対してエラー信号が送られ、「リソース待機の間にデッドロックが検出されました」というOracleエラー・コードがSQLCAのSQLCODEに戻されます。

1人のユーザーによって問合せが行われている表を、同時に別のユーザーが更新すると、Oracleでは問合せ用の表データの読取り一貫性ビューが生成されます。つまり、1つの問合せが開始され、処理が進む間、問合せによって読み取られるデータは変わりません。更新アクティビティが続行している間、Oracleでは、表データのスナップショットを取り、変更内容をロールバック・セグメントに記録します。Oracleは、ロールバック・セグメント内の情報を使用して、読取り一貫性のある問合せ結果を作成し、必要に応じて変更を元に戻します。

トランザクションによるデータベースの保護

Oracleはトランザクション指向です。つまり、トランザクションを使用して、データの整合性を保ちます。トランザクションとは、あるタスクを完了するために定義する1つ以上の論理的に関連付けられたSQL文です。Oracleでは、一連のSQL文を1つの単位 として扱うため、これらの文による変更はすべて、同時にコミット(確定)またはロールバック(取消し)されます。トランザクションの途中でアプリケーション・プログラムに障害が発生すると、データベースは自動的に前(トランザクションの前)の状態にリストアされます。

以降の項では、トランザクションの定義および制御方法について説明します。特に、次の方法を学習します。

この章で説明するSQL文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

トランザクションの開始および終了方法

トランザクションは、プログラムの最初の実行SQL文(CONNECT以外)により開始します。1つのトランザクションが終了すると、次の実行SQL文により別のトランザクションが自動的に開始します。このように、すべての実行文はトランザクションの一部です。宣言SQL文は、ロールバックできず、コミットも必要ないため、トランザクションの一部とはみなされません。

トランザクションは、次のいずれかの方法で終了します。

システム障害が発生した場合や、ソフトウェアの問題、ハードウェアの問題または強制割込みなどが原因で、予期しないユーザー・セッション停止が発生した場合にも、トランザクションは終了します。そのトランザクションはOracleによりロールバックされます。

トランザクションの途中でプログラムに障害が発生すると、Oracleによりエラーが検出され、トランザクションはロールバックされます。オペレーティング・システムに障害が発生すると、データベースがトランザクション前の状態にリストアされます。

COMMIT文の使用方法

データベースへの変更を確定するには、COMMIT文を使用します。変更がコミットされるまで、他のユーザーは変更されたデータにアクセスできず、表示されるのはトランザクション開始前の状態のデータです。COMMIT文は、ホスト変数の値にも、プログラムの制御フローにも影響はありません。具体的には、COMMIT文により次の処理が実行されます。

MODE={ANSI13|ORACLE}の場合、CURRENT OF句で参照されていない明示カーソルは、複数のコミットにわたりオープン状態のままです。これによりパフォーマンスが向上します。たとえば、「複数のコミットにわたるフェッチ」を参照してください。

これらは通常の処理の一部であるため、COMMIT文は、プログラムのメイン・パスにインラインで設定する必要があります。プログラムの終了前に、保留中の変更を明示的にコミットする必要があります。そうしないと、保留中の変更はロールバックされます。次の例では、トランザクションをコミットして、Oracleとの接続を切断します。

EXEC SQL COMMIT WORK RELEASE;

オプションのキーワードWORKを指定すると、ANSI互換になります。RELEASEオプションを指定すると、プログラムで使用されているOracleリソース(ロックとカーソル)がすべて解放され、データベースとの接続が切断されます。

データ定義文では、実行の前後に自動コミットが発行されるため、データ定義文の後にCOMMIT文を記述する必要はありません。したがって、文の実行に成功するか失敗するかに関係なく、前のトランザクションがコミットされます。

ROLLBACK文の使用方法

データベースに加えられ保留中の変更を取り消すには、ROLLBACK文を使用します。たとえば、表から間違った行を削除するなどのミスを犯した場合、ROLLBACKを使用すれば、元のデータをリストアできます。ROLLBACK文は、ホスト変数の値にも、プログラムのフロー制御にも影響がありません。具体的には、ROLLBACK文により次の処理が実行されます。

MODE={ANSI13|ORACLE}の場合、CURRENT OF句で参照されない明示カーソルは、複数のロールバックにわたりオープン状態のままです。

ROLLBACK文は例外処理の一部であるため、プログラムのメイン・パスではなく、エラー処理ルーチン内に指定する必要があります。次の例では、トランザクションをロールバックして、Oracleとの接続を切断します。

EXEC SQL ROLLBACK WORK RELEASE;

オプションのキーワードWORKを指定すると、ANSI互換になります。RELEASEオプションを指定すると、プログラムで使用されているすべてのリソースが解放され、データベースとの接続が切断されます。

WHENEVER SQLERROR GOTO文からエラー処理ルーチンに分岐するとき、そのルーチンにROLLBACK文が含まれている場合は、ロールバックがエラーで失敗すると、プログラムが無限ループに入るおそれがあります。このような無限ループは、ROLLBACK文の前にWHENEVER SQLERROR CONTINUEを記述することで回避できます。

次の例を考えてみます。

EXEC SQL WHENEVER SQLERROR GOTO sql_error;
FOR EACH new employee
 display 'Employee number? ';
 read emp_number;
 display 'Employee name? ';
 read emp_name;
 EXEC SQL INSERT INTO EMP (EMPNO, ENAME)
 VALUES (:emp_number, :emp_name);
ENDFOR;
...
sql_error:
 EXEC SQL WHENEVER SQLERROR CONTINUE;
 EXEC SQL ROLLBACK WORK RELEASE;
 display 'Processing error';
 exit program with an error;

プログラムが異常終了すると、Oracleによりトランザクションがロールバックされます。

文レベルのロールバック

SQL文を実行する前に、Oracleでは暗黙的なセーブ・ポイント(ユーザーには使用不可)が設定されます。その結果、この文が失敗すると、自動的にロールバックされ、SQLCA内のSQLCODEにアプリケーション・エラーが戻されます。たとえば、INSERT文で一意の索引に重複する値を挿入しようとしてエラーが発生すると、その文はロールバックされます。

失敗したSQL文により開始された作業のみが失われます。つまり、現行のトランザクション内でこの文より前に行われた作業は保存されます。したがって、データ定義文が失敗しても、それ以前の自動コミットは取り消されません。

SQL文を実行する前に、Oracleではその文を解析する、つまり、その文が構文規則に従っているか、あるいは有効なデータベース・オブジェクトを参照しているかを確認する要があります。SQL文の実行中にエラーが検出されると、ロールバックが発生しますが、解析中にエラーが検出されても、ロールバックは発生しません。

Oracleでは、デッドロックを解消するために、1つのSQL文をのみをロールバックすることもできます。関連しているトランザクションの1つにエラーが通知され、そのトランザクション内の現在の文がロールバックされます。

SAVEPOINT文の使用方法

トランザクションの処理中に現在のポイントにマークを付け、名前を指定するには、SAVEPOINT文を使用します。マークを付けた各ポイントは、セーブポイントと呼ばれます。たとえば、次の文では、start_deleteというセーブポイントを設定しています。

EXEC SQL SAVEPOINT start_delete;

セーブポイントを設定すると、長いトランザクションを分割できるため、複雑なプロシージャでもうまく制御できます。たとえば、1つのトランザクションで複数のファンクションが実行される場合、各ファンクションの前にセーブポイントを設定できます。その結果、1つのファンクションが失敗しても、簡単にOracleデータを前の状態にリストアし、リカバリしてから、ファンクションを再実行できます。

トランザクションの一部を取り消すには、セーブポイントをROLLBACK文とそのTO SAVEPOINT句とともに使用します。TO SAVEPOINT句により、現行のトランザクションの途中にある文までロールバックでき、すべての変更を取り消す必要がありません。具体的には、ROLLBACK TO SAVEPOINT文により次の処理が実行されます。

次の例では、MAIL_LIST表にアクセスして新しいリストを挿入し、古いリストを更新して、アクティブでない(少数の)リストを削除しています。削除後に、SQLCA内のSQLERRD(3)で、削除された行数を調べます。行数が予想以上に多い場合は、セーブポイントstart_deleteまでロールバックして、その削除のみを取り消します。

FOR EACH new customer
 display 'Customer number? ';
 read cust_number;
 display 'Customer name? ';
 read cust_name;
EXEC SQL INSERT INTO MAIL_LIST (CUSTNO, CNAME, STAT)
 VALUES (:cust_number, :cust_name, 'ACTIVE');
ENDFOR;
FOR EACH revised status
 display 'Customer number? ';
 read cust_number;
 display 'New status? ';
 read new_status;
 EXEC SQL UPDATE MAIL_LIST
 SET STAT = :new_status WHERE CUSTNO = :cust_number;
ENDFOR;
-- mark savepoint
EXEC SQL SAVEPOINT start_delete;
EXEC SQL DELETE FROM MAIL_LIST WHERE STAT = 'INACTIVE';
IF sqlca.sqlerrd(3) < 25 THEN -- check number of rows deleted
 display 'Number of rows deleted is ', sqlca.sqlerrd(3);
ELSE
 display 'Undoing deletion of ', sqlca.sqlerrd(3), ' rows';
 EXEC SQL WHENEVER SQLERROR GOTO sql_error;
 EXEC SQL ROLLBACK TO SAVEPOINT start_delete;
ENDIF;
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL COMMIT WORK RELEASE;
exit program;
sql_error:
 EXEC SQL WHENEVER SQLERROR CONTINUE;
 EXEC SQL ROLLBACK WORK RELEASE;
 display 'Processing error';
 exit program with an error;

ROLLBACK TO SAVEPOINT文ではRELEASEオプションを指定できないので注意してください。

あるセーブポイントまでロールバックすると、そのセーブポイント以降に設定されたセーブポイントはすべて消去されます。ただし、ロールバック先のセーブポイントは消去されません。たとえば、5つのセーブポイントを設定し、3番目のセーブポイントまでロールバックすると、4番目と5番目のセーブポイントのみが消去されます。COMMIT文またはROLLBACK文では、すべてのセーブポイントが消去されます。

デフォルトでは、ユーザー・セッションごとのアクティブなセーブポイントの数は、5つに制限されています。アクティブなセーブポイントとは、最後のコミットまたはロールバック以降に設定されたセーブポイントです。データベース管理者(DBA)は、Oracleの初期化パラメータSAVEPOINTSの値を増やして、この制限を引き上げることができます。2つのセーブポイントに同じ名前を付けると、最初のセーブポイントが消去されます。

RELEASEオプションの使用方法

プログラムが異常終了すると、Oracleにより変更が自動的にロールバックされます。異常終了が発生するのは、プログラムが作業を明示的にコミットもロールバックもせずに、RELEASEオプションを使用してOracleとの接続を切断するときです。

プログラムが正常に終了するのは、プログラムが正常に実行され、オープン状態のカーソルがクローズされ、作業が明示的にコミットまたはロールバックされて、Oracleとの接続が切断され、制御がユーザーに戻された場合です。最後に実行されるSQL文が次のいずれかの場合、プログラムは正常に終了します。

EXEC SQL COMMIT RELEASE;

または

EXEC SQL ROLLBACK RELEASE;

それ以外の場合は、ユーザー・セッションで取得したロックおよびカーソルは、そのユーザー・セッションがアクティブでなくなったことがOracleで認識されるまで、プログラムの終了後も保持されます。このため、マルチユーザー環境では、他のユーザーがロックされたリソースが解除されるまで必要以上に長く待たされることになりかねません。

SET TRANSACTION文の使用方法

SET TRANSACTION文を使用すると、読取り専用または読取り/書込みトランザクションを開始したり、現行のトランザクションを指定したロールバック・セグメントに割り当てたりできます。読取り専用トランザクションは、COMMIT文、ROLLBACK文またはデータ定義文で終了します。

読取り専用トランザクションでは反復可能読取りが行えるため、他のユーザーが更新中の1つ以上の表に対して、複数の問合せを実行する場合に便利です。読取り専用トランザクション中、複数の表と複数の問合せで構成された読取り一貫性ビューが作成され、すべての問合せがデータベースの同じスナップショットを参照します。他のユーザーは、いつもどおりデータの問合せや更新を続けられます。次にSET TRANSACTION文の例を示します。

EXEC SQL SET TRANSACTION READ ONLY;

SET TRANSACTION文は、読取り専用トランザクションの最初のSQL文であることが必要で、1つのトランザクションで1回しか使用できません。READ ONLYパラメータは必須です。これを使用しても、他のトランザクションには影響がありません。読取り専用トランザクションで使用できるのは、SELECT文(FOR UPDATEなし)、LOCK TABLE文、SET ROLE文、ALTER SESSION文、ALTER SYSTEM文、COMMIT文およびROLLBACK文のみです。

次の例では、店の管理者として、読取り専用トランザクションを使用して要約レポートを生成することで、当日、先週および先月の売り上げを調べます。トランザクションの途中で、他のユーザーがデータベースを更新しても、レポートには影響はありません。

EXEC SQL SET TRANSACTION READ ONLY; 
 EXEC SQL SELECT SUM(SALEAMT) INTO :daily FROM SALES 
 WHERE SALEDATE = SYSDATE; 
 EXEC SQL SELECT SUM(SALEAMT) INTO :weekly FROM SALES 
 WHERE SALEDATE > SYSDATE - 7; 
 EXEC SQL SELECT SUM(SALEAMT) INTO :monthly FROM SALES 
 WHERE SALEDATE > SYSDATE - 30; 
 EXEC SQL COMMIT WORK; 
-- simply ends the transaction since there are no changes 
-- to make permanent 
-- format and print report

デフォルト・ロックのオーバーライド

デフォルトでは、多数のデータ構造がOracleにより暗黙的(自動的)にロックされます。ただし、デフォルトのロックをオーバーライドした方が有利な場合は、行または表に対して特定のデータ・ロックを要求できます。明示的なロックにより、トランザクション中に表に対するアクセスを共有または制限したり、複数の表および複数の問合せの読取り一貫性を確保できます。

SELECT FOR UPDATE OF文を使用すると、表の特定行を明示的にロックすることで、更新または削除が実行されるまで、その行が変更されないようにできます。ただし、Oracleでは、更新または削除時に行レベルのロックが自動的に取得されます。したがって、更新または削除のにロックする場合にのみ、FOR UPDATE OF句を使用してください。

LOCK TABLE文を使用すると、表全体をロックできます。

FOR UPDATE OF句の使用方法

UPDATE文またはDELETE文のCURRENT OF句で参照されるカーソルをDECLAREする場合は、 FOR UPDATE OF句を使用すると行の排他的ロックを取得できます。SELECT FOR UPDATE OF文では、更新または削除の対象となる行が識別され、アクティブ・セット内の各行がロックされます。(行はすべて、オープン時にロックされ、フェッチ時にはロックされません。)これは、ある行内の既存の値に基づいて更新を行う場合に便利です。更新前に、その行が他のユーザーにより変更されないようにする必要があります。

FOR UPDATE OF句はオプションです。たとえば、次のようなコードがあるとします。

EXEC SQL DECLARE emp_cursor CURSOR FOR 
 SELECT ENAME, JOB, SAL FROM EMP WHERE DEPTNO = 20 
 FOR UPDATE OF SAL;

ここでFOR UPDATE OF句を削除すると、次のようにコードが単純になります。

EXEC SQL DECLARE emp_cursor CURSOR FOR 
 SELECT ENAME, JOB, SAL FROM EMP WHERE DEPTNO = 20;

CURRENT OF句では、必要に応じてFOR UPDATE句を追加するようにプリコンパイラに指示します。CURRENT OF句を使用して、カーソルから最後にフェッチされた行を参照します。

制限事項

FOR UPDATE OF句を使用すると、複数の表を参照できません。また、明示的なFOR UPDATE OFまたは暗黙的FOR UPDATEでは、行の排他ロックが取得されます。行ロックが解除されるのは、コミット時またはロールバック時です(セーブポイントまでロールバックする場合は、解除されません)。コミット後にFOR UPDATEカーソルからフェッチしようとすると、次のエラーが発生します。

ORA-01002: fetch out of sequence

LOCK TABLE文の使用方法

LOCK TABLE文を使用すると、指定したロック・モードで1つ以上の表をロックできます。たとえば、次の文は行共有モードでEMP表をロックします。行共有ロックにより表への同時アクセスが可能になります。つまり、他のユーザーがその表全体をロックして排他的に使用することはできません。

EXEC SQL LOCK TABLE EMP IN ROW SHARE MODE NOWAIT;

ロック・モードにより、その表に設定できる他のロックが決まります。たとえば、多くのユーザーが同時に1つの表に対して行共有ロックを取得できる一方、排他ロックを取得できるのは一度に1ユーザーのみです。1人のユーザーが表を排他ロックしている間は、他のユーザーはその表での行を挿入、更新または削除できません。ロック・モードの詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。

オプションのキーワードNOWAITを指定すると、他のユーザーが表をロックしている場合、その表のロックが解除されるまで待機しないようにOracleに指示できます。制御はただちにプログラムに戻されるため、プログラムではロックの取得を再度試みるまで、別の作業ができます。(SQLCA内のSQLCODEをチェックすれば、表のロックが失敗したかどうかを確認できます。)NOWAITを省略すると、Oracleはその表が使用可能になるまで待機状態になります。この待機時間に制限はありません。

表をロックしても、他のユーザーは表に対して問い合せができますが、問合せを実行しても表ロックを取得できません。したがって、問合せが他の問合せや更新を妨げることはなく、更新が問合せを妨げることもありません。2つの異なるトランザクションで同じ行の更新が試みられる場合にのみ、一方のトランザクションが他方のトランザクションの完了まで待機の状態になります。表のロックは、トランザクションがコミットまたはロールバックを発行すると解除されます。

複数のコミットにわたるフェッチ

複数のコミットとフェッチを併用する場合は、CURRENT OF句を使用しないでください。かわりに、各行のROWIDを選択してから、その値を使用して、更新または削除中の現在の行を識別します。次に例を示します。

EXEC SQL DECLARE emp_cursor CURSOR FOR
 SELECT ENAME, SAL, ROWID FROM EMP WHERE JOB = 'CLERK';
...
EXEC SQL OPEN emp_cursor;
EXEC SQL WHENEVER NOT FOUND GOTO ...
LOOP
 EXEC SQL FETCH emp_cursor INTO :emp_name, :salary, :row_id;
 ... 
 EXEC SQL UPDATE EMP SET SAL = :new_salary
 WHERE ROWID = :row_id;
 EXEC SQL COMMIT;
ENDLOOP;

注意:

フェッチされた行はロックされません。つまり、ある行を読み取っても、その行を更新または削除する前に別のユーザーがその行を変更すると、結果が一貫性のないものになる可能性があります。

分散トランザクションの処理

分散データベースとは、異なるノード上の複数の物理データベースで構成される1つの論理データベースです。分散型の文とは、データベース・リンクを使用してリモート・ノードにアクセスするSQL文です。分散トランザクションには、分散データベースの複数のノードでデータを更新する分散型の文が少なくとも1つ含まれています。更新の影響を受けるノードが1つのみの場合、そのトランザクションは分散型ではありません。

コミットを発行すると、分散トランザクションによる影響を受ける各データベースの変更が確定されます。コミットのかわりにロールバックを発行すると、すべての変更が取り消されます。ただし、コミットまたはロールバック中にネットワークやコンピュータで障害が発生すると、分散トランザクションの状態は不明またはインダウトになることがあります。そのような場合、FORCE TRANSACTIONシステム権限があれば、FORCE句を使用して、ローカル・データベースでトランザクションを手動でコミットまたはロールバックできます。このトランザクションは、データ・ディクショナリ・ビューDBA_2PC_PENDINGにあるトランザクションIDを引用符で囲んだリテラルで指定する必要があります。次に例を示します。

EXEC SQL COMMIT FORCE '22.31.83';
...
EXEC SQL ROLLBACK FORCE '25.33.86';

FORCEでは、指定したトランザクションのみがコミットまたはロールバックされるため、現行のトランザクションには影響がありません。インダウト・トランザクションは、手動でセーブポイントまでロールバックできません。

COMMIT文中でCOMMENT句を使用すると、分散トランザクションに関連付けるコメントを指定できます。トランザクションがインダウトになると、OracleによりCOMMENTで指定したテキストがトランザクションIDとともに、データ・ディクショナリ・ビューDBA_2PC_PENDINGに格納されます。テキストには、長さ50文字以内の引用符付きリテラルを指定する必要があります。次に例を示します。

EXEC SQL COMMIT COMMENT 'In-doubt trans; notify Order Entry';

分散トランザクションの詳細は、『Oracle Database概要』を参照してください。

ガイドライン

次のガイドラインは、いくつかの一般的な問題を回避するのに役立ちます。

アプリケーションの設計

アプリケーションの設計時には、論理的に関連するアクションを1つのトランザクションにグループ化してください。適切に設計されたトランザクションには、特定のタスクを完了するために必要なすべてのステップが過不足なく含まれています。

表内で参照するデータは、一貫した状態に保たれている必要があります。したがって、トランザクション内のSQL文は、一貫した方法でデータを変更する必要があります。たとえば、2つの銀行口座間の資金振替には、一方の口座の借方勘定ともう一方の口座の貸方勘定が含まれています。更新の成功または失敗は、両方の口座で一致する必要があります。一方の口座への新規預金など、関連のない更新はトランザクションに含めないでください。

ロックの取得

アプリケーション・プログラムにSQLのロック文が含まれている場合、ロックを要求するOracleユーザーに、そのロックの取得に必要な権限があるか確認してください。DBAは、任意の表をロックできます。その他のユーザーは、自分の所有する表か、ALTERSELECTINSERTUPDATEまたはDELETEなどの権限を付与されている表をロックできます。

PL/SQLの使用方法

PL/SQLブロックがトランザクションの一部になっている場合、そのロック内のコミットおよびロールバックは、トランザクション全体に影響を与えます。次の例では、ロールバックは更新および挿入による変更を取り消します。

EXEC SQL INSERT INTO EMP ...
EXEC SQL EXECUTE
 BEGIN UPDATE emp 
...
 ...
 EXCEPTION
 WHEN DUP_VAL_ON_INDEX THEN
 ROLLBACK;
 END;
END-EXEC;
...