เปลี่ยนคอลัมน์จาก NOT NULL เป็น NULL - เกิดอะไรขึ้นภายใต้ประทุน?


25

เรามีตารางที่มีแถว 2.3B อยู่ เราต้องการเปลี่ยนคอลัมน์จาก NOT NULL เป็น NULL คอลัมน์มีอยู่ในหนึ่งดัชนี (ไม่ใช่ดัชนีแบบคลัสเตอร์หรือ PK) ชนิดข้อมูลไม่เปลี่ยนแปลง (เป็น INT) เพียงความไร้ค่า คำสั่งดังต่อไปนี้:

Alter Table dbo.Workflow Alter Column LineId Int NULL

การดำเนินการใช้เวลาเกินกว่า 10 ก่อนที่เราจะหยุดมัน (เรายังไม่ปล่อยให้มันทำงานจนเสร็จเพราะมันเป็นการปิดกั้นและใช้เวลานานเกินไป) เราอาจคัดลอกตารางไปยังเซิร์ฟเวอร์ dev เพื่อทดสอบว่าใช้เวลานานเท่าใด แต่ฉันอยากรู้ถ้าใครรู้ว่าสิ่งที่ SQL Server ทำภายใต้ประทุนเมื่อแปลงจาก NOT NULL เป็น NULL นอกจากนี้ดัชนีที่ได้รับผลกระทบจะต้องได้รับการสร้างใหม่หรือไม่ แผนแบบสอบถามที่สร้างขึ้นไม่ได้ระบุว่าเกิดอะไรขึ้น

ตารางในคำถามถูกทำคลัสเตอร์ (ไม่ใช่ฮีป)


2
ฉันคิดว่ามันจะต้องปรับปรุง null bitmap ใน datapages ระดับ leaf ทั้งหมด และด้วยแถว 2.3B ฉันพนันได้เลยว่ามันจะมีหน้าให้ทำงานมากมาย ฉันก็ไม่แน่ใจเหมือนกัน
souplex

3
อาจกำลังยุ่งอยู่กับการวางบิตแมปเป็นโมฆะบนดัชนีด้วย บิตแมป NULL จะไม่ปรากฏใน NON-CLUSTERED INDEX ถ้าคอลัมน์ทั้งหมดของคำนิยามดัชนีถูกกำหนดเป็น NOT NULL
souplex

คำตอบ:


27

ตามที่กล่าวถึงโดย @Souplex ในความคิดเห็นหนึ่งคำอธิบายที่เป็นไปได้อาจเป็นได้หากคอลัมน์นี้เป็นคอลัมน์แรกที่สามารถใช้ได้NULLในดัชนีที่ไม่ใช่คลัสเตอร์ที่เข้าร่วม

สำหรับการตั้งค่าต่อไปนี้

CREATE TABLE Foo
  (
     A UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
     B CHAR(1) NOT NULL DEFAULT 'B'
  )

CREATE NONCLUSTERED INDEX ix
  ON Foo(B);

INSERT INTO Foo
            (B)
SELECT TOP 100000 'B'
FROM   master..spt_values v1,
       master..spt_values v2 

sys.dm_db_index_physical_stats แสดงดัชนีที่ไม่ทำคลัสเตอร์ixมี 248 leaf leaf page และ root เพจเดียว

แถวทั่วไปในหน้าดัชนีของใบไม้ดูเหมือนว่า

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

และในหน้ารูท

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

จากนั้นวิ่ง ...

CHECKPOINT;

GO

ALTER TABLE Foo ALTER COLUMN B  CHAR(1) NULL;


SELECT Operation, 
       Context,
       ROUND(SUM([Log Record Length]) / 1024.0,1) AS [Log KB],
       COUNT(*) as [OperationCount]
FROM sys.fn_dblog(NULL,NULL)
WHERE AllocUnitName = 'dbo.Foo.ix'
GROUP BY Operation, Context

กลับ

