Читать частично обновленную строку?

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'?

Запросы выполняются почти одновременно в отдельных сеансах.

Tesh
источник

Ответы:

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, Имя VARCHAR (100), Фамилия VARCHAR (100)) (без каких-либо индексов по Имени и Фамилии) и два запроса, как в вопросе, которые выполняются в отдельных сеансах я получу обновленную строку или старую строку, но не какой-нибудь промежуточный результат обновления строки?
Tesh
@ Да, я не думаю, что было бы возможно получить какой-либо другой результат, так как все это было бы на той же странице и защищено защелкой во время записи.
Мартин Смит,
Все неожиданное, что вы получите с WITH (NLOCK)подсказкой, является вашей собственной ошибкой. Может ли это произойти без NOLOCKнамека?
Росс Прессер
2
@RossPresser - Да, для первого примера, смотрите раздел пересечения индексов здесь blogs.msdn.com/b/craigfr/archive/2007/05/02/… . Во-вторых, я думаю, что это возможно, если бы были доступны две разные версии. Не уверен, что это будет возможно на практике.
Мартин Смит