ALTER TABLE หรือไม่ DROP COLUMN เป็นเพียงข้อมูลเมตาดาต้าเท่านั้น


11

ฉันพบแหล่งข้อมูลหลายแห่งที่ระบุเปลี่ยนแปลงตาราง ... DROP COLUMN เป็นการดำเนินการเฉพาะเมตาดาต้า

แหล่ง

สิ่งนี้จะเป็นอย่างไร ข้อมูลในระหว่าง DROP COLUMN ไม่จำเป็นต้องถูกลบออกจากดัชนีที่ไม่ใช่คลัสเตอร์และดัชนี / ฮีปคลัสเตอร์หรือไม่?

นอกจากนี้เหตุใดMicrosoft Docs จึงบอกเป็นนัยว่าเป็นการดำเนินการที่บันทึกไว้อย่างสมบูรณ์

การปรับเปลี่ยนที่ทำกับตารางจะถูกบันทึกและสามารถกู้คืนได้อย่างสมบูรณ์ การเปลี่ยนแปลงที่ส่งผลกระทบต่อทุกแถวในตารางขนาดใหญ่เช่นลดลงคอลัมน์หรือในบางรุ่นของ SQL Server เพิ่มคอลัมน์ไม่เป็นโมฆะด้วยค่าเริ่มต้นที่สามารถใช้เวลานานในการสร้างที่สมบูรณ์และบันทึกเข้าสู่ระบบจำนวนมาก เรียกใช้คำสั่ง ALTER TABLE เหล่านี้ด้วยความระมัดระวังเช่นเดียวกับคำสั่ง INSERT, UPDATE หรือ DELETE ที่มีผลกับแถวจำนวนมาก

ในฐานะที่เป็นคำถามรอง: เครื่องยนต์จะติดตามคอลัมน์ที่ถูกทิ้งได้อย่างไรหากข้อมูลไม่ได้ถูกลบออกจากหน้าเว็บต้นแบบ


2
ฉันคิดว่าภาษาสามารถอยู่รอดผ่านผลิตภัณฑ์หลายเวอร์ชันและทำซ้ำเอกสารอีกมากมาย เมื่อเวลาผ่านไปการดำเนินการที่เกี่ยวข้องกับคอลัมน์มากขึ้นเรื่อย ๆ ทำให้เกิดการเปลี่ยนแปลงทางออนไลน์ / ข้อมูลเมตาเท่านั้น มันอาจเป็นตัวอย่างที่ไม่ดีในตอนนี้ แต่จุดประสงค์ของประโยคก็เพื่อเตือนคุณว่าโดยทั่วไปการดำเนินการเปลี่ยนแปลงบางอย่างอาจเป็นการดำเนินการขนาดของข้อมูลในบางสถานการณ์แทนที่จะแสดงรายการทุกสถานการณ์ที่เฉพาะเจาะจง
Aaron Bertrand

คำตอบ:


14

มีบางสถานการณ์ที่การวางคอลัมน์สามารถเป็นการดำเนินการกับเมตาดาต้า - ข้อมูลเท่านั้น คำจำกัดความของคอลัมน์สำหรับตารางที่กำหนดจะไม่รวมอยู่ในแต่ละหน้าและทุกแถวที่เก็บแถวคำจำกัดความของคอลัมน์จะถูกเก็บไว้ในข้อมูลเมตาของฐานข้อมูลเท่านั้นรวมถึง sys.sysrowsets, sys.sysrscols เป็นต้น

เมื่อวางคอลัมน์ที่ไม่ได้อ้างถึงโดยวัตถุอื่น ๆ เครื่องมือจัดเก็บข้อมูลจะทำเครื่องหมายคำจำกัดความของคอลัมน์ว่าไม่มีอยู่อีกต่อไปโดยการลบรายละเอียดที่เกี่ยวข้องจากตารางระบบต่างๆ การกระทำของการลบ meta-data จะทำให้โพรซีเดอร์แคชใช้ไม่ได้ซึ่งจำเป็นต้องมีการคอมไพล์ใหม่เมื่อใดก็ตามที่เคียวรีอ้างถึงตารางนั้นในภายหลัง ตั้งแต่ recompile เท่านั้นส่งกลับคอลัมน์ที่ขณะนี้อยู่ในตารางรายละเอียดคอลัมน์สำหรับคอลัมน์ลดลงจะไม่เคยถามแม้สำหรับ; เอ็นจิ้นการจัดเก็บข้ามไบต์ที่จัดเก็บในแต่ละหน้าของคอลัมน์นั้นราวกับว่าไม่มีคอลัมน์อยู่อีกต่อไป

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

