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:
La SELECT
requê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).
L'instruction de mise à jour obtient un vaste plan de mise à jour ...
... 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 |
+------------+------------+
WITH (NLOCK)
indice est de votre faute. Cela peut-il arriver sans unNOLOCK
soupçon?