次のスクリプトをまとめて、私が過去に使用したこのトリックを証明しました。使用する場合は、目的に合わせて変更する必要があります。コメントは次のとおりです:
/*
CREATE TABLE Item
(
Title varchar(255) not null
,Teaser varchar(255) not null
,ContentId varchar(30) not null
,RowLocked bit not null
)
UPDATE item
set RowLocked = 1
where ContentId = 'Test01'
*/
DECLARE
@Check varchar(30)
,@pContentID varchar(30)
,@pTitle varchar(255)
,@pTeaser varchar(255)
set @pContentID = 'Test01'
set @pTitle = 'TestingTitle'
set @pTeaser = 'TestingTeasier'
set @check = null
UPDATE dbo.Item
set
@Check = ContentId
,Title = @pTitle
,Teaser = @pTeaser
where ContentID = @pContentID
and RowLocked = 0
print isnull(@check, '<check is null>')
IF @Check is null
INSERT dbo.Item (ContentID, Title, Teaser, RowLocked)
values (@pContentID, @pTitle, @pTeaser, 0)
select * from Item
ここでの秘訣は、Update ステートメント内のローカル変数に値を設定できることです。上記の「フラグ」値は、更新が機能する (つまり、更新基準が満たされる) 場合にのみ設定されます。それ以外の場合は変更されません (ここでは null のままです)。それを確認し、それに応じて処理できます。
トランザクションとそれをシリアライズ可能にすることに関して、処理方法を提案する前に、トランザクション内にカプセル化する必要があるものについて詳しく知りたい.
-- 補遺、以下の 2 番目のコメントからのフォローアップ -----------
Saffron 氏のアイデアは、主キーが外部で定義され、データベースに渡されるため、このルーチンを実装するための徹底的かつ堅実な方法です (つまり、ID 列を使用していません。>
さらにテストを行いました (列 ContentId に主キー制約を追加し、UPDATE と INSERT をトランザクションでラップし、シリアル化可能なヒントを更新に追加しました)、はい、それで必要なすべてが実行されるはずです。更新が失敗すると、インデックスのその部分に範囲ロックが適用され、列に新しい値を同時に挿入しようとする試みがブロックされます。もちろん、N 個のリクエストが同時に送信された場合、「最初」が行を作成し、行のどこかに「ロック」を設定しない限り、2 番目、3 番目などによってすぐに更新されます。良いトリックです!
(キー列にインデックスがないと、テーブル全体がロックされることに注意してください。また、範囲ロックにより、新しい値の「両側」の行がロックされる場合があります-または、ロックされない可能性があります。私はしませんでした操作の期間は [?] 1 桁のミリ秒単位である必要があるため、重要ではありません。)