Có một số trường hợp nhất định trong đó việc thả một cột có thể là một hoạt động chỉ có siêu dữ liệu. Các định nghĩa cột cho bất kỳ bảng đã cho nào không được bao gồm trong mỗi và mỗi trang nơi các hàng được lưu trữ, các định nghĩa cột chỉ được lưu trữ trong siêu dữ liệu cơ sở dữ liệu, bao gồm sys.sysrowsets, sys.sysrscols, v.v.
Khi thả một cột không được tham chiếu bởi bất kỳ đối tượng nào khác, công cụ lưu trữ chỉ cần đánh dấu định nghĩa cột là không còn nữa bằng cách xóa các chi tiết thích hợp khỏi các bảng hệ thống khác nhau. Hành động xóa dữ liệu meta làm mất hiệu lực bộ đệm thủ tục, yêu cầu biên dịch lại bất cứ khi nào một truy vấn sau đó tham chiếu bảng đó. Vì biên dịch lại chỉ trả về các cột hiện tồn tại trong bảng, nên các chi tiết cột cho cột bị rớt thậm chí không bao giờ được yêu cầu; công cụ lưu trữ bỏ qua các byte được lưu trữ trong mỗi trang cho cột đó, như thể cột không còn tồn tại.
Khi một hoạt động DML tiếp theo xảy ra đối với bảng, các trang bị ảnh hưởng sẽ được viết lại mà không có dữ liệu cho cột bị bỏ. Nếu bạn xây dựng lại một chỉ mục được nhóm hoặc một đống, tất cả các byte cho cột bị rơi tự nhiên không được ghi lại vào trang trên đĩa. Điều này có hiệu quả lan truyền tải trọng của việc thả cột theo thời gian, làm cho nó ít được chú ý hơn.
Có những trường hợp bạn không thể thả một cột, chẳng hạn như khi cột được bao gồm trong một chỉ mục hoặc khi bạn đã tạo thủ công một đối tượng thống kê cho cột. Tôi đã viết một bài đăng trên blog cho thấy lỗi được trình bày khi cố gắng thay đổi một cột bằng một đối tượng thống kê được tạo thủ công. Các ngữ nghĩa tương tự được áp dụng khi thả một cột - nếu cột được tham chiếu bởi bất kỳ đối tượng nào khác, thì nó không thể bị loại bỏ. Đối tượng tham chiếu phải được thay đổi trước, sau đó cột có thể được loại bỏ.
Điều này khá dễ dàng để hiển thị bằng cách xem nội dung của nhật ký giao dịch sau khi thả một cột. Mã dưới đây tạo một bảng với một cột char dài 8.000. Nó thêm một hàng, sau đó thả nó và hiển thị nội dung của nhật ký giao dịch áp dụng cho hoạt động thả. Các bản ghi nhật ký hiển thị các sửa đổi cho các bảng hệ thống khác nhau nơi các định nghĩa bảng và cột được lưu trữ. Nếu dữ liệu cột thực sự bị xóa khỏi các trang được phân bổ vào bảng, bạn sẽ thấy các bản ghi nhật ký ghi lại dữ liệu trang thực tế; không có hồ sơ như vậy.
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)
(Đầu ra quá lớn để hiển thị ở đây và dbfiddle.uk sẽ không cho phép tôi truy cập fn_dblog)
Tập hợp đầu ra đầu tiên hiển thị nhật ký là kết quả của câu lệnh DDL thả cột. Bộ đầu ra thứ hai hiển thị nhật ký sau khi chạy câu lệnh DML nơi chúng tôi cập nhật rid
cột. Trong tập kết quả thứ hai, chúng ta thấy các bản ghi nhật ký chỉ ra việc xóa đối với dbo.DropColumnTest, sau đó là chèn vào dbo.DropColumnTest. Mỗi Độ dài bản ghi nhật ký là 8116, cho biết trang thực tế đã được cập nhật.
Như bạn có thể thấy từ đầu ra của fn_dblog
lệnh trong thử nghiệm ở trên, toàn bộ hoạt động được ghi lại đầy đủ. Điều này đi để phục hồi đơn giản, cũng như phục hồi đầy đủ. Thuật ngữ "đăng nhập đầy đủ" có thể bị hiểu sai vì sửa đổi dữ liệu không được ghi lại. Đây không phải là những gì xảy ra - sửa đổi được ghi lại, và có thể được khôi phục hoàn toàn. Nhật ký được đơn giản chỉ ghi lại các trang đã được chạm vào, và vì không ai trong số của bảng dữ liệu trang đã được đăng nhập bởi các hoạt động DDL, cả hai DROP COLUMN
, và bất kỳ rollback có thể xảy ra sẽ xảy ra rất nhanh chóng, không phụ thuộc vào kích thước của bảng.
Đối với khoa học , đoạn mã sau sẽ kết xuất các trang dữ liệu cho bảng được bao gồm trong đoạn mã trên, sử dụng DBCC PAGE
kiểu "3". Kiểu "3" cho biết chúng tôi muốn tiêu đề trang cộng với giải thích chi tiết theo từng hàng . Mã sử dụng một con trỏ để hiển thị chi tiết cho mọi trang trong bảng, vì vậy bạn có thể muốn đảm bảo rằng bạn không chạy mã này trên một bảng lớn.
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);
Nhìn vào đầu ra cho trang đầu tiên từ bản demo của tôi (sau khi cột bị bỏ, nhưng trước khi cột được cập nhật), tôi thấy điều này:
TRANG: (1: 100104)
ĐỆM:
BUF @ 0x0000021793E42040
bpage = 0x000002175A7A0000 bhash = 0x0000000000000000 bpageno = (1: 100104)
bdbid = 10 breferences = 1 bcputicks = 0
bsampleCount = 0 bUse1 = 13760 bstat = 0x10b
blog = 0x212121cc bnext = 0x0000000000000000 bDentyContext = 0x000002175004B640
bstat2 = 0x0
TRANG CHỦ ĐẦU TƯ:
Trang @ 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
Siêu dữ liệu: AllocUnitId = 72057594057588736
Siêu dữ liệu: PartitionId = 72057594051756032 Siêu dữ liệu: IndexId = 1
Siêu dữ liệu: 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
Tình trạng phân bổ
GAM (1: 2) = SGAM ALLOCATED (1: 3) = KHÔNG ĐƯỢC PHÉP
PFS (1: 97056) = 0x40 ĐƯỢC PHÉP 0_PCT_FULL DIFF (1: 6) = THAY ĐỔI
ML (1: 7) = KHÔNG MIN_LOGGED
Khe 0 Offset 0x60 Chiều dài 8015
Loại bản ghi = PRIMARY_RECORD Thuộc tính bản ghi = NULL_BITMAP VARIABLE_COLUMNS
Kích thước bản ghi = 8015
Bộ nhớ kết xuất @ 0x000000B75227A060
0000000000000000: 30000800 01000000 02000001 004f1f5a 5a5a5a5a 0 ............ O.ZZZZZ
0000000000000014: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a Zzzzzzzzzzzzzzzzz
.
.
.
0000000000001F2C: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a
0000000000001F40: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a
Khe 0 Cột 1 Offset 0x4 Chiều dài 4 Chiều dài (vật lý) 4
bỏ = 1
Khe 0 Cột 67108865 Offset 0xf Chiều dài 0 Độ dài (vật lý) 8000
DROPPED = NULL
Khe 0 Offset 0x0 Chiều dài 0 Độ dài (vật lý) 0
KeyHashValue = (8194443284a0)
Tôi đã loại bỏ hầu hết kết xuất trang thô khỏi đầu ra được hiển thị ở trên để đơn giản. Ở cuối đầu ra, bạn sẽ thấy điều này cho rid
cột:
Khe 0 Cột 1 Offset 0x4 Chiều dài 4 Chiều dài (vật lý) 4
bỏ = 1
Dòng cuối cùng ở trên, rid = 1
trả về tên của cột và giá trị hiện tại được lưu trữ trong cột trên trang.
Tiếp theo, bạn sẽ thấy điều này:
Khe 0 Cột 67108865 Offset 0xf Chiều dài 0 Độ dài (vật lý) 8000
DROPPED = NULL
Kết quả đầu ra cho thấy Slot 0 chứa một cột bị xóa, nhờ vào DELETED
văn bản mà tên cột thường là. Giá trị của cột được trả về NULL
vì cột đã bị xóa. Tuy nhiên, như bạn có thể thấy trong dữ liệu thô, giá trị dài 8.000 ký tự REPLICATE('Z', 8000)
, cho cột đó vẫn tồn tại trên trang. Đây là một mẫu của phần đầu ra TRANG DBCC:
0000000000001EDC: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a Zzzzzzzzzzzzzzzzz
0000000000001EF0: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a Zzzzzzzzzzzzzzzzzzz
0000000000001F04: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a
0000000000001F18: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a