부분적으로 업데이트 된 행을 읽습니까?


15

SSMS에서 두 개의 별도 세션에서 실행되는 두 개의 쿼리가 있다고 가정 해 보겠습니다.

첫 세션 :

UPDATE Person
SET Name='Jonny', Surname='Cage'
WHERE Id=42

두 번째 세션 :

SELECT Name, Surname
FROM Person WITH(NOLOCK)
WHERE Id > 30

그것은 가능성이 SELECT문을 가진 인스턴스 하나에 대한 반 업데이트 된 행을 읽을 수 Name = 'Jonny'Surname = 'Goody'?

쿼리는 별도의 세션에서 거의 동시에 실행됩니다.

답변:


22

예, 경우에 따라 SQL Server는 "이전"버전의 행에서 하나의 열 값을 읽을 수 있고 "새"버전의 행에서 다른 열 값을 읽을 수 있습니다.

설정:

CREATE TABLE Person
  (
     Id      INT PRIMARY KEY,
     Name    VARCHAR(100),
     Surname VARCHAR(100)
  );

CREATE INDEX ix_Name
  ON Person(Name);

CREATE INDEX ix_Surname
  ON Person(Surname);

INSERT INTO Person
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID),
                   'Jonny1',
                   'Jonny1'
FROM   master..spt_values v1,
       master..spt_values v2 

첫 번째 연결에서 다음을 실행하십시오.

WHILE ( 1 = 1 )
  BEGIN
      UPDATE Person
      SET    Name = 'Jonny2',
             Surname = 'Jonny2'

      UPDATE Person
      SET    Name = 'Jonny1',
             Surname = 'Jonny1'
  END 

두 번째 연결에서 다음을 실행하십시오.

DECLARE @Person TABLE (
  Id      INT PRIMARY KEY,
  Name    VARCHAR(100),
  Surname VARCHAR(100));

SELECT 'Setting intial Rowcount'
WHERE  1 = 0

WHILE @@ROWCOUNT = 0
  INSERT INTO @Person
  SELECT Id,
         Name,
         Surname
  FROM   Person WITH(NOLOCK, INDEX = ix_Name, INDEX = ix_Surname)
  WHERE  Id > 30
         AND Name <> Surname

SELECT *
FROM   @Person 

약 30 초 동안 실행 한 후 다음을 얻습니다.

여기에 이미지 설명을 입력하십시오

SELECT쿼리는 비 클러스터 인덱스보다는 (힌트 때문이기는하지만) 클러스터 된 인덱스에서 열을 검색합니다.

여기에 이미지 설명을 입력하십시오

업데이트 설명은 광범위한 업데이트 계획을 얻습니다 ...

여기에 이미지 설명을 입력하십시오

... 및 인덱스를 순서대로 업데이트하여 한 인덱스에서 "이전"값을 읽고 다른 인덱스에서 "이전"값을 읽을 수 있습니다.

동일한 열 값의 서로 다른 두 가지 버전을 검색 할 수도 있습니다.

첫 번째 연결에서 다음을 실행하십시오.

DECLARE @A VARCHAR(MAX) = 'A';
DECLARE @B VARCHAR(MAX) = 'B';

SELECT @A = REPLICATE(@A, 200000),
       @B = REPLICATE(@B, 200000);

CREATE TABLE T
  (
     V VARCHAR(MAX) NULL
  );

INSERT INTO T
VALUES     (@B);

WHILE 1 = 1
  BEGIN
      UPDATE T
      SET    V = @A;

      UPDATE T
      SET    V = @B;
  END   

그런 다음 두 번째에서 다음을 실행하십시오.

SELECT 'Setting intial Rowcount'
WHERE  1 = 0;

WHILE @@ROWCOUNT = 0
  SELECT LEFT(V, 10)  AS Left10,
         RIGHT(V, 10) AS Right10
  FROM   T WITH (NOLOCK)
  WHERE  LEFT(V, 10) <> RIGHT(V, 10);

DROP TABLE T;

곧이 결과는 다음과 같습니다.

+------------+------------+
|   Left10   |  Right10   |
+------------+------------+
| BBBBBBBBBB | AAAAAAAAAA |
+------------+------------+

1
CREATE TABLE Person (Id INT PRIMARY KEY, Name VARCHAR (100), Surname VARCHAR (100)) 테이블이 있고 (Name과 Surname에 대한 색인없이) 질문과 같은 두 개의 쿼리가 있으면 실행됩니다. 별도의 세션에서 업데이트 된 행이나 이전 행을 얻지 만 행을 업데이트 한 중간 결과는 얻지 않습니까?
Tesh

@Tesh 예 나는 같은 페이지에 있고 쓰기 중에 래치로 보호되는 다른 결과를 얻을 수 있다고 생각하지 않습니다.
Martin Smith

WITH (NLOCK)힌트로 예상치 못한 것은 자신의 잘못입니다. NOLOCK힌트 없이 이런 일이 일어날 수 있습니까 ?
Ross Presser

2
@RossPresser-첫 번째 예의 경우 인덱스 교차점 ( blogs.msdn.com/b/craigfr/archive/2007/05/02/…)을 참조하십시오 . 두 번째로 두 개의 다른 커밋 된 버전을 사용할 수있을 가능성이 있습니다. 실제로 엔지니어링 할 수 있는지 확실하지 않습니다.
Martin Smith
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.