มีสถานการณ์ที่คุณไม่สามารถวางคอลัมน์เช่นเมื่อคอลัมน์รวมอยู่ในดัชนีหรือเมื่อคุณสร้างวัตถุสถิติสำหรับคอลัมน์ด้วยตนเอง ฉันเขียนโพสต์บล็อกที่แสดงข้อผิดพลาดที่เกิดขึ้นเมื่อพยายามแก้ไขคอลัมน์ด้วยวัตถุสถิติที่สร้างขึ้นด้วยตนเอง ซีแมนทิกส์เดียวกันจะใช้เมื่อวางคอลัมน์ - หากมีการอ้างอิงคอลัมน์โดยวัตถุอื่นใดมันไม่สามารถทิ้งได้ ต้องอ้างอิงวัตถุการอ้างอิงก่อนจากนั้นจึงสามารถลบคอลัมน์ได้

นี่เป็นเรื่องง่ายที่จะแสดงโดยดูที่เนื้อหาของบันทึกการทำธุรกรรมหลังจากวางคอลัมน์ รหัสด้านล่างสร้างตารางที่มีคอลัมน์ถ่านยาว 8,000 รายการ มันเพิ่มแถวจากนั้นลดลงและแสดงเนื้อหาของบันทึกการทำธุรกรรมที่เกี่ยวข้องกับการดำเนินการปล่อย บันทึกข้อมูลแสดงการปรับเปลี่ยนตารางระบบต่างๆที่จัดเก็บคำจำกัดความของตารางและคอลัมน์ หากข้อมูลคอลัมน์ถูกลบจริงจากหน้าเว็บที่จัดสรรให้กับตารางคุณจะเห็นบันทึกบันทึกที่บันทึกข้อมูลหน้าจริง ไม่มีบันทึกดังกล่าว

DROP TABLE IF EXISTS dbo.DropColumnTest;
GO
CREATE TABLE dbo.DropColumnTest
(
    rid int NOT NULL
        CONSTRAINT DropColumnTest_pkc
        PRIMARY KEY CLUSTERED
    , someCol varchar(8000) NOT NULL
);

INSERT INTO dbo.DropColumnTest (rid, someCol)
SELECT 1, REPLICATE('Z', 8000);
GO

DECLARE @startLSN nvarchar(25);

SELECT TOP(1) @startLSN = dl.[Current LSN]
FROM sys.fn_dblog(NULL, NULL) dl
ORDER BY dl.[Current LSN] DESC;

DECLARE @a int = CONVERT(varbinary(8), '0x' + CONVERT(varchar(10),      LEFT(@startLSN, 8), 0), 1)
      , @b int = CONVERT(varbinary(8), '0x' + CONVERT(varchar(10), SUBSTRING(@startLSN, 10, 8), 0), 1)
      , @c int = CONVERT(varbinary(8), '0x' + CONVERT(varchar(10),     RIGHT(@startLSN, 4), 0), 1);

SELECT @startLSN = CONVERT(varchar(8), @a, 1) 
    + ':' + CONVERT(varchar(8), @b, 1) 
    + ':' + CONVERT(varchar(8), @c, 1)

ALTER TABLE dbo.DropColumnTest DROP COLUMN someCol;

SELECT *
FROM sys.fn_dblog(@startLSN, NULL)


--modify an existing data row 
SELECT TOP(1) @startLSN = dl.[Current LSN]
FROM sys.fn_dblog(NULL, NULL) dl
ORDER BY dl.[Current LSN] DESC;

SET @a = CONVERT(varbinary(8), '0x' + CONVERT(varchar(10),      LEFT(@startLSN, 8), 0), 1);
SET @b = CONVERT(varbinary(8), '0x' + CONVERT(varchar(10), SUBSTRING(@startLSN, 10, 8), 0), 1);
SET @c = CONVERT(varbinary(8), '0x' + CONVERT(varchar(10),     RIGHT(@startLSN, 4), 0), 1);

SELECT @startLSN = CONVERT(varchar(8), @a, 1) 
    + ':' + CONVERT(varchar(8), @b, 1) 
    + ':' + CONVERT(varchar(8), @c, 1)

