Lire une ligne partiellement mise à jour?

15

Disons que j'ai deux requêtes, exécutées en deux sessions distinctes dans SSMS:

Première session:

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

Deuxième session:

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

Est-il possible que l' SELECTinstruction puisse lire une ligne semi-mise à jour, par exemple une avec Name = 'Jonny'et Surname = 'Goody'?

Les requêtes sont exécutées presque simultanément dans des sessions distinctes.

Tesh
la source

Réponses:

22

Oui, SQL Server peut, dans certaines circonstances, lire la valeur d'une colonne de la «vieille» version de la ligne et la valeur d'une autre colonne de la «nouvelle» version de la ligne.

Installer:

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 

Dans la première connexion, exécutez ceci:

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

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

Dans la deuxième connexion, exécutez ceci:

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 

Après avoir couru pendant environ 30 secondes, j'obtiens:

entrez la description de l'image ici

La SELECTrequête récupère les colonnes à partir des index non clusterisés plutôt que de l'index cluster (bien qu'en raison des indications).

entrez la description de l'image ici

L'instruction de mise à jour obtient un vaste plan de mise à jour ...

entrez la description de l'image ici

... et met à jour les index en séquence afin qu'il soit possible de lire les valeurs "avant" d'un index et "après" de l'autre.

Il est également possible de récupérer deux versions différentes de la même valeur de colonne.

Dans la première connexion, exécutez ceci:

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   

Et puis dans le second, exécutez ceci:

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;

Tout de suite, cela m'a renvoyé le résultat suivant

+------------+------------+
|   Left10   |  Right10   |
+------------+------------+
| BBBBBBBBBB | AAAAAAAAAA |
+------------+------------+
Martin Smith
la source
1
Ai-je raison de dire que si j'ai une table CREATE TABLE Person (Id INT PRIMARY KEY, Name VARCHAR (100), Surname VARCHAR (100)) (sans index sur Name and Surname) et deux requêtes comme dans la question, qui sont exécutées dans des sessions distinctes, alors j'obtiendrai une ligne mise à jour ou une ancienne ligne, mais pas un résultat intermédiaire de la mise à jour de la ligne?
Tesh
@Tesh oui, je ne pense pas qu'il serait possible d'obtenir un autre résultat car tout cela serait sur la même page et protégé par un verrou pendant l'écriture.
Martin Smith
Tout ce qui est inattendu avec un WITH (NLOCK)indice est de votre faute. Cela peut-il arriver sans un NOLOCKsoupçon?
Ross Presser
2
@RossPresser - Oui pour le premier exemple, voir la section d'indexation ici blogs.msdn.com/b/craigfr/archive/2007/05/02/… . Pour la seconde, je suppose que cela pourrait être le cas si deux versions validées différentes étaient disponibles. Je ne sais pas s'il serait possible de concevoir en pratique.
Martin Smith