อ่านแถวที่อัปเดตบางส่วนหรือไม่


15

สมมติว่าฉันมีสองแบบสอบถามทำงานในสองช่วงแยกกันใน SSMS:

เซสชั่นแรก:

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

เซสชั่นที่สอง:

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

เป็นไปได้ไหมที่SELECTคำแถลงสามารถอ่านแถวที่มีการอัพเดตครึ่งหนึ่งได้เช่นเดียวกับName = 'Jonny'และSurname = 'Goody'?

แบบสอบถามจะถูกดำเนินการเกือบจะพร้อมกันในเซสชันที่แยกต่างหาก

คำตอบ:


22

ใช่ SQL Server สามารถอ่านค่าของคอลัมน์หนึ่งจากแถว "รุ่นเก่า" ในบางกรณีและค่าของคอลัมน์อื่นจากรุ่น "ใหม่" ของแถว

ติดตั้ง:

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 

ในการเชื่อมต่อครั้งแรกให้รันสิ่งนี้:

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

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

ในการเชื่อมต่อที่สองเรียกใช้สิ่งนี้:

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 

หลังจากทำงานประมาณ 30 วินาทีฉันจะได้รับ:

ป้อนคำอธิบายรูปภาพที่นี่

SELECTแบบสอบถามจะดึงคอลัมน์จากดัชนีคลัสเตอร์มากกว่าดัชนีคลัสเตอร์ (แม้ว่าเนื่องจากคำแนะนำ)

ป้อนคำอธิบายรูปภาพที่นี่

คำสั่งการปรับปรุงได้รับแผนการปรับปรุงที่กว้างขวาง ...

ป้อนคำอธิบายรูปภาพที่นี่

... และอัปเดตดัชนีตามลำดับดังนั้นจึงเป็นไปได้ที่จะอ่านค่า "ก่อน" จากดัชนีหนึ่งและ "หลัง" จากดัชนีอื่น

นอกจากนี้ยังเป็นไปได้ที่จะเรียกคืนค่าคอลัมน์เดียวกันสองเวอร์ชัน

ในการเชื่อมต่อครั้งแรกให้รันสิ่งนี้:

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   

จากนั้นในวินาทีให้เรียกใช้สิ่งนี้:

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;

ทันทีนี้กลับผลลัพธ์ต่อไปนี้สำหรับฉัน

+------------+------------+
|   Left10   |  Right10   |
+------------+------------+
| BBBBBBBBBB | AAAAAAAAAA |
+------------+------------+

1
ฉันถูกใช่ไหมถ้าฉันมีตารางสร้างบุคคลตาราง (รหัสหลัก INT, ชื่อ VARCHAR (100), นามสกุล VARCHAR (100)) (ไม่มีดัชนีใด ๆ ในชื่อและนามสกุล) และแบบสอบถามสองรายการที่ดำเนินการอยู่ ในเซสชันที่แยกกันฉันจะได้รับแถวที่อัปเดตหรือแถวเก่า แต่ไม่ใช่ผลลัพธ์ระดับกลางของการอัปเดตแถวหรือไม่
Tesh

@ ใช่ฉันไม่คิดว่ามันจะเป็นไปได้ที่จะได้รับผลอื่น ๆ ที่จะอยู่ในหน้าเดียวกันและได้รับการคุ้มครองโดยสลักในระหว่างการเขียน
Martin Smith

อะไรก็ตามที่คุณคาดไม่ถึงด้วยWITH (NLOCK)คำใบ้คือความผิดของคุณเอง สิ่งนี้จะเกิดขึ้นได้NOLOCKไหม
Ross Presser

2
@RossPresser - ใช่เพื่อตัวอย่างแรกเห็นชิ้นส่วนแยกดัชนีที่นี่blogs.msdn.com/b/craigfr/archive/2007/05/02/... สำหรับครั้งที่สองฉันเดาว่ามันน่าจะเป็นไปได้ถ้ามีเวอร์ชั่นที่ต่างกันสองเวอร์ชั่น ไม่แน่ใจว่ามันจะเป็นไปได้ที่จะวิศวกรในทางปฏิบัติ
Martin Smith
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.