UPDATE dbo.DropColumnTest SET rid = 2;

SELECT *
FROM sys.fn_dblog(@startLSN, NULL)

(เอาต์พุตมีขนาดใหญ่เกินกว่าที่จะแสดงที่นี่และ dbfiddle.uk จะไม่อนุญาตให้ฉันเข้าถึง fn_dblog)

ชุดแรกของการส่งออกแสดงบันทึกเป็นผลมาจากคำสั่ง DDL วางคอลัมน์ ชุดที่สองของการส่งออกแสดงบันทึกหลังจากใช้คำสั่ง DML ที่เราปรับปรุงridคอลัมน์ ในชุดผลลัพธ์ที่สองเราจะเห็นบันทึกบันทึกแสดงการลบต่อ dbo.DropColumnTest ตามด้วยการแทรกลงใน dbo.DropColumnTest ความยาวของบันทึกแต่ละรายการคือ 8116 เพื่อระบุว่าหน้าจริงได้รับการอัพเดต

ดังที่คุณเห็นจากผลลัพธ์ของfn_dblogคำสั่งในการทดสอบด้านบนการดำเนินการทั้งหมดถูกบันทึกอย่างสมบูรณ์ นี่เป็นการกู้คืนแบบง่ายเช่นเดียวกับการกู้คืนแบบเต็ม คำศัพท์ "ล็อกเต็มที่" อาจตีความผิดเนื่องจากการดัดแปลงข้อมูลไม่ถูกบันทึก นี่ไม่ใช่สิ่งที่เกิดขึ้น - การแก้ไขจะถูกบันทึกและสามารถย้อนกลับได้อย่างสมบูรณ์ บันทึกเป็นเพียงเพียงบันทึกหน้าเว็บที่ถูกสัมผัสและเนื่องจากไม่มีตารางข้อมูลหน้าถูกบันทึกไว้โดยการดำเนินการ DDL ทั้งDROP COLUMNและย้อนกลับใด ๆ ที่อาจเกิดขึ้นจะเกิดขึ้นอย่างรวดเร็วมากโดยไม่คำนึงถึงขนาดของตาราง

สำหรับวิทยาศาสตร์รหัสต่อไปนี้จะถ่ายโอนข้อมูลหน้าสำหรับตารางที่รวมอยู่ในรหัสข้างต้นโดยใช้DBCC PAGEสไตล์ "3" สไตล์ "3" หมายถึงเราต้องการให้ส่วนหัวของหน้าพร้อมการตีความต่อแถวอย่างละเอียด รหัสใช้เคอร์เซอร์เพื่อแสดงรายละเอียดสำหรับทุกหน้าในตารางดังนั้นคุณอาจต้องการตรวจสอบให้แน่ใจว่าคุณไม่ได้เรียกใช้สิ่งนี้ในตารางขนาดใหญ่

DBCC TRACEON(3604); --directs out from DBCC commands to the console, instead of the error log
DECLARE @dbid int = DB_ID();
DECLARE @fileid int;
DECLARE @pageid int;
DECLARE cur CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT dpa.allocated_page_file_id
    , dpa.allocated_page_page_id
FROM sys.schemas s  
    INNER JOIN sys.objects o ON o.schema_id = s.schema_id
CROSS APPLY sys.dm_db_database_page_allocations(DB_ID(), o.object_id, NULL, NULL, 'DETAILED') dpa
WHERE o.name = N'DropColumnTest'
    AND s.name = N'dbo'
    AND dpa.page_type_desc = N'DATA_PAGE';
OPEN cur;
FETCH NEXT FROM cur INTO @fileid, @pageid;
WHILE @@FETCH_STATUS = 0
BEGIN
    DBCC PAGE (@dbid, @fileid, @pageid, 3);
    FETCH NEXT FROM cur INTO @fileid, @pageid;
END
CLOSE cur;
DEALLOCATE cur;
DBCC TRACEOFF(3604);

ดูผลลัพธ์สำหรับหน้าแรกจากการสาธิตของฉัน (หลังจากที่คอลัมน์ถูกดร็อป แต่ก่อนที่คอลัมน์จะถูกอัพเดต) ฉันเห็นสิ่งนี้:

หน้า: (1: 100104)


กันชน:


BUF @ 0x0000021793E42040

