none
フェッチ使用時の排他について

    質問

  • ある1つのテーブルにたいし、フェッチで1レコードづつ処理をするSQLを作成しています。

    イメージとしては以下の通りです。

    BEGIN TRANSACTION MYTRAN

    DECLARE MYCURSOR CURSOR FOR
    SELECT A,B,C
      FROM MYTABLE
      WHERE A = @PARAMETER

    OPEN MYCURSOR

    FETCH NEXT FROM MYCURSOR

    into @a,@b,@c

    WHILE @@FETCH_STATUS = 0
    BEGIN

    FETCH NEXT FROM MYCURSOR

    into @a,@b,@c

    END

    CLOSE MYCURSOR

    DEALLOCATE MYCURSOR

    COMMIT TRANSACTION MYTRAN

    ここでわからないので、排他制御の挙動です。

    以下のように考えているのですが、どれが正しいのでしょうか?

    また、別な動きをするのでしょうか?

    1.トランザクションによりテーブルロックが働き処理中にアップデートは行えない。

    2.カーソル宣言時にテーブルロックへの悲観的ロックが働き処理中にアップデートは行えない。

    3.フェッチ時にレコードへの悲観的ロックが働き処理中のレコードのみアップデートは行えない。

    以上、よろしくお願いいたします。

    2012年5月16日 1:38

回答

  • 「排他」とはどのようなものを想像されているのでしょうか?
    挙げられたコードと別の処理とが何らかの条件の競合によりどちらかもしくは両方が排他されるわけです。しかし、質問には一方の処理(コード)しか挙げられていません。

    質問の範囲で答えるなら、処理が一つしかなければ排他するべき相手がいないため「アップデートは必ずできます」になります。

    2012年5月16日 2:05
  • 1.トランザクションによりテーブルロックが働き処理中にアップデートは行えない。

    2.カーソル宣言時にテーブルロックへの悲観的ロックが働き処理中にアップデートは行えない。

    3.フェッチ時にレコードへの悲観的ロックが働き処理中のレコードのみアップデートは行えない。

    ・まず1ですが、トランザクション発行時点でテーブルロックが発生するという事はありません。
    ・次に2ですが、カーソル宣言部においてLOCK指定を行っておりませんので、更新ロック(または排他ロック)はこの時点ではありません。
      ※共有ロックはあるでしょうが。
    ・最後3ですが、フェッチ中も更新等は行っておりませんので、共有ロックが更新ロックに切り替わる事も無いです。

    実際の動きについてはエスカレーションが起きたりでロックの範囲は変わるでしょうが、
    恐らくOPEN MYCURSOR ~ COMMIT TRANSACTION MYTRANの間に、行・ページ・テーブルいずれかの共有ロックが発生すると思います。

    ちなみに余談ですが、下記の内容は少なくとも理解された方が良いと思います。
    1.カーソル宣言の種類 (例えば下記の動作の違い)
      ①DECLARE MYCURSOR CURSOR FOR
      ②DECLARE MYCURSOR CURSOR FORWARD_ONLY FOR
      ③DECLARE MYCURSOR CURSOR STATIC FOR

    2.ロックエスカレーション

    3.ロックヒント(例:FROM [テーブル名] WITH(UPDLOCK))


    2012年5月16日 2:08

すべての返信

  • 「排他」とはどのようなものを想像されているのでしょうか?
    挙げられたコードと別の処理とが何らかの条件の競合によりどちらかもしくは両方が排他されるわけです。しかし、質問には一方の処理(コード)しか挙げられていません。

    質問の範囲で答えるなら、処理が一つしかなければ排他するべき相手がいないため「アップデートは必ずできます」になります。

    2012年5月16日 2:05
  • 1.トランザクションによりテーブルロックが働き処理中にアップデートは行えない。

    2.カーソル宣言時にテーブルロックへの悲観的ロックが働き処理中にアップデートは行えない。

    3.フェッチ時にレコードへの悲観的ロックが働き処理中のレコードのみアップデートは行えない。

    ・まず1ですが、トランザクション発行時点でテーブルロックが発生するという事はありません。
    ・次に2ですが、カーソル宣言部においてLOCK指定を行っておりませんので、更新ロック(または排他ロック)はこの時点ではありません。
      ※共有ロックはあるでしょうが。
    ・最後3ですが、フェッチ中も更新等は行っておりませんので、共有ロックが更新ロックに切り替わる事も無いです。

    実際の動きについてはエスカレーションが起きたりでロックの範囲は変わるでしょうが、
    恐らくOPEN MYCURSOR ~ COMMIT TRANSACTION MYTRANの間に、行・ページ・テーブルいずれかの共有ロックが発生すると思います。

    ちなみに余談ですが、下記の内容は少なくとも理解された方が良いと思います。
    1.カーソル宣言の種類 (例えば下記の動作の違い)
      ①DECLARE MYCURSOR CURSOR FOR
      ②DECLARE MYCURSOR CURSOR FORWARD_ONLY FOR
      ③DECLARE MYCURSOR CURSOR STATIC FOR

    2.ロックエスカレーション

    3.ロックヒント(例:FROM [テーブル名] WITH(UPDLOCK))


    2012年5月16日 2:08
  • 情報が十分ではありませんでしたので、お詫びいたします。

    まずはやりたいことです。

    フェッチループ内で、MYTABLEの状態を確認し、

    MYTABLEのキーを元に当該レコードにループ内で算出した値をセットします。

    この算出は別テーブルを見ることがあるため、時間の掛かる可能性があります。

    システムがwebシステムであるため、

    この処理中にMYTABLEが更新される可能性があります。

    この際に適切な挙動をさせたいのですが、

    フェッチへの理解度が低く今回の質問に至りました。

    ロックヒントを理解していれば1はありえませんね、

    ありがとうございます。

    また、カーソルは1レコード1度読めばよいため、

    FORWARD_ONLYオプション指定で大丈夫です。



    • 編集済み issei1102 2012年5月16日 2:25
    2012年5月16日 2:24
  • まだ理解されていないのかな…。

    フェッチにより、共有ロック、更新ロック、排他ロックなど処理に応じてロックが取られていきます。別の処理もそれぞれの処理に応じたロックが取られます。既にロックが取られているところに後から実行した方の処理が追加でロックを取ろうとすると、ロックの互換性に従い、ロックが取れる取れないが判断され、取れない場合は排他されます。

    一方の処理だけ挙げられましても、どのようなロックを取りに行くのかしか説明までで、取れる/取れない(排他)の回答はできません。

    # 関係ないけど、行ごとに算出するだけならトリガーで計算してしまえば…とか思ってしまう。ロックとか悩まずに済むし。

    2012年5月16日 4:09
  • システムがwebシステムであるため、

    この処理中にMYTABLEが更新される可能性があります。

    この際に適切な挙動をさせたいのですが、

    フェッチへの理解度が低く今回の質問に至りました。

    まず、MYTABLEが更新されるかどうかの問題にシステムがwebかどうかって関係ありますか?

    本題ですが、適切な挙動って具体的にどういった内容でしょうか。
    これは、佐祐理さんがおっしゃられている所の「何らかの条件の競合によりどちらかもしくは両方が排他される」という所だと思いますが、
    これにはやはり「挙げられたコードと別の処理」の関係性が無いと答えようがありませんし、設計に従って下さいとしか言えません。

    2012年5月16日 5:02