Ada keadaan tertentu di mana menjatuhkan kolom bisa menjadi operasi meta-data-only. Definisi kolom untuk setiap tabel yang diberikan tidak termasuk dalam setiap halaman di mana baris disimpan, definisi kolom hanya disimpan dalam metadata database, termasuk sys.sysrowsets, sys.sysrscols, dll.
Ketika menjatuhkan kolom yang tidak direferensikan oleh objek lain, mesin penyimpanan hanya menandai definisi kolom sebagai tidak lagi hadir dengan menghapus rincian terkait dari berbagai tabel sistem. Tindakan menghapus meta-data membatalkan cache prosedur, mengharuskan kompilasi ulang setiap kali kueri selanjutnya referensi tabel itu. Karena kompilasi hanya mengembalikan kolom yang saat ini ada dalam tabel, detail kolom untuk kolom yang dijatuhkan bahkan tidak pernah diminta; mesin penyimpanan melewatkan byte yang disimpan di setiap halaman untuk kolom itu, seolah-olah kolom itu tidak ada lagi.
Ketika operasi DML berikutnya terjadi terhadap tabel, halaman yang terpengaruh ditulis ulang tanpa data untuk kolom yang dijatuhkan. Jika Anda membangun kembali indeks berkerumun atau tumpukan, semua byte untuk kolom yang dijatuhkan secara alami tidak ditulis kembali ke halaman pada disk. Ini secara efektif menyebarkan beban menjatuhkan kolom dari waktu ke waktu, membuatnya kurang terlihat.
Ada beberapa situasi di mana Anda tidak bisa menjatuhkan kolom, seperti ketika kolom dimasukkan dalam indeks, atau ketika Anda secara manual membuat objek statistik untuk kolom. Saya menulis posting blog yang menunjukkan kesalahan yang disajikan ketika mencoba mengubah kolom dengan objek statistik yang dibuat secara manual. Semantik yang sama berlaku ketika menjatuhkan kolom - jika kolom direferensikan oleh setiap objek lain, tidak bisa begitu saja dijatuhkan. Objek referensi harus diubah terlebih dahulu, kemudian kolom bisa dijatuhkan.
Ini cukup mudah untuk ditampilkan dengan melihat isi dari log transaksi setelah menjatuhkan kolom. Kode di bawah ini membuat tabel dengan satu kolom char panjang 8.000. Ia menambahkan satu baris, kemudian menjatuhkannya, dan menampilkan isi dari log transaksi yang berlaku untuk operasi drop. Catatan log menunjukkan modifikasi ke berbagai tabel sistem tempat definisi tabel dan kolom disimpan. Jika data kolom sebenarnya dihapus dari halaman yang dialokasikan untuk tabel, Anda akan melihat catatan log merekam data halaman yang sebenarnya; tidak ada catatan seperti itu.
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)
(Outputnya terlalu besar untuk ditampilkan di sini, dan dbfiddle.uk tidak akan mengizinkan saya mengakses fn_dblog)
Set output pertama menunjukkan log sebagai hasil dari pernyataan DDL menjatuhkan kolom. Set output kedua menunjukkan log setelah menjalankan pernyataan DML tempat kami memperbarui rid
kolom. Di set hasil kedua, kita melihat catatan log yang menunjukkan penghapusan terhadap dbo.DropColumnTest, diikuti oleh masukkan ke dbo.DropColumnTest. Setiap Panjang Catatan Log adalah 8116, menunjukkan halaman yang sebenarnya telah diperbarui.
Seperti yang dapat Anda lihat dari output dari fn_dblog
perintah dalam tes di atas, seluruh operasi yang sepenuhnya login. Ini berlaku untuk pemulihan sederhana, serta pemulihan penuh. Terminologi "sepenuhnya dicatat" mungkin disalahartikan karena modifikasi data tidak dicatat. Ini bukan apa yang terjadi - modifikasi adalah login, dan dapat sepenuhnya digulung kembali. Log hanya merekam halaman-halaman yang disentuh, dan karena tidak ada halaman data tabel yang dicatat oleh operasi DDL, baik itu DROP COLUMN
, dan setiap rollback yang mungkin terjadi akan terjadi dengan sangat cepat, terlepas dari ukuran tabel.
Untuk sains , kode berikut akan membuang halaman data untuk tabel yang termasuk dalam kode di atas, menggunakan DBCC PAGE
, gaya "3". Gaya "3" menunjukkan kami menginginkan tajuk halaman plus interpretasi per baris terperinci . Kode menggunakan kursor untuk menampilkan detail untuk setiap halaman dalam tabel, jadi Anda mungkin ingin memastikan Anda tidak menjalankan ini pada tabel besar.
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);
Melihat output untuk halaman pertama dari demo saya (setelah kolom dijatuhkan, tetapi sebelum kolom diperbarui), saya melihat ini:
HALAMAN: (1: 100104)
PENYANGGA:
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 bDirtyContext = 0x000002175004B640
bstat2 = 0x0
KEPALA HALAMAN:
Halaman @ 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
Metadata: AllocUnitId = 72057594057588736
Metadata: PartitionId = 72057594051756032 Metadata: IndexId = 1
Metadata: 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
Status Alokasi
GAM (1: 2) = ALOKASI SGAM (1: 3) = TIDAK DIALOKASI
PFS (1: 97056) = 0x40 ALLOCATED 0_PCT_FULL DIFF (1: 6) = CHANGED
ML (1: 7) = TIDAK MIN_LOGGED
Slot 0 Offset 0x60 Panjang 8015
Jenis Catatan = PRIMARY_RECORD Atribut Catatan = NULL_BITMAP VARIABLE_COLUMNS
Ukuran Rekaman = 8015
Memory Dump @ 0x000000B75227A060
0000000000000000: 30000800 01000000 02000001 004f1f5a 5a5a5a5a 0 ............ O.ZZZZZ
0000000000000014: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZZZZ
.
.
.
0000000000001F2C: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZZZ
0000000000001F40: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a ZZZZZZZZZZZZZZZZZZZ
Slot 0 Kolom 1 Offset 0x4 Panjang 4 Panjang (fisik) 4
rid = 1
Slot 0 Kolom 67108865 Offset 0xf Panjang 0 Panjang (fisik) 8000
DROPPED = NULL
Slot 0 Offset 0x0 Panjang 0 Panjang (fisik) 0
KeyHashValue = (8194443284a0)
Saya telah menghapus sebagian besar dump halaman mentah dari output yang ditunjukkan di atas untuk singkatnya. Di akhir output, Anda akan melihat ini untuk rid
kolom:
Slot 0 Kolom 1 Offset 0x4 Panjang 4 Panjang (fisik) 4
rid = 1
Baris terakhir di atas,, rid = 1
mengembalikan nama kolom, dan nilai saat ini disimpan dalam kolom di halaman.
Selanjutnya, Anda akan melihat ini:
Slot 0 Kolom 67108865 Offset 0xf Panjang 0 Panjang (fisik) 8000
DROPPED = NULL
Output menunjukkan bahwa Slot 0 berisi kolom yang dihapus, berdasarkan DELETED
teks di mana nama kolom biasanya. Nilai kolom dikembalikan NULL
karena kolom telah dihapus. Namun, seperti yang dapat Anda lihat di data mentah, nilai karakter 8.000 REPLICATE('Z', 8000)
, untuk kolom itu masih ada di halaman. Ini adalah contoh dari bagian dari output PAGE DBCC:
0000000000001EDC: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZZZ
0000000000001EF0: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZZZ
0000000000001F04: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZZZ
0000000000001F18: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZZZZZZZ