3

私のデータベースは SQL Server 2005/8 です。予約システムでは、イベントの予約は 24 件に制限されています。ストアド プロシージャ内のこのコードは、次をチェックします。 - 現在のユーザー (@UserId) がイベント (@EventsID) でまだ予約されていないこと - 現在のイベントの現在の予約リストが 24 未満であること - 新しい予約を挿入します。

BEGIN TRANSACTION 
IF (((select count (*) from dbo.aspnet_UsersEvents with (updlock) 
      where UserId = @UserId and EventsId = @EventsId) = 0) 
AND  ((SELECT Count(*)  FROM dbo.aspnet_UsersEvents with (updlock) 
      WHERE EventsId = @EventsId) < 24))
BEGIN
  insert into dbo.aspnet_UsersEvents (UserId, EventsId) 
      Values (@UserId, @EventsId)
END
COMMIT

問題は、安全ではないということです。2 人のユーザーが同時にテストを実行し、どちらも予約できると結論付ける場合があります。両方とも行を挿入すると、最終的に 25 の予約になります。

単にトランザクションに含めるだけでは機能しません。一方が更新ロックを取得し、もう一方がロックされないようにすることを期待して、セレクトに WITH (UPDLOCK) を追加してみました。それはうまくいきません。

4

2 に答える 2

3

3 つのオプション:

  1. SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
  2. ロックヒントをWITH (UPDLOCK, HOLDLOCK)
  3. 一意の制約を dbo.aspnet_UsersEvents に追加しTRY/CATCH、挿入を囲みます。

を省略した場合、次のスクリプトを使用して、ロックが取得され、すぐに解放されることを確認できますHOLDLOCK。を使用すると、ロックが解除されないこともわかります (「KEY のロック参照の解除」出力はありません) HOLDLOCK

( Gist スクリプト)

于 2011-12-01T21:22:31.280 に答える
1

atREAD COMMITTEDまたはそれ以上の 1 つのステートメントで実行してください。

INSERT dbo.aspnet_UsersEvents
       (UserId,EventsId)
OUTPUT inserted.UserEventsId -- Or whatever, just getting back one row identifies the insert was successful
SELECT @UserId
       , @EventsId
 WHERE ( SELECT COUNT (*)
           FROM dbo.aspnet_UsersEvents
          WHERE UserId = @UserId
                AND EventsId = @EventsId ) = 0
       AND ( SELECT COUNT(*)
               FROM dbo.aspnet_UsersEvents
              WHERE EventsId = @EventsId ) < 24 

補足:SELECT COUNT(*)重複チェックは過剰に思えますが、個人的には を使用しますNOT EXISTS(SELECT NULL FROM ... WHERE UserID = ..., EventsID = ...

于 2011-12-01T21:25:14.327 に答える