क्या वास्तव में… DROP COLUMN वास्तव में केवल मेटाडेटा ऑपरेशन है?


11

मुझे कई स्रोत मिले हैं जो बताए गए हैं कि राज्य ... DROP COLUMN एक मेटा-डेटा केवल ऑपरेशन है।

स्रोत

यह कैसे हो सकता है? क्या DROP COLUMN के दौरान डेटा को अंतर्निहित गैर-संकुल इंडेक्स और क्लस्टर इंडेक्स / हीप से शुद्ध करने की आवश्यकता नहीं है?

इसके अलावा, Microsoft डॉक्स का मतलब यह क्यों है कि यह पूरी तरह से लॉग ऑपरेशन है?

तालिका में किए गए संशोधन लॉग और पूरी तरह से पुनर्प्राप्त करने योग्य हैं। परिवर्तन जो बड़ी तालिकाओं में सभी पंक्तियों को प्रभावित करते हैं, जैसे कि कॉलम को ड्रॉप करना या SQL सर्वर के कुछ संस्करणों पर, डिफ़ॉल्ट मान के साथ एक नॉट NULL कॉलम जोड़ना, कई लॉग रिकॉर्ड को पूरा करने और उत्पन्न करने में लंबा समय ले सकता है । किसी भी INSERT, UPDATE, या DELETE स्टेटमेंट की तरह ही देखभाल के साथ इन TABLE TAB स्टेटमेंट को चलाएं जो कई पंक्तियों को प्रभावित करता है।

एक माध्यमिक प्रश्न के रूप में: यदि अंतर्निहित पन्नों से डेटा नहीं हटाया जाता है तो इंजन कैसे गिराए गए कॉलम का ट्रैक रखता है?


2
खैर, मुझे लगता है कि उत्पाद के कई संस्करणों और प्रलेखन के कई और पुनरावृत्तियों के माध्यम से भाषा बच गई है। समय के साथ, कॉलम से जुड़े अधिक से अधिक ऑपरेशन ऑनलाइन / मेटाडेटा में बदल गए हैं। यह शायद अब एक बुरी विशिष्ट उदाहरण है, लेकिन सजा के उद्देश्य केवल चेतावनी देने के लिए है कि सामान्य रूप में, है कुछ बदलने संचालन हो सकता है में आकार के- डेटा संचालन होना निश्चित सूची परिदृश्यों, बजाय हर एक विशिष्ट परिदृश्य बाहर।
हारून बर्ट्रेंड

जवाबों:


14

ऐसी कुछ परिस्थितियाँ हैं, जहाँ एक कॉलम को छोड़ना मेटा-डेटा-ओनली ऑपरेशन हो सकता है। किसी भी दी गई तालिका के लिए स्तंभ की परिभाषाएं प्रत्येक पृष्ठ में शामिल नहीं होती हैं और जहाँ पंक्तियाँ संग्रहीत होती हैं, स्तंभ परिभाषाएँ केवल डेटाबेस मेटाडेटा में संग्रहीत की जाती हैं, जिसमें sys.sysrowsets, sys.sysrscols, आदि शामिल हैं।

किसी अन्य ऑब्जेक्ट द्वारा संदर्भित नहीं होने वाले स्तंभ को छोड़ने पर, भंडारण इंजन बस स्तंभ परिभाषा को चिह्नित करता है, जो कि विभिन्न सिस्टम तालिकाओं से प्रासंगिक विवरण हटाकर अब मौजूद नहीं है। मेटा-डेटा को हटाने की कार्रवाई प्रक्रिया कैश को अमान्य कर देती है, जब भी कोई क्वेरी उस तालिका के संदर्भ में एक recompile की आवश्यकता होती है। के बाद से recompile स्तंभ ही रिटर्न वर्तमान तालिका में मौजूद हैं, गिरा दिया स्तंभ के लिए स्तंभ विवरण भी के लिए कहा कभी नहीं कर रहे हैं; भंडारण इंजन उस कॉलम के लिए प्रत्येक पृष्ठ में संग्रहीत बाइट्स को छोड़ देता है, जैसे कि स्तंभ अब मौजूद नहीं है।

जब एक बाद का डीएमएल ऑपरेशन टेबल के खिलाफ होता है, तो जो पेज प्रभावित होते हैं, उन्हें बिना गिराए गए कॉलम के डेटा के साथ फिर से लिखा जाता है। यदि आप किसी क्लस्टर इंडेक्स या हीप को फिर से बनाते हैं, तो ड्रॉप किए गए कॉलम के सभी बाइट्स स्वाभाविक रूप से डिस्क पर पेज पर वापस नहीं लिखे जाते हैं। यह प्रभावी रूप से समय के साथ स्तंभ छोड़ने का भार फैलाता है, जिससे यह कम ध्यान देने योग्य होता है।

ऐसी परिस्थितियाँ होती हैं जहाँ आप किसी कॉलम को नहीं छोड़ सकते हैं, जैसे कि जब कॉलम को एक इंडेक्स में शामिल किया जाता है, या जब आपने मैन्युअल रूप से कॉलम के लिए एक ऑब्जेक्ट ऑब्जेक्ट बनाया है। मैंने एक ब्लॉग पोस्ट लिखी है जिसमें त्रुटि दिखाई गई है जो मैन्युअल रूप से बनाई गई सांख्यिकी ऑब्जेक्ट के साथ एक कॉलम को बदलने का प्रयास करते समय प्रस्तुत की जाती है। स्तंभ को छोड़ते समय एक ही शब्दार्थ लागू होता है - यदि स्तंभ को किसी अन्य ऑब्जेक्ट द्वारा संदर्भित किया जाता है , तो इसे बस नहीं छोड़ा जा सकता है। संदर्भित ऑब्जेक्ट को पहले बदल दिया जाना चाहिए, फिर स्तंभ को गिराया जा सकता है।

