ALTER TABLE… DROP COLUMN gerçekten sadece meta veri işlemi mi?


11

ALTER TABLE ... DROP COLUMN, yalnızca meta-veri işlemidir.

Kaynak

Bu nasıl olabilir? Bir DÜŞME SÜTÜRÜ sırasındaki verilerin temeldeki kümelenmemiş dizinlerden ve kümelenmiş dizin / yığından temizlenmesine gerek yok mu?

Ayrıca, Microsoft Docs neden tam olarak günlüğe kaydedilen bir işlem olduğunu ima ediyor?

Tabloda yapılan değişiklikler günlüğe kaydedilir ve tamamen kurtarılabilir. Bir sütunu bırakmak veya SQL Server'ın bazı sürümlerinde, varsayılan değere sahip bir NOT NULL sütun eklemek gibi büyük tablolardaki tüm satırları etkileyen değişikliklerin tamamlanması ve birçok günlük kaydı oluşturulması uzun sürebilir . Bu ALTER TABLE deyimlerini, birçok satırı etkileyen herhangi bir INSERT, UPDATE veya DELETE deyimiyle aynı özenle çalıştırın.

İkincil bir soru olarak: veriler alttaki sayfalardan kaldırılmadıysa motor bırakılan sütunları nasıl izler?


2
Bence dil, ürünün birçok sürümü ve belgelerin daha birçok yinelemesi ile hayatta kaldı. Zaman içinde, sütunları içeren daha fazla işlem yalnızca çevrimiçi / meta veri değişiklikleri haline geldi. Şu anda belki de kötü bir örnek, ancak cümlenin amacı, genel olarak, bazı değiştirme işlemlerinin , her bir belirli senaryoyu listelemek yerine belirli senaryolarda veri boyutu işlemleri olabileceği konusunda sizi uyarmaktır .
Aaron Bertrand

Yanıtlar:


14

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, ridsü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_dblogyukarı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, ridsü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 DELETEDnormalde sütun adının olacağı metin nedeniyle silinmiş bir sütun içerdiğini gösterir . Sütunun değeri, sütun NULLsilindiğ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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.