+-----------------+--------------------+-------------+----------------+
|    Operation    |      Context       |   Log KB    | OperationCount |
+-----------------+--------------------+-------------+----------------+
| LOP_SET_BITS    | LCX_GAM            | 4.200000    |             69 |
| LOP_FORMAT_PAGE | LCX_IAM            | 0.100000    |              1 |
| LOP_SET_BITS    | LCX_IAM            | 4.200000    |             69 |
| LOP_FORMAT_PAGE | LCX_INDEX_INTERIOR | 8.700000    |              3 |
| LOP_FORMAT_PAGE | LCX_INDEX_LEAF     | 2296.200000 |            285 |
| LOP_MODIFY_ROW  | LCX_PFS            | 16.300000   |            189 |
+-----------------+--------------------+-------------+----------------+

ตรวจสอบดัชนีดัชนีอีกครั้งตอนนี้แถวดูเหมือนว่า

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

และแถวในหน้าระดับบนเป็นด้านล่าง

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

แต่ละแถวได้รับการปรับปรุงและขณะนี้มีสองไบต์สำหรับจำนวนคอลัมน์พร้อมกับอีกหนึ่งไบต์สำหรับ NULL_BITMAP

เนื่องจากความกว้างของแถวพิเศษตอนนี้ดัชนีที่ไม่ได้ทำคลัสเตอร์จึงมี 285 leaf leaf page และตอนนี้เพจระดับกลางสองเพจพร้อมกับรูทเพจ

แผนการดำเนินการสำหรับ

 ALTER TABLE Foo ALTER COLUMN B  CHAR(1) NULL;

มีลักษณะดังนี้

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

สิ่งนี้สร้างสำเนาใหม่ของดัชนีแทนที่จะอัปเดตดัชนีที่มีอยู่และต้องการแยกหน้า


9

มันจะสร้างดัชนีที่ไม่ได้ทำคลัสเตอร์ขึ้นใหม่และไม่ใช่เพียงแค่อัปเดตข้อมูลเมตา สิ่งนี้ได้รับการทดสอบใน SQL 2014 และไม่ควรทดสอบในระบบการผลิตจริง ๆ :

CREATE TABLE [z](
    [a] [int] IDENTITY(1,1) NOT NULL,
    [b] [int] NOT NULL,
 CONSTRAINT [c_a] PRIMARY KEY CLUSTERED  ([a] ASC))
go
CREATE NONCLUSTERED INDEX [nc_b] on z (b asc)
GO
insert into z (b)
values (1);

และตอนนี้เพื่อความสนุก:

DBCC IND (0, z, -1)

สิ่งนี้จะทำให้เรามีหน้าฐานข้อมูลที่เก็บตารางและดัชนีที่ไม่ใช่แบบคลัสเตอร์

ค้นหาPagePIDตำแหน่งIndexIDคือ 2 และPageType2 แล้วทำสิ่งต่อไปนี้:

DBCC TRACEON(3604) --are you sure that you are allowed to do this?

แล้ว:

dbcc page (0, 1, PagePID, 3) with tableresults

ขอให้สังเกตว่ามีบิตแมปเป็นโมฆะในส่วนหัว:

สารสกัดส่วนหัวของหน้า

ตอนนี้มาทำกัน:

alter table z alter Column b int null;

หากคุณกำลังจริงๆใจร้อนคุณสามารถพยายามที่จะเรียกใช้dbcc pageคำสั่งอีกครั้ง DBCC IND (0, z, -1)แต่มันก็จะล้มเหลวเพื่อให้ตรวจสอบการจัดสรรอีกครั้งกับ หน้าเว็บจะย้ายไปราวกับว่าเป็นเวทมนต์

ดังนั้นการเปลี่ยนความไม่แน่นอนของคอลัมน์จะส่งผลต่อการจัดเก็บดัชนีที่ไม่ใช่คลัสเตอร์ที่ครอบคลุมคอลัมน์นั้นเนื่องจากข้อมูลเมตาจำเป็นต้องได้รับการอัปเดตและคุณไม่จำเป็นต้องสร้างดัชนีใหม่ในภายหลัง


ALTER TABLE ... ALTER COLUMN ...การดำเนินการหลายอย่างสามารถONLINEเริ่มต้นด้วย SQL Server 2016 แต่:

ALTER TABLE (Transact-SQL)

  • การปรับเปลี่ยนคอลัมน์จากNOT NULLการNULLไม่ได้รับการสนับสนุนเป็นการดำเนินการออนไลน์เมื่อคอลัมน์การเปลี่ยนแปลงมีการอ้างอิงจากดัชนี nonclustered
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.