Читати частково оновлений рядок?


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
Я маю рацію, що якщо у мене є таблиця СТВОРИТИ ТАБЛИЦЮ (Id INT PRIMARY KEY, Ім'я VARCHAR (100), Прізвище VARCHAR (100)) (без будь-яких індексів на ім’я та прізвище) та два запити, як у запитанні, які виконуються на окремих сесіях я отримаю оновлений рядок або старий рядок, але не якийсь проміжний результат оновлення рядка?
Теш

@ Так, я не думаю, що можна було б отримати будь-який інший результат, оскільки це було б на одній сторінці та захищено засувкою під час запису.
Мартін Сміт

Все, що несподівано ви отримаєте із WITH (NLOCK)підказкою, - це ваша вина. Чи може це статися без NOLOCKнатяку?
Росс Пресер

2
@RossPresser - Так, до першого прикладу дивіться фрагмент перетину індексу тут blogs.msdn.com/b/craigfr/archive/2007/05/02/… . Я вважаю, що потенційно це могло б бути, якби були доступні дві різні версії. Не впевнений, що це можливо на практиці.
Мартін Сміт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.