本稿はデータベースソフトウェア「SQL Anywhere」およびデータベース全般に関する英語ドキュメントを翻訳する形で提供しています。図など、部分的に英語のままになっていますが、製品のSQL Anywhere自体は完全に日本語化されていますのでご安心ください。
※編集部注:掲載しているブログ内容は執筆時点での情報のため、現在とは異なる場合があります。
ISO/ANSI SQL独立性レベル3(SERIALIZABLE
)で実行されるアプリケーションはほとんどありません。実際のところ、SQL Anywhereのデフォルトの独立性レベルは0(READ UNCOMMITTED
)で、JDBCアプリケーションに限り、デフォルトはREAD COMMITTED
となっています。
SQL AnywhereのREAD UNCOMMITTED
独立性レベルでは、操作中にスキーマロックとローの書き込みロックのみがトランザクションによって取得され、ローの読み込みロックはまったく取得されません。したがって、READ UNCOMMITTED
では、書き込みトランザクションは読み込みトランザクションをブロックしません。しかしその一方、SQL AnywhereはREAD UNCOMMITTED
独立性レベルでのセマンティクスを保証していません。俗な言葉で言うなれば、支払っただけのものしか手に入らない、ということです。多くのアプリケーションでは、コミットされていないローの危険性や影響は限られているため、時として、READ UNCOMMITTED
の本当の意味が正しく理解されていない場合があります。本稿では、その影響がよりはっきりとする例を示してみたいと思います。
セットレベルのUPDATE操作
1997年頃のSQL Anywhereバージョン5.5リリースで、セットレベルUPDATE
文のフルサポートが導入され、当時の最新SQL標準であったISO SQL/1992標準のサポートにおいて、テーブルのPRIMARY KEY
、UNIQUE
制約の一部、またはユニークインデックスの一部であるカラムを変更することができました。説明のために、次のようなテーブルを想定してみます。
CREATE TABLE updkey ( a INTEGER PRIMARY KEY, b INTEGER UNIQUE, c VARCHAR(500) )
ここで、次のINSERT
文を使用してデータを挿入してみます。
INSERT INTO updkey(a,b,c) SELECT row_num, row_num, 'test string' FROM rowgenerator WHERE row_num <= 10
この例では、単一のアトミック文を使用して10個のローすべての「b」値を再番号付けしたいと思います。これは、以下で実行することができます。
UPDATE updkey SET b = 11-b, c = 'New value'
おわかりと思いますが、このUPDATE
文をロー単位で実行することはできません。updkeyテーブル内のどのローを更新しても、カラム「b」の一意性制約に即座に違反するからです(ちなみに、ここでWAIT_ON_COMMIT
接続オプションを使用すればうまくいくと思うかもしれませんが、WAIT_ON_COMMIT
は参照整合性制約のみに作用するもので、一意性制約には影響しません)。したがって、SQL Anywhereバージョン5.5にはこのような更新を実行するために別のメカニズムが用意されています。詳しくはこれから見ていきますが、このメカニズムは同時制御の下位レベルに影響を与えます。
HOLD一時テーブル
SQL AnywhereサーバーがUPDATE
またはMERGE
文を処理する際に、プライマリーキー、ユニークインデックス、または一意性制約で一意性制約違反が発生した場合、サーバーは自動的に名前のない「HOLD(保留)」一時テーブルを作成して、問題のあるローを一時的に格納します。この一時テーブルには、ローの変更前と変更後の両方の値が含まれているので、AFTER
ローおよびAFTER
文トリガーが正常に機能できます。ローの処理は、ロー単位で次のように実行されます。
- 一意性制約違反なしでローを変更できる場合は、更新は通常通り処理されます。
- 変更によって一意性制約の違反が発生した場合は、次のようにします。
- ローの内容がその新しい値とともに保留一時テーブルにコピーされます。
-
ローは、そのインデックスエントリとともに、「一時的に」ベーステーブルから削除されます。この一時的な削除では
DELETE
トリガーは起動しません。 -
該当する
AFTER
ロートリガーがこのローに対して起動します。
すべてのローが処理された後で、保留一時テーブルにコピーされた削除済みローが、UPDATE
またはMERGE
文による変更値とともにベーステーブルに再挿入されます。保留一時テーブルからのローの処理順序は保証されていません。保存されたローの再挿入で一意性違反が''まだ''発生する場合は、UPDATE
文またはMERGE
文全体がロールバックされて、一意性制約違反がアプリケーションにレポートされます。
すべてのロー変更が正常に実行された場合のみ、要求に応じてAFTER
文トリガーが起動されます。
影響
INSERT
またはMERGE
文の実行中にローを削除すると、以下の結果に影響する可能性があります。
-
このアクションを開始した
UPDATE
文またはMERGE
文に対して起動されたAFTER
ロートリガー内で発行される、同じテーブルを問い合わせるSQL文 -
SERIALIZABLE
またはSNAPSHOT
独立性レベルで実行していないその他の接続(イベントハンドラを含む)
セットレベル更新操作におけるこの処理のセマンティクスは、直感的には理解しにくいものです。というのも、表面的に考えれば、テーブルを同時に問い合わせている別の接続は、少なくとも古いロー値と新しいロー値のどちらかを「見る」だろうと思われるからです。しかし、一意性制約のあるテーブルでのセットレベルの更新操作では、使用されている独立性レベルによっては、別の接続が特定のローを''まったく見ない''可能性もあります。別の接続がSERIALIZABLE
独立性レベルで実行中の場合は、更新を実行しようとするトランザクションがCOMMIT
またはROLLBACK
を発行するまではブロックされます。一方、別の接続がSNAPSHOT
独立性レベルで実行中の場合、そのトランザクションは、トランザクションの期間中、変更されたローの元値を見ることになります。
この複雑多岐な動作は、今まで文書化されていませんでした。これについては、次回のSQL Anywhereのメジャーリリースで、標準文書内に掲載する予定です。