2017-02-26 10 views
1

"체크 아웃"된 오브젝트를 추적하는 테이블이 있지만 오브젝트는 다양한 다른 테이블에 있습니다. 목표는 사용자가 기준에 맞는 객체를 체크 아웃 할 수있게하여 주어진 객체를 한 번만 체크 아웃 할 수 있도록합니다 (즉, 두 명의 사용자가 동일한 객체를 체크 아웃 할 수 없어야 함). 경우에 따라 단일 객체가 여러 테이블로 확장 될 수 있으므로 조인을 사용하여 모든 사용자의 기준 (문제가있는 경우)을 확인해야합니다.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 하위 쿼리에,이 쿼리가 원자 아니라고 생각하기 때문에 두 명의 사용자가 객체의 동일한 맛을 요청 동시에 같은 대상을 체크 아웃 할 수 있습니다.

사실입니까? 그렇다면 어떻게 해결할 수 있습니까?

output DELETED.* 절을 추가하고 IsCheckedOut 열의 반환 값이 1 인 경우 사용자가 해당 쿼리를 다시 시도하는 것에 대해 생각했습니다. (틀렸다면 올바른 답변입니다) ...하지만 사용자가 재 시도에 대해 걱정할 필요가없는 부분을 얻고 싶습니다.

철저한 설명은 편집

아래 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을 잠 그려면 한 행을 잡고 업데이트 할 수 있습니다.

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' 쿼리에 추가하고 전체 쿼리를 그대로 두는 것 (즉, 두 개의 별도 명령문으로 다시 작성하지 마십시오)이 맞습니까? – gzak

+0

오, 깜박했는데, 출력 된 절을 사용자에게 반환하기를 원한다는 이유로 어쨌든 출력 절이 필요합니다. – gzak

+0

@ gzak 예, cte를 사용하여 쓸 수도 있습니다 – SqlZim