Baca baris yang diperbarui sebagian?

15

Katakanlah saya memiliki dua kueri, berjalan dalam dua sesi terpisah di SSMS:

Sesi pertama:

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

Sesi kedua:

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

Mungkinkah SELECTpernyataan itu dapat membaca baris yang setengah diperbarui, misalnya satu dengan Name = 'Jonny'dan Surname = 'Goody'?

Kueri dieksekusi hampir bersamaan dalam sesi terpisah.

Tesh
sumber

Jawaban:

22

Ya, SQL Server dapat, dalam beberapa keadaan membaca nilai satu kolom dari versi "lama" dari baris, dan nilai kolom lain dari versi "baru" dari baris.

Mempersiapkan:

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 

Di koneksi pertama, jalankan ini:

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

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

Di koneksi kedua, jalankan ini:

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 

Setelah berjalan sekitar 30 detik saya mendapatkan:

masukkan deskripsi gambar di sini

The SELECTquery mengambil kolom dari indeks berkerumun non daripada indeks berkerumun (meskipun karena petunjuk).

masukkan deskripsi gambar di sini

Pernyataan pembaruan mendapat paket pembaruan yang luas ...

masukkan deskripsi gambar di sini

... dan memperbarui indeks secara berurutan sehingga dimungkinkan untuk membaca nilai "sebelum" dari satu indeks dan "setelah" dari yang lain.

Dimungkinkan juga untuk mengambil dua versi berbeda dari nilai kolom yang sama.

Di koneksi pertama, jalankan ini:

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   

Dan kemudian di yang kedua, jalankan ini:

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;

Segera, ini mengembalikan hasil berikut untuk saya

+------------+------------+
|   Left10   |  Right10   |
+------------+------------+
| BBBBBBBBBB | AAAAAAAAAA |
+------------+------------+
Martin Smith
sumber
1
Apakah saya benar bahwa Jika saya memiliki tabel CREATE TABLE Person (Id INT PRIMARY KEY, Name VARCHAR (100), Surname VARCHAR (100)) (tanpa indeks pada Name dan Surname) dan dua pertanyaan seperti dalam pertanyaan, yang dieksekusi dalam sesi terpisah, maka saya akan mendapatkan baris yang diperbarui atau baris yang lama, tetapi bukankah hasil antara memperbarui baris?
Tesh
@Jadi ya saya tidak berpikir itu akan mungkin untuk mendapatkan hasil lain karena itu semua akan berada di halaman yang sama dan dilindungi oleh kait selama penulisan.
Martin Smith
Apa pun yang tidak terduga yang Anda dapatkan dengan WITH (NLOCK)petunjuk adalah kesalahan Anda sendiri. Bisakah ini terjadi tanpa NOLOCKpetunjuk?
Ross Presser
2
@RossPresser - Ya untuk contoh pertama, lihat potongan persimpangan indeks di sini blogs.msdn.com/b/craigfr/archive/2007/05/02/… . Untuk yang kedua saya kira berpotensi jika dua versi yang berbeda tersedia. Tidak yakin akan mungkin untuk merekayasa dalam praktik.
Martin Smith