문제

사용자가 주장하고 해당 사용자가 해당 작업을 수행 할 수있는 메시지 테이블이있는 간단한 메시징 프로그램을 작성하고 있습니다. 특정 사용자가 주어진 메시지를 청구 할 예정이 아니므로 쿼리가 내가 가진 모든 메시지 중 첫 번째 메시지를 선택한 다음 해당 메시지를 가져 오는 메시지를 선택할 수 있도록 쿼리를 원합니다. 문제는 두 명의 사용자가 동시에 동시에 사용하는 것을 동시에 사용하는 것을 원하지 않으므로 다음에 실행할 내용을 찾기 위해 프로그램으로 돌아가야하지 않고 두 가지 진술을 연속적으로 실행하고 싶습니다. 진술 사이. 반 콜론으로 분리하여 두 개의 연속 문장을 실행할 수 있다고 생각하지만 첫 번째 쿼리에서 두 번째 쿼리에서 반환 된 데이터를 사용하고 싶습니다. 변수는 완벽하지만 SQL에는 존재하지 않는 한 아는 한. 쿼리 사이에 상태를 보존 할 수있는 방법이 있습니까?

도움이 되었습니까?

해결책

Le Dorfier가 말한 것처럼 거래는 좋은 방법이지만, 성가신 것들이 있습니다.

먼저 업데이트를 수행 할 수 있습니다. 즉, 사용자 ID 또는 이와 유사한 메시지를 태그로 표시 할 수 있습니다. 당신은 어떤 SQL 맛을 사용하는지 언급하지 않지만 MySQL에서는 다음과 같이 보일 것 같습니다.

UPDATE message
SET    user_id = ...
WHERE  user_id = 0   -- Ensures no two users gets the same message
LIMIT 1

MS SQL에서는 다음과 같은 선을 따라 무언가가 될 것입니다.

WITH q AS (
  SELECT TOP 1
  FROM message m
  WHERE user_id = 0
) 
UPDATE q
SET    user_id = 1

/비

다른 팁

이것이 Tran과 Commit Tran이 시작하는 것입니다. 거래 내에 보호하려는 진술을 배치하십시오.

쿼리 사이에 상태를 보존 할 수있는 방법이 있습니까?

아니요. SQL은 절차 적 언어가 아닙니다. 두 쿼리를 단일 쿼리로 다시 쓸 수 있습니다 (항상 가능하지는 않지만 종종 가능하지 않더라도 가치가 없습니다). 절차 적 언어와 함께 붙일 수 있습니다. 많은 SQL 서버 가이 언어 ( "저장 절차")에 대한 내장 언어를 제공하거나 응용 프로그램에서 수행 할 수 있습니다.

문제는 두 명의 사용자가 동시에 동시에 사용하는 것을 원하지 않는다는 것입니다.

자물쇠를 사용하십시오. 어떤 SQL 서버를 사용하고 있는지 모르겠지만 SELECT ... FOR UPDATE 사용 가능한 경우 원하는 것 같습니다.

아마도 임시 테이블을 사용할 수 있습니다.

SQL 자체에는 변수가 없지만 거의? 모든 RDBMS SQL 확장 기능이 있습니다. 그러나, 나는 그것만으로도 당신의 문제를 어떻게 해결할 것인지 잘 모르겠습니다.

언급 한 바와 같이, 거래는 트릭을 수행 할 것입니다. 2 개의 관련이없는 진술을 효과적으로 그룹화합니다. 하지만, 기본 트랜잭션 수준이됩니다 작동하지. (대부분?) RDBMS 서버의 기본 트랜잭션 수준은 저지른 것을 읽습니다. 그렇다면 사용자 2가 사용자 1과 동일한 행을 읽지 못하게하는 것은 아닙니다. 이를 위해서는 반복 가능한 읽기 또는 직렬화 가능해야합니다.

이것은 고전적인 동시성 문제입니다. 일반적으로 처리하는 두 가지 방법은 비관적 잠금 또는 낙관적 인 점검입니다. 반복 가능한 읽기 트랜잭션은 비관적이며 (필요했는지 여부에 관계없이 잠금 비용을 발생 시키면) @@ rowCount를 확인하는 것은 낙관적이라고 생각하지만 (@@ rowCount = 0 일 때 현명한 일을 할 때)

일반적으로 우리는 낙관적 (잠금이 비싸다)을 사용하며, 타임 스탬프 또는 필드 조합을 사용하여 생각한 데이터를 변경하도록합니다. 따라서 제 제안은 Rowversion 또는 Timestamp 필드를 포함하여 업데이트 문으로 전달하는 것입니다. 그런 다음 @@ rowCount를 확인하여 레코드를 업데이트했는지 확인하십시오. 그렇지 않은 경우 돌아가서 다른 메시지를 선택하십시오. 의사 코드에서 :

int messageId, byte[] rowVersion = DB.Select(
  "SELECT TOP 1 
      MessageId, RowVersion 
   FROM Messages 
   WHERE 
      User IS NULL";

int rowsAffected = DB.Update(
   "UPDATE Messages SET 
       User = @myUserId 
    WHERE 
       MessageId = @messageId 
       AND RowVersion = @rowVersion", 
    myUserId, messageId, rowVersion
);
if (rowsAffected = 0) 
   throw new ConcurrencyException("The message was taken by someone else");

특정 진술에 따라 업데이트 명령문에서 "userID IS NULL"을 반복하면서 도망 갈 수 있습니다. 그것은 Brimstedt의 솔루션과 유사하지만 여전히 @@ rowcount를 확인해야합니다 행이 실제로 업데이트되었는지 확인하기 위해.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top