2017-02-26 7 views
1

"チェックアウト"されたオブジェクトを追跡するテーブルがありますが、オブジェクトはさまざまな他のテーブルに存在します。目的は、特定のオブジェクトを1回のみチェックアウトできるように(つまり、2人のユーザーが同じオブジェクトをチェックアウトできないように)、基準に一致するオブジェクトをチェックアウトできるようにすることです。場合によっては、単一のオブジェクトが複数の表にまたがることがあり、すべてのユーザーの基準(問題の場合)を確認するために結合を使用する必要があります。SQLでの結合による原子的更新

は、ここで(うまくいけば、あなたがスキーマを推測することができます)非常に単純なクエリの例です:

update top (1) Tracker 
set IsCheckedOut = 1 
    from Tracker t 
    join Object o on t.ObjectId = o.Id 
    join Property p on p.ObjectId = o.Id 
    where t.IsCheckedOut = 0 
    and o.SomePropertyColumn = 'blah' 
    and p.SomeOtherPropertyColumn = 42 

によりfromサブクエリに、私はこのクエリはアトミックではないと思われるので、2人のユーザーがオブジェクトの同じ味を要求します同時に、同じオブジェクトをチェックアウトすることができます。

本当ですか?もしそうなら、私はこれをどのように修正するのですか?

output DELETED.*句を追加し、IsCheckedOut列の戻り値が1の場合、ユーザーがクエリを再試行すると考えましたが、これは正しく動作すると思います。ユーザーが再試行を心配する必要がないようなものを得るのが好きです。完全な説明については

EDIT

は、以下のSqlZimの回答を参照してください、私はちょうど上に掲載クエリに直接ヒントを追加することができ、この単純なケースのために:

update top (1) Tracker 
set IsCheckedOut = 1 
    from Tracker t (updlock, rowlock, readpast) 
    join Object o on t.ObjectId = o.Id 
    join Property p on p.ObjectId = o.Id 
    where t.IsCheckedOut = 0 
    and o.SomePropertyColumn = 'blah' 
    and p.SomeOtherPropertyColumn = 42 
+0

マインドは、与えられた例をもう少し詳しく説明していますか?それが正しいと判明すれば、私はそれを答えとして受け入れます。 – gzak

答えて

0

トランザクションを使用すると、いくつかのテーブルhintsをロックすると、ただ1つの行を取得して更新することができます。

declare @TrackerId int; 

begin tran; 

select top 1 @TrackerId = TrackerId 
    from Tracker t with (updlock, rowlock, readpast) 
    inner join Object o on t.ObjectId = o.Id 
    inner join Property p on p.ObjectId = o.Id; 
    where t.IsCheckedOut = 0 
    and o.SomePropertyColumn = 'blah' 
    and p.SomeOtherPropertyColumn = 42; 

if @TrackerId is not null 
begin; 
update Tracker 
    set IsCheckedOut = 1 
    where TrackerId = @TrackerId; 
end; 

commit tran 
  • updlock当社selectから行の更新ロックを配置します。他のトランザクションは、行を更新または削除することはできませんが、行を選択することはできますが、この行の更新ロックを取得しようとする同時選択(つまり、同じ検索基準の差異プロセスからこの手順を実行)この特定の行を選択することはできませんが、readpastも使用しているため、次の行を選択してロックすることができます。

  • rowlockは、ページまたはテーブルのロックではなく、更新する特定の行のみをロックしようとします。

  • readpast行レベルのロックを持つ行をスキップします。

参照:共通表式を使用して


代替の一の工程コード:

begin tran; 

    with cte as (
    select top 1 
     t.* 
     from Tracker t with (updlock, rowlock, readpast) 
     inner join Object o 
      on t.ObjectId = o.Id 
     inner join Property p 
      on p.ObjectId = o.Id; 
     where t.IsCheckedOut = 0 
     and o.SomePropertyColumn = 'blah' 
     and p.SomeOtherPropertyColumn = 42 
     --order by TrackerId asc /* optional order by */ 
) 
    update cte 
    set IsCheckedOut = 1 
    output inserted.*; 

commit tran; 
+0

これらのヒントをそのまま私の 'from'クエリに追加し、全体的なクエリを同じままにします(つまり、別の2つのステートメントとして書き直さないでください)。 – gzak

+0

ああ、忘れてしまいましたが、私は出力された行をユーザに返したいので、とにかく 'output'節が必要です – gzak

+0

@gzakはい、あなたもcteを使って書くことができます – SqlZim

関連する問題