यह एक स्तंभ छोड़ने के बाद लेनदेन लॉग की सामग्री को देखकर दिखाना काफी आसान है। नीचे दिया गया कोड एकल 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 तक पहुंचने की अनुमति नहीं देगा)

कॉलम छोड़ने वाले डीडीएल स्टेटमेंट के परिणामस्वरूप आउटपुट का पहला सेट लॉग दिखाता है। आउटपुट का दूसरा सेट डीएमएल स्टेटमेंट को चलाने के बाद लॉग दिखाता है जहां हम ridकॉलम अपडेट करते हैं । दूसरे परिणाम सेट में, हम लॉग रिकॉर्ड्स को dbo.DropColumnTest के विरुद्ध हटाए जाने का संकेत देते हैं, इसके बाद dbo.DropColumnTest में सम्मिलित करें। प्रत्येक लॉग रिकॉर्ड लंबाई 8116 है, यह दर्शाता है कि वास्तविक पृष्ठ अपडेट किया गया था।

आप के उत्पादन में से देख सकते हैं fn_dblogइसके बाद के संस्करण की परीक्षा में आदेश, पूरे आपरेशन किया जाता है पूरी तरह से लॉग इन किया। यह सरल वसूली के लिए जाता है, साथ ही पूर्ण वसूली भी। शब्दावली "पूरी तरह से लॉग इन" शायद गलत तरीके से व्याख्या की गई क्योंकि डेटा संशोधन लॉग नहीं है। ऐसा नहीं होता है - संशोधन लॉग होता है , और पूरी तरह से वापस रोल किया जा सकता है। लॉग केवल उन पृष्ठों को रिकॉर्ड कर रहा है जिन्हें छुआ गया था, और चूंकि तालिका के डेटा-पृष्ठों में से कोई भी डीडीएल ऑपरेशन द्वारा लॉग नहीं किया गया था, दोनों DROP COLUMN, और कोई भी रोलबैक हो सकता है जो तालिका के आकार की परवाह किए बिना, बहुत तेज़ी से होगा।

विज्ञान के लिए , निम्न कोड DBCC PAGE"3", शैली, "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 = 0x0000000000000000 bpageno = (1% 100%)
bdbid = 10 breferences = 1 bcputicks = 0
bsampleCount = 0 bUse1 = 13760 bstat = 0x10b
ब्लॉग = 0x212121cc bnext = 0x0000000000000000 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 
मेटाडेटा: ऑलोक्युनीट = 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                  
मेमोरी डंप @ 0x000000B75227A060

0000000000000000: 30000800 01000000 02000001 004f1f5a 5a5a5a5a 0 ............ O.ZZZZZ
0000000000000014: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZ
।
।
।
0000000000001F2C: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZ
0000000000001F40: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a ZZZZZZZZZZZZZZZ

स्लॉट 0 कॉलम 1 ऑफसेट 0x4 लंबाई 4 लंबाई (भौतिक) 4

छुटकारा = १                             

स्लॉट 0 कॉलम 67108865 ऑफसेट 0xf लंबाई 0 लंबाई (भौतिक) 8000

गिरा = पूरा                      

स्लॉट 0 ऑफसेट 0x0 लंबाई 0 लंबाई (भौतिक) 0

KeyHashValue = (8194443284a0)       

मैंने कच्चे माल के अधिकांश पृष्ठ को संक्षिप्तता के लिए ऊपर दिखाए गए आउटपुट से हटा दिया है। आउटपुट के अंत में, आप इसे ridकॉलम के लिए देखेंगे :

स्लॉट 0 कॉलम 1 ऑफसेट 0x4 लंबाई 4 लंबाई (भौतिक) 4

छुटकारा = १                             

उपरोक्त अंतिम पंक्ति, rid = 1कॉलम का नाम और पृष्ठ पर कॉलम में संग्रहीत वर्तमान मान लौटाती है।

अगला, आप इसे देखेंगे:

स्लॉट 0 कॉलम 67108865 ऑफसेट 0xf लंबाई 0 लंबाई (भौतिक) 8000

गिरा = पूरा                      

आउटपुट से पता चलता है कि स्लॉट 0 में एक हटाए गए कॉलम होते हैं, DELETEDपाठ के आधार पर जहां कॉलम नाम सामान्य रूप से होगा। NULLकॉलम हटा दिए जाने के बाद से कॉलम का मान वापस आ गया है। हालाँकि, जैसा कि आप कच्चे डेटा में देख सकते हैं, 8,000 वर्ण लंबा मान, REPLICATE('Z', 8000)उस स्तंभ के लिए अभी भी पृष्ठ पर मौजूद है। यह DBCC पेज आउटपुट के उस हिस्से का एक नमूना है:

0000000000001EDC: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZ
0000000000001EFEF0: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZ
0000000000001F04: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZ
0000000000001FF18: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a5a 5a5a5a5a ZZZZZZZZZZZZZZZZZZZ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.