Przeczytaj częściowo zaktualizowany wiersz?

15

Powiedzmy, że mam dwa zapytania uruchomione w dwóch osobnych sesjach w SSMS:

Pierwsza sesja:

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

Druga sesja:

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

Czy to możliwe, że SELECTinstrukcja może odczytać częściowo zaktualizowany wiersz, na przykład jeden z Name = 'Jonny'i Surname = 'Goody'?

Zapytania są wykonywane prawie jednocześnie w oddzielnych sesjach.

Tesh
źródło

Odpowiedzi:

22

Tak, SQL Server może, w pewnych okolicznościach, odczytać wartość jednej kolumny ze „starej” wersji wiersza, a drugą wartość z „nowej” wersji wiersza.

Ustawiać:

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 

W pierwszym połączeniu uruchom to:

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

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

W drugim połączeniu uruchom to:

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 

Po około 30 sekundach pracy dostaję:

wprowadź opis zdjęcia tutaj

SELECTZapytania pobierania kolumny z non klastrowych indeksów zamiast indeksu klastra (chociaż ze względu na odcieniami).

wprowadź opis zdjęcia tutaj

Instrukcja aktualizacji otrzymuje szeroki plan aktualizacji ...

wprowadź opis zdjęcia tutaj

... i aktualizuje indeksy po kolei, dzięki czemu można odczytać wartości „przed” z jednego indeksu i „po” z drugiego.

Możliwe jest również pobranie dwóch różnych wersji tej samej wartości kolumny.

W pierwszym połączeniu uruchom to:

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   

A następnie w drugim, uruchom to:

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;

Od razu zwróciło mi to następujący wynik

+------------+------------+
|   Left10   |  Right10   |
+------------+------------+
| BBBBBBBBBB | AAAAAAAAAA |
+------------+------------+
Martin Smith
źródło
1
Czy mam rację, jeśli mam tabelę UTWÓRZ TABELĘ Osoba (Id INT PODSTAWOWY KLUCZ, Imię VARCHAR (100), Nazwisko VARCHAR (100)) (bez indeksów imienia i nazwiska) i dwa zapytania jak w pytaniu, które są wykonywane w oddzielnych sesjach otrzymam zaktualizowany wiersz lub stary wiersz, ale nie jakiś pośredni wynik aktualizacji wiersza?
Tesh
@ Tak, nie sądzę, aby można było uzyskać jakikolwiek inny wynik, ponieważ wszystkie byłyby na tej samej stronie i chronione przez zatrzask podczas zapisu.
Martin Smith
Wszystko, co nieoczekiwane otrzymujesz z WITH (NLOCK)podpowiedzią, to twoja wina. Czy może się to zdarzyć bez NOLOCKpodpowiedzi?
Ross Presser
2
@RossPresser - Tak na pierwszy przykład, patrz tutaj kawałek indeksu przecięcia blogs.msdn.com/b/craigfr/archive/2007/05/02/... . Po drugie, myślę, że potencjalnie mogłoby to być możliwe, gdyby były dostępne dwie różne zatwierdzone wersje. Nie jestem pewien, czy będzie możliwe inżynierowanie w praktyce.
Martin Smith,