bpage = 0x000002175A7A0000 bhash = 0x00000000000000000000 bpageno = (1: 100104)
bdbid = 10 Breferences = 1 bcputicks = 0
bsampleCount = 0 bUse1 = 13760 bstat = 0x10b
บล็อก = 0x212121cc bnext = 0x00000000000000000000 bDirtyContext = 0x000002175004B640
bstat2 = 0x0                        

ส่วนหัวของหน้า:


หน้า @ 0x000002175A7A0000

m_pageId = (1: 100104) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0xc000
m_objId (AllocUnitId.idObj) = 300 m_indexId (AllocUnitId.idInd) = 256 
ข้อมูลเมตา: AllocUnitId = 72057594057588736                                
ข้อมูลเมตา: พาร์ติชันรหัส = 72057594051756032 ข้อมูลเมตา: ดัชนีหมายเลข = 1
ข้อมูลเมตา: ObjectId = 174623665 m_prevPage = (0: 0) m_nextPage = (0: 0)
pminlen = 8 m_slotCnt = 1 m_freeCnt = 79
m_freeData = 8111 m_reservedCnt = 0 m_lsn = (616: 14191: 25)
m_xactReserved = 0 m_xdesId = (0: 0) m_ghostRecCnt = 0
m_tornBits = 0 DB Frag ID = 1                      

สถานะการจัดสรร

GAM (1: 2) = จัดสรร SGAM (1: 3) = ไม่จัดสรร          
PFS (1: 97056) = 0x40 จัดสรร 0_PCT_FULL DIFF (1: 6) = เปลี่ยน
ML (1: 7) = ไม่ MIN_LOGGED           

ช่อง 0 ออฟเซ็ต 0x60 ความยาว 8015

ประเภทระเบียน = Primary_RECORD คุณสมบัติบันทึก = NULL_BITMAP VARIABLE_COLUMNS
ขนาดบันทึก = 8015                  
Memory Dump @ 0x000000B75227A060

0000000000000000: 30000800 01000000 02000001 004f1f5a 5a5a5a5a 0 ............ O.ZZZZZ
0000000000000014: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZZ
.
.
.
0000000000001F2C: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZ
0000000000001F40: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a ZZZZZZZZZZZZZZZZZZ

ช่อง 0 คอลัมน์ 1 ชดเชย 0x4 ความยาว 4 ความยาว (ทางกายภาพ) 4

rid = 1                             

ช่อง 0 คอลัมน์ 67108865 ชดเชย 0xf ความยาว 0 ความยาว (ทางกายภาพ) 8000

DROPPED = NULL                      

ช่อง 0 ออฟเซ็ต 0x0 ความยาว 0 ความยาว (ทางกายภาพ) 0

KeyHashValue = (8194443284a0)       

ฉันได้ลบทราฟฟิกหน้าส่วนใหญ่ออกจากผลลัพธ์ที่แสดงด้านบนเพื่อความกระชับ ในตอนท้ายของผลลัพธ์คุณจะเห็นสิ่งนี้สำหรับridคอลัมน์:

ช่อง 0 คอลัมน์ 1 ชดเชย 0x4 ความยาว 4 ความยาว (ทางกายภาพ) 4

rid = 1                             

บรรทัดสุดท้ายด้านบนrid = 1ส่งคืนชื่อของคอลัมน์และค่าปัจจุบันที่เก็บไว้ในคอลัมน์ในหน้า

ถัดไปคุณจะเห็นสิ่งนี้:

ช่อง 0 คอลัมน์ 67108865 ชดเชย 0xf ความยาว 0 ความยาว (ทางกายภาพ) 8000

DROPPED = NULL                      

ผลลัพธ์แสดงให้เห็นว่า Slot 0 มีคอลัมน์ที่ถูกลบโดยอาศัยDELETEDข้อความที่ปกติชื่อคอลัมน์จะเป็น ค่าของคอลัมน์ถูกส่งคืนNULLเนื่องจากลบคอลัมน์แล้ว อย่างไรก็ตามตามที่คุณเห็นในข้อมูลดิบค่าความยาว 8,000 อักขระREPLICATE('Z', 8000)สำหรับคอลัมน์นั้นยังคงมีอยู่บนหน้า นี่เป็นตัวอย่างของส่วนนั้นของเอาต์พุต DBAGE PAGE:

0000000000001EDC: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZ
0000000000001EF0: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZ
0000000000001F04: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZ
0000000000001F18: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZZ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.