Bir sütunu bırakmanın yalnızca meta veri içeren bir işlem olabileceği belirli durumlar vardır. Belirli bir tablo için sütun tanımları, satırların depolandığı her sayfaya dahil edilmez, sütun tanımları yalnızca sys.sysrowsets, sys.sysrscols vb. Dahil olmak üzere veritabanı meta verilerinde depolanır.
Başka bir nesne tarafından başvurulmayan bir sütunu düşürürken, depolama motoru, çeşitli sistem tablolarından ilgili ayrıntıları silerek sütun tanımını artık yok olarak işaretler. Meta verileri silme eylemi, yordam önbelleğini geçersiz kılar ve bir sorgu daha sonra bu tabloya başvurduğunda yeniden derlemeyi gerektirir. Yeniden derleme yalnızca şu anda tabloda var olan sütunları döndürdüğünden , bırakılan sütunun sütun ayrıntıları hiçbir zaman istenmez; depolama motoru, bu sütun için her sayfada depolanan baytları, sütun artık yokmuş gibi atlar.
Tabloda bir sonraki DML işlemi gerçekleştiğinde, etkilenen sayfalar bırakılan sütuna ait veriler olmadan yeniden yazılır. Kümelenmiş bir dizini veya bir yığını yeniden oluşturursanız, bırakılan sütunun tüm baytları doğal olarak diskteki sayfaya geri yazılmaz. Bu, sütunu zamanla düşürmenin yükünü etkili bir şekilde yayar ve daha az fark edilir hale getirir.
Sütunun bir dizine eklendiği veya sütun için manuel olarak bir istatistik nesnesi oluşturduğunuz gibi bir sütunu bırakamayacağınız durumlar vardır. Manuel olarak oluşturulan bir istatistik nesnesiyle bir sütun değiştirmeye çalışırken sunulan hatayı gösteren bir blog yazısı yazdım . Bir sütunu bırakarak aynı semantik uygulamak - sütun ile başvurulursa, herhangi başka bir nesne, sadece düştü edilemez. Önce referans nesnesi değiştirilmelidir, daha sonra sütun atılabilir.
Bir sütunu bıraktıktan sonra işlem günlüğünün içeriğine bakarak bunu göstermek oldukça kolaydır. Aşağıdaki kod, tek bir 8.000 uzunluğunda sütun içeren bir tablo oluşturur. Bir satır ekler, sonra bırakır ve bırakma işlemi için geçerli olan işlem günlüğünün içeriğini görüntüler. Günlük kayıtları, tablo ve sütun tanımlarının depolandığı çeşitli sistem tablolarında yapılan değişiklikleri gösterir. Sütun verileri gerçekten tabloya atanan sayfalardan siliniyorsa, gerçek sayfa verilerini kaydeden günlük kayıtlarını görürsünüz; böyle bir kayıt yok.
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)
(Çıktı burada gösterilemeyecek kadar büyük ve dbfiddle.uk fn_dblog dosyasına erişmeme izin vermiyor)
İlk çıktı kümesi, sütunu bırakan DDL ifadesinin sonucu olarak günlüğü gösterir. İkinci çıktı kümesi, rid
sütunu güncellediğimiz DML ifadesini çalıştırdıktan sonra günlüğü gösterir . İkinci sonuç kümesinde, dbo.DropColumnTest'e karşı bir silme ve ardından dbo.DropColumnTest'e bir ekleme olduğunu gösteren günlük kayıtlarını görüyoruz. Her Günlük Kayıt Uzunluğu, gerçek sayfanın güncellendiğini gösteren 8116'dır.
Eğer çıkışından görebileceğiniz gibi fn_dblog
yukarıdaki testte komuta, tüm operasyon olduğunu tam olarak kaydedilir. Bu, basit kurtarma ve tam kurtarma için geçerlidir. Veri değişikliği günlüğe kaydedilmediğinden "tamamen günlüğe kaydedilmiş" terminolojisi yanlış yorumlanabilir. Modifikasyon - Bu ne değildir edilir açmış ve tamamen geri çekilebilir. Günlük sadece bir tek dokundu sayfaları kaydetme ve tablonun veri sayfalarının hiçbirinde bu yana DDL operasyonla günlüğe, hem DROP COLUMN
, ve oluşabilecek her türlü geri alma bakılmaksızın tablonun boyutu, son derece hızlı bir şekilde gerçekleşir.
Bilim için , aşağıdaki kod, DBCC PAGE
"3" stilini kullanarak yukarıdaki kodda yer alan tabloya ait veri sayfalarını dökecektir . "3" stili, sayfa üstbilgisi ve ayrıntılı satır başına yorumlama istediğimizi gösterir . Kod , tablodaki her sayfanın ayrıntılarını görüntülemek için bir imleç kullanır , bu nedenle bunu büyük bir tabloda çalıştırmadığınızdan emin olmak isteyebilirsiniz.
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);
Demo sayfamdaki ilk sayfanın çıktısına baktığımda (sütun bırakıldıktan sonra, ancak sütun güncellenmeden önce) şunu görüyorum:
SAYFA: (1: 100104)
TAMPON:
BUF @ 0x0000021793E42040
bpage = 0x000002175A7A0000 bhash = 0x0000000000000000 bpageno = (1: 100104)
bdbid = 10 ırk = 1 bcputicks = 0
bsampleCount = 0 bKullanım1 = 13760 bstat = 0x10b
blog = 0x212121cc bnext = 0x0000000000000000 bDirtyContext = 0x000002175004B640
bstat2 = 0x0
SAYFA BAŞLIĞI:
Sayfa @ 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
Meta veriler: AllocUnitId = 72057594057588736
Meta veriler: PartitionId = 72057594051756032 Meta veriler: IndexId = 1
Meta veriler: 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 Parça Kimliği = 1
Tahsis Durumu
GAM (1: 2) = TAHSİSLİ SGAM (1: 3) = TAHSİS EDİLMİYOR
PFS (1: 97056) = 0x40 TAHSİSLİ 0_PCT_FULL FARK (1: 6) = DEĞİŞTİRİLDİ
ML (1: 7) = MIN_LOGGED DEĞİL
Yuva 0 Ofset 0x60 Uzunluk 8015
Kayıt Türü = PRIMARY_RECORD Kayıt Özellikleri = NULL_BITMAP VARIABLE_COLUMNS
Kayıt Boyutu = 8015
Bellek Dökümü @ 0x000000B75227A060
0000000000000000: 30000800 01000000 02000001 004f1f5a 5a5a5a5a 0 ............ O.ZZZZZ
0000000000000014: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZ
.
.
.
0000000000001F2C: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZ
0000000000001F40: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a ZZZZZZZZZZZZZZZ
Yuva 0 Sütun 1 Ofset 0x4 Uzunluk 4 Uzunluk (fiziksel) 4
kurtulmak = 1
Yuva 0 Sütun 67108865 Ofset 0xf Uzunluk 0 Uzunluk (fiziksel) 8000
DROPPED = NULL
Yuva 0 Ofset 0x0 Uzunluk 0 Uzunluk (fiziksel) 0
KeyHashValue = (8194443284a0)
Ham sayfa dökümü çoğunu kısalık için yukarıda gösterilen çıktıdan kaldırdım. Çıktının sonunda, rid
sütun için bunu göreceksiniz :
Yuva 0 Sütun 1 Ofset 0x4 Uzunluk 4 Uzunluk (fiziksel) 4
kurtulmak = 1
Yukarıdaki son satır rid = 1
, sütunun adını ve sayfadaki sütunda depolanan geçerli değeri döndürür.
Sonra şunu göreceksiniz:
Yuva 0 Sütun 67108865 Ofset 0xf Uzunluk 0 Uzunluk (fiziksel) 8000
DROPPED = NULL
Çıktı, Alan 0'ın DELETED
normalde sütun adının olacağı metin nedeniyle silinmiş bir sütun içerdiğini gösterir . Sütunun değeri, sütun NULL
silindiği için döndürülür . Ancak, ham verilerde de görebileceğiniz gibi, REPLICATE('Z', 8000)
bu sütun için 8.000 karakter uzunluğunda değer, sayfada hala var. Bu, DBCC PAGE çıktısının bu kısmının bir örneğidir:
0000000000001EDC: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZ
0000000000001EF0: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZ
0000000000001F04: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZ
0000000000001F18: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZZ