更新可能なカーソルを使用する場合は、更新内容の消失から保護する必要があります。更新内容の消失は、2 つ以上のトランザクションが同じローを更新して、どのトランザクションも別のトランザクションによって変更されたことに気付かず、2 番目の変更が最初の変更内容を上書きしてしまう場合に生じます。このような問題について、次の例で説明します。
アプリケーションが、次のようなサンプルデータベースに対するクエリについてカーソルを開く。
SELECT ID, Quantity FROM Products; |
ID | Quantity |
---|---|
300 | 28 |
301 | 54 |
302 | 75 |
... | ... |
アプリケーションが、カーソルを介して ID = 300 のローをフェッチする。
次の文を使用して別のトランザクションがローを更新する。
UPDATE Products SET Quantity = Quantity - 10 WHERE ID = 300; |
アプリケーションが、カーソルを使用してローを (Quantity - 5)
の値に更新する。
最終的な正しいロー値は 13 になります。カーソルによってローがプリフェッチされていた場合は、そのローの新しい値は 23 になります。別のトランザクションが更新した内容は失われます。
データベースアプリケーションでは、前もって値の検証を行わずにローの内容を変更すると、どの独立性レベルにおいても更新内容が消失する可能性があります。より高い独立性レベル (2 と 3) では、ロック (読み込み、意図的、書き込みロック) を使用して、アプリケーションでいったん読み込まれたローの内容を別のトランザクションが変更できないように設定できます。一方、独立性レベル 0 と 1 では、更新内容が消失する可能性が高くなります。独立性レベルが 0 の場合、データがその後変更されることを防ぐための読み込みロックは取得されません。独立性レベルが 1 の場合は、現在のローだけがロックされます。スナップショットアイソレーションを使用している場合、更新内容の消失は起こりません。これは、古い値を変更しようとすると必ず更新の競合が発生するからです。さらに、独立性レベル 1 でプリフェッチを使用した場合も更新内容が消失する可能性があります。これは、アプリケーションが位置設定されている結果セットロー (クライアントのプリフェッチバッファー内) は、サーバーがカーソル内で位置設定されている現在のローとは異なる場合があるためです。
独立性レベルが 1 の場合にカーソルで更新内容が消失されるのを防ぐため、データベースサーバーは、アプリケーションで指定可能な 3 種類の同時制御メカニズムをサポートしています。
ローをフェッチするときに、カーソルの各ローに対する意図的ローロックの取得。意図的ロックを取得することで、他のトランザクションが同じローに対して意図的ロックや書き込みロックを取得できないようにし、同時更新の発生を防ぎます。ただし、意図的ロックでは読み込みローロックをブロックしないため、読み込み専用文の同時実行性には影響しません。
value-sensitive カーソルの使用。value-sensitive カーソルを使用して、基本となるローに対する変更や削除を追跡できるため、アプリケーションはそれに応じて応答できます。
FETCH FOR UPDATE の使用。特定のローに対する意図的ローロックを取得します。
これらのメカニズムの指定方法は、アプリケーションで使用されるインターフェイスによって異なります。SELECT 文に関する最初の 2 つのメカニズムについては、次のようになります。
ODBC では、アプリケーションで更新可能なカーソルを宣言するときに SQLSetStmtAttr 関数でカーソル同時実行性パラメーターを指定する必要があるため、更新内容の消失は発生しません。このパラメーターは、SQL_CONCUR_LOCK、SQL_CONCUR_VALUES、SQL_CONCUR_READ_ONLY、SQL_CONCUR_TIMESTAMP のいずれかです。SQL_CONCUR_LOCK を指定すると、データベースサーバーはローに対する意図的ロックを取得します。SQL_CONCUR_VALUES と SQL_CONCUR_TIMESTAMP の場合は、value-sensitive カーソルが使用されます。SQL_CONCUR_READ_ONLY はデフォルトのパラメーターで、読み込み専用カーソルに使用されます。
JDBC では、文の同時実行性設定は ODBC の場合と似ています。SQL Anywhere JDBC ドライバーでは、JDBC 同時実行性の値として RESULTSET_CONCUR_READ_ONLY と RESULTSET_CONCUR_UPDATABLE がサポートされています。最初の値は ODBC の同時実行性設定 SQL_CONCUR_READ_ONLY に対応し、読み込み専用文を指定します。2 番目の値は、ODBC の SQL_CONCUR_LOCK 設定に対応し、更新内容の消失を防ぐためにローの意図的ロックが使用されます。value-sensitive カーソルは、JDBC 3.0 または 4.0 仕様では直接指定できません。
jConnect では、更新可能なカーソルは API レベルではサポートされますが、(TDS を使用する) 基本の実装ではカーソルを使用した更新はサポートされていません。その代わりに、jConnect では個別の UPDATE 文をデータベースサーバーに送信して、特定のローを更新します。更新内容が失われないようにするには、アプリケーションを独立性レベル 2 以上で実行してください。アプリケーションはカーソルから個別の UPDATE 文を発行できますが、UPDATE 文の WHERE 句で条件を指定してローを読み込んだ後でロー値が変更されていないことを UPDATE 文で必ず確認するようにしてください。
Embedded SQL では、同時実行性の指定は SELECT 文自体またはカーソル宣言に構文を含めることで設定できます。SELECT 文で構文 SELECT...FOR UPDATE BY LOCK を使用すると、データベースサーバーは結果セットに対する意図的ローロックを取得します。
または、SELECT...FOR UPDATE BY [ VALUES | TIMESTAMP ] を使用すると、データベースサーバーはカーソルタイプを value-sensitive に変更するため、そのカーソルを使用して特定のローを最後に読み込んだ後でローが変更された場合、アプリケーションには FETCH 文に対する警告 (SQLE_ROW_UPDATED_WARNING)、または UPDATE WHERE CURRENT OF 文に対するエラー (SQLE_ROW_UPDATED_SINCE_READ) のいずれかが返されます。ローが削除されている場合も、アプリケーションにはエラー (SQLE_NO_CURRENT_ROW) が返されます。
FETCH FOR UPDATE 機能は Embedded SQL と ODBC インターフェイスでもサポートされていますが、詳細は使用している API によって異なります。
Embedded SQL の場合、アプリケーションは FETCH の代わりに FETCH FOR UPDATE を使用してローに対する意図的ロックを取得します。ODBC の場合、アプリケーションは API 呼び出しの SQLSetPos を使用し、オペレーション引数 SQL_POSITION または SQL_REFRESH とロックタイプ引数 SQL_LOCK_EXCLUSIVE を指定して、ローに対する意図的ロックを取得します。SQL Anywhere の場合、このロックは、トランザクションがコミットまたはロールバックされるまで保持される長時間のロックです。
![]() |
DocCommentXchange で意見交換できます
|
Copyright © 2012, iAnywhere Solutions, Inc. - SQL Anywhere 12.0.1 |