Mr.Tです、こんにちは。
前回(http://blogs.wankuma.com/mrt/archive/2007/10/12/101615.aspx)の続きです。
トランザクションは、一連の流れにある複数の処理をまとめて管理するわけで、最終的な結果が、成功か
失敗かで、一貫性を保つことができます。
じゃあ、具体的に、データ更新中にアクセスしたら、どうなるん?というのは、やはり皆さん知ってるとは思います。
それを具体的にチョー簡単に、試してみましょう。
まずは、クエリのウィンドウ(Aとします)を開いて、次のようにSelect実行してみました。
これに対して、もう一つクエリのウィンドウ(Bとします)を追加してみます。
追加したら、トランザクションをかけて、Update文を書き、その結果を見てみます。
きちんと更新されています。この状態のまま、ウィンドウAに戻り、再度Selectを実行してみます。
さて、何も出てきません。どーなってんの?なんで?とか、最初は思いますが、
この状態では、いくら待っても表示が出てこないので、あきらめてクエリのキャンセルをします。
どうして出てこないのか?それの答えは、ロックにあります。
ロックは、Lockです。鍵のロックと同じですね。
SET TRANSACTION ISOLATION LEVEL:
http://msdn2.microsoft.com/ja-jp/library/ms173763.aspx
READ COMMITTED 他のトランザクションで変更されたが、まだコミットされていないデータを、ステートメントで読み取れないように指定します。これにより、ダーティ リードを防ぐことができます。現在のトランザクション内にある各ステートメント間では、他のトランザクションによるデータの変更が可能です。この結果、反復不能読み取りやファントム データが発生することがあります。これは SQL Server の既定のオプションです。
※引用中の赤色は、私がつけたもので、原文は黒色です。
きちんと書いてありますね。コミットされていないデータを読み取るのは、既定でできないようになっています。
利用状況モニタで見ると、プロセスIDの53番が、55番をブロックしていることがわかります。ちなみに、LCK_M_Sというのは共有ロックを
示しています。(共有ロックってどんなん?ってのはここじゃ触れません)
とりあえず、ブロックしてるよ、というのがきちんとわかればいいわけですね。
決して、SQLServerが壊れたわけではないのです。
でも、読み取れるようにしたいのが人情。まあ、変更されたデータでなくとも、変更前の状態でもいいや、となれば
SQLSrever2005では、スナップショット分離というのができるようになっています。
スナップショットとは、ここでは、その時の状態を保存する、くらいの意味ですね。
いったんウィンドウBで、前の文をコメントアウトして、Rollback Tranしてください。
で、次のようにします。
最初に、スナップショット分離レベルでやるよー宣言してから、トランザクションをかけます。
で、ウィンドウAで、スナップショット分離でみるよーと指定して、再度Selectを実行してやると、
きちんと、表示できました。
で、ウィンドウBで、再度、全文コメントアウトしてから、RollBack tranしてください。
実は、もう一つ方法があります。トランザクションは、そのセッション中でトークン(鍵のようなものですね)を持っていて、そのトークンを
知ることで、トランザクションに参加することが出来ます。
ただし、この方法は、推奨されていません。
http://technet.microsoft.com/ja-jp/library/ms174403.aspx
この機能は、Microsoft SQL Server の将来のバージョンで削除されます。新規の開発作業ではこの機能を使用しないようにし、現在この機能を使用しているアプリケーションは修正することを検討してください。代わりに、複数のアクティブな結果セット (MARS) または分散トランザクションを使用してください。詳細については、「複数のアクティブな結果セット (MARS) の使用」または「分散トランザクション (データベース エンジン)」を参照してください。
削除されます、と書いてあるくらいに強いわけなので、参考程度ですね。SQLServer2000でも利用はできるはずです。
最初のSelectで表示された、MCE...という文字列をコピーして、ウィンドウAに次のようにして指定します。
はい、同じようにできましたね。
最後に、プログラムで複数のアクセスがあった場合には、さっきみたいにロックされているとずっと待ってしまうのか?
という点ですが、ADOなどを使う時は、特に指定しないならば、CommandTimeoutでタイムアウトして制御を戻してくれます。
そのため、プログラムがフリーズ状態になってしまう、という現象にはなりません。
#トランザクションがかかっているなら、ちょっと待ってリトライしようよ、ということをしたくなるのですが...