¿Leer una fila parcialmente actualizada?

15

Digamos que tengo dos consultas, que se ejecutan en dos sesiones separadas en SSMS:

Primera sesión:

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

Segunda sesión:

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

¿Es posible que la SELECTinstrucción pueda leer una fila medio actualizada, por ejemplo, una con Name = 'Jonny'y Surname = 'Goody'?

Las consultas se ejecutan casi simultáneamente en sesiones separadas.

Tesh
fuente

Respuestas:

22

Sí, SQL Server puede, en algunas circunstancias, leer el valor de una columna de la versión "antigua" de la fila y el valor de otra columna de la versión "nueva" de la fila.

Preparar:

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 

En la primera conexión, ejecute esto:

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

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

En la segunda conexión, ejecute esto:

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 

Después de correr durante unos 30 segundos me sale:

ingrese la descripción de la imagen aquí

La SELECTconsulta está recuperando las columnas de los índices no agrupados en lugar del índice agrupado (aunque debido a las sugerencias).

ingrese la descripción de la imagen aquí

La declaración de actualización obtiene un amplio plan de actualización ...

ingrese la descripción de la imagen aquí

... y actualiza los índices en secuencia para que sea posible leer los valores "antes" de un índice y "después" del otro.

También es posible recuperar dos versiones diferentes del mismo valor de columna.

En la primera conexión, ejecute esto:

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   

Y luego en el segundo, ejecuta esto:

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;

De inmediato, esto me devolvió el siguiente resultado

+------------+------------+
|   Left10   |  Right10   |
+------------+------------+
| BBBBBBBBBB | AAAAAAAAAA |
+------------+------------+
Martin Smith
fuente
1
Estoy en lo cierto si tengo una tabla CREAR TABLA Persona (Id INT PRIMARY KEY, Nombre VARCHAR (100), Apellido VARCHAR (100)) (sin ningún índice en Nombre y Apellido) y dos consultas como en la pregunta, que se ejecutan en sesiones separadas, obtendré una fila actualizada o una fila anterior, pero ¿no algún resultado intermedio de actualizar la fila?
Tesh
@Tesh sí, no creo que sea posible obtener ningún otro resultado, ya que todo estaría en la misma página y protegido por un pestillo durante la escritura.
Martin Smith
Cualquier cosa inesperada que obtengas con una WITH (NLOCK)pista es tu culpa. ¿Puede suceder esto sin una NOLOCKpista?
Ross Presser
2
@RossPresser - Sí al primer ejemplo, vea la pieza de intersección del índice aquí blogs.msdn.com/b/craigfr/archive/2007/05/02/… . Por el segundo, supongo que podría hacerlo si dos versiones comprometidas diferentes estuvieran disponibles. No estoy seguro de que sea posible diseñar en la práctica.
Martin Smith