2008-11-13 14 views
49

कई रिकॉर्ड्स वाले किसी तालिका में न्यूल कॉलम जोड़ने के लिए, एक डिफॉल्ट बाधा लागू करने की आवश्यकता है। यह बाधा पूरी ALTER तालिका कमांड को चलाने के लिए लंबे समय तक ले जाती है यदि तालिका बहुत बड़ी है।SQL सर्वर में एक बड़ी तालिका में आप एक न्यूल कॉलम कैसे जोड़ते हैं?

अनुमान:

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

संभावित समाधान:

  1. यह चूसो अप और प्रक्रिया को पूरा करने के लिए प्रतीक्षा करें। बस समय सीमा अवधि बहुत लंबे समय तक सेट करना सुनिश्चित करें। इसके साथ समस्या यह है कि रिकॉर्ड के # के आधार पर इसे करने में घंटों या दिन लग सकते हैं।
  2. कॉलम जोड़ें लेकिन न्यूल को अनुमति दें। इसके बाद, मौजूदा पंक्तियों के लिए DEFAULT मान सेट करने के लिए एक अद्यतन क्वेरी चलाएं। अपडेट न करें *। एक समय में रिकॉर्ड के बैच अपडेट करें या आप समाधान # 1 के समान समस्या के साथ समाप्त हो जाएंगे। इस दृष्टिकोण के साथ समस्या यह है कि आप एक कॉलम के साथ समाप्त होते हैं जो NULL को अनुमति देता है जब आप जानते हैं कि यह एक अनावश्यक विकल्प है। मेरा मानना ​​है कि वहां कुछ बेहतरीन अभ्यास दस्तावेज हैं जो कहते हैं कि आपके पास ऐसे कॉलम नहीं होने चाहिए जो शून्य होने की अनुमति न दें जब तक कि यह आवश्यक न हो।
  3. उसी स्कीमा के साथ एक नई तालिका बनाएं। उस स्कीमा में कॉलम जोड़ें। मूल तालिका से डेटा को स्थानांतरित करें। मूल तालिका ड्रॉप करें और नई तालिका का नाम बदलें। मुझे यकीन नहीं है कि यह # 1 से बेहतर कैसे है।

सवाल:

  1. मेरी मान्यताओं सही हैं?
  2. क्या ये मेरे एकमात्र समाधान हैं? यदि हां, तो कौन सा सबसे अच्छा है? मैं नहीं, मैं और क्या कर सकता था?

उत्तर

57

मैं भी अपने काम के लिए इस समस्या का सामना किया। और मेरा समाधान # 2 के साथ है।

1) एक डिफ़ॉल्ट मान के साथ तालिका में कॉलम जोड़ें:

यहाँ (मैं उपयोग कर रहा हूँ SQL सर्वर 2005) मेरे कदम हैं

ALTER TABLE MyTable ADD MyColumn varchar(40) DEFAULT('') 

2) NOCHECK के साथ एक NOT NULL स्थिर बिंदु जोड़ें विकल्प। NOCHECK मौजूदा मूल्यों पर लागू नहीं करता है:

ALTER TABLE MyTable WITH NOCHECK 
ADD CONSTRAINT MyColumn_NOTNULL CHECK (MyColumn IS NOT NULL) 

3) संवर्द्धित अद्यतन मूल्यों तालिका में:

GO 
UPDATE TOP(3000) MyTable SET MyColumn = '' WHERE MyColumn IS NULL 
GO 1000 
  • अद्यतन बयान केवल अधिकतम 3000 रिकॉर्ड अद्यतन करेगा। यह उस समय डेटा का एक हिस्सा सहेजने की अनुमति देता है। मुझे "MyColumn IS NULL" का उपयोग करना है क्योंकि मेरी तालिका में अनुक्रम प्राथमिक कुंजी नहीं है।

  • GO 1000 पिछले कथन को 1000 बार निष्पादित करेगा। यदि आपको इस नंबर को और बढ़ाने की आवश्यकता है, तो यह 3 मिलियन रिकॉर्ड अपडेट करेगा। यह तब तक निष्पादित रहेगा जब तक कि SQL सर्वर अद्यतन विवरण के लिए 0 रिकॉर्ड्स लौटाता है।

+53

मुझे नहीं पता था कि आप गो के बाद एक नंबर डाल सकते हैं। आपने मेरी दुनिया बदल दी है –

+2

यह उल्लेखनीय है कि एमएसडीएन दृढ़ता से 'NOCHECK' का उपयोग करके हतोत्साहित करता है:" यदि आप मौजूदा डेटा के खिलाफ नई जांच या विदेशी कुंजी बाधाओं को सत्यापित नहीं करना चाहते हैं, तो NOCHECK के साथ उपयोग करें। हम दुर्लभ मामलों को छोड़कर ऐसा करने की अनुशंसा नहीं करते हैं। नया सभी बाद के डेटा अपडेट में बाधा का मूल्यांकन किया जाएगा। किसी भी बाधा उल्लंघन जो कि जब भी बाधा डाली जाती है तो NOCHECK द्वारा दबाए जाते हैं, तो भविष्य में अपडेट विफल हो सकते हैं यदि वे डेटा के साथ पंक्तियों को अपडेट करते हैं जो बाधा का पालन नहीं करते हैं। " –

+2

इसके अलावा: "क्वेरी ऑप्टिमाइज़र उनको बाधाओं पर विचार नहीं करता है जिन्हें NOCHECK के साथ परिभाषित किया गया है। इस तरह की बाधाओं को तब तक अनदेखा कर दिया जाता है जब तक कि वे सभी चेक कंसस्ट्रेंट के साथ ALTER तालिका

का उपयोग करके पुनः सक्षम नहीं होते हैं।" –

0

मुझे लगता है कि यह आपके द्वारा उपयोग किए जा रहे एसक्यूएल स्वाद पर निर्भर करता है, लेकिन यदि आपने विकल्प 2 लिया है, तो अंत में तालिका तालिका को डिफ़ॉल्ट मान के साथ शून्य नहीं करने के लिए बदलें?

क्या यह तेज़ होगा, क्योंकि यह देखता है कि सभी मान शून्य नहीं हैं?

-3

तालिका को लंबवत रूप से विभाजित करें। इसका मतलब है कि आपके पास दो प्राथमिक तालिकाएं होंगी, वही प्राथमिक कुंजी के साथ, और रिकॉर्ड्स की एक ही संख्या होगी ... एक आपके पास पहले से ही होगा, दूसरे के पास केवल कुंजी होगी, और नया गैर-शून्य कॉलम होगा डिफ़ॉल्ट मान) । सभी को सम्मिलित करें, अपडेट करें और कोड हटाएं ताकि वे दो तालिकाओं को synch में रख सकें ... यदि आप चाहते हैं कि आप एक दृश्य बना सकें जो दोनों तालिकाओं को एक साथ जोड़ता है जो कि दो जैसा दिखता है ग्राहक के लिए एकल तालिका कथन का चयन करें ...

+0

क्या आपका मतलब लंबवत है? –

+0

आह हाँ, एक टी याद किया ... दुर्भाग्य से मैं एक 2 उंगली typer ... –

+1

यह एक बहुत ही खतरनाक समाधान की तरह लगता है। यह सही तरीका है। यदि आप इस समाधान का प्रयास करते हैं, तो दो टेबलों में शामिल होने वाला नया दृश्य मूल तालिका का नाम होना चाहिए (इसलिए कोई क्लाइंट कोड बदलने की आवश्यकता नहीं है)। – rmeador

3

यहाँ मैं क्या कोशिश करेगा है:

  • डेटाबेस की एक पूर्ण बैकअप है।
  • नया कॉलम जोड़ें, नल की अनुमति दें - डिफ़ॉल्ट सेट न करें।
  • सरल पुनर्प्राप्ति सेट करें, जो प्रत्येक बैच के रूप में जल्द ही ट्रैन लॉग को छोटा करता है।
  • एसक्यूएल है: ALTER डेटाबेस XXX सेट वसूली सरल
  • बैचों के रूप में आप ऊपर चर्चा की, हर एक के बाद करने में अद्यतन चलाएँ।
  • नए कॉलम को रीसेट करने के लिए अब नल की अनुमति दें।
  • सामान्य पूर्ण रिकवरी पर वापस जाएं।
  • एसक्यूएल है: ALTER डेटाबेस XXX सेट वसूली पूर्ण
  • बैकअप डेटाबेस फिर से।

सिंपल रिकवरी मॉडल का उपयोग लॉगिंग को रोकता नहीं है, लेकिन यह इसके प्रभाव को काफी कम करता है। ऐसा इसलिए है क्योंकि प्रत्येक प्रतिबद्धता के बाद सर्वर रिकवरी जानकारी को त्याग देता है।

+0

तालिका के आकार के आधार पर, सुनिश्चित करें कि आपके पास पर्याप्त लॉग स्थान है। कॉलम को रीसेट करने के लिए अब नल की अनुमति नहीं है लॉग गहन है। – Lamar

-1

मैं अद्यतन के बजाय CURSOR का उपयोग करूंगा। कर्सर बैच में सभी मिलान रिकॉर्ड अपडेट करेगा, रिकॉर्ड द्वारा रिकॉर्ड - इसमें समय लगता है लेकिन ताले टेबल नहीं।

यदि आप ताले से बचने के लिए WAIT का उपयोग करना चाहते हैं।

मुझे भी यकीन नहीं है कि डिफॉल्ट मौजूदा पंक्तियों को बाधित करता है। संभवतः लेखक द्वारा वर्णित केस के कारण DEFAULT के साथ एक साथ उपयोग को सीमित नहीं किया जाता है।

यह बदलता है, तो अंत में जोड़ सकते हैं ताकि स्यूडोकोड दिखेगा की तरह:

-- without NOT NULL constrain -- we will add it in the end 
ALTER TABLE table ADD new_column INT DEFAULT 0 

DECLARE fillNullColumn CURSOR LOCAL FAST_FORWARD 
    SELECT 
     key 
    FROM 
     table WITH (NOLOCK) 
    WHERE 
     new_column IS NULL 

OPEN fillNullColumn 

DECLARE 
    @key INT 

FETCH NEXT FROM fillNullColumn INTO @key 

WHILE @@FETCH_STATUS = 0 BEGIN 
    UPDATE 
     table WITH (ROWLOCK) 
    SET 
     new_column = 0 -- default value 
    WHERE 
     key = @key 

    WAIT 00:00:05 --wait 5 seconds, keep in mind it causes updating only 12 rows per minute 

    FETCH NEXT FROM fillNullColumn INTO @key 
END 

CLOSE fillNullColumn 
DEALLOCATE fillNullColumn 

ALTER TABLE table ALTER COLUMN new_column ADD CONSTRAIN xxx 

मुझे यकीन है कि कुछ वाक्यविन्यास त्रुटियों देखते हैं कि हूँ, लेकिन मुझे आशा है कि इस मदद अपनी समस्या को हल करने के लिए।

शुभकामनाएं!

0

यदि आप एक ही तालिका में कॉलम चाहते हैं, तो आपको बस इसे करना होगा। अब, विकल्प 3 संभावित रूप से इसके लिए सबसे अच्छा है क्योंकि इस ऑपरेशन के दौरान आपके पास अभी भी डेटाबेस "लाइव" हो सकता है। यदि आप विकल्प 1 का उपयोग करते हैं, तो ऑपरेशन होने पर तालिका लॉक हो जाती है और फिर आप वास्तव में अटक जाते हैं।

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

2

आप कर सकते हैं:

  1. एक सौदे की शुरुआत करें।
  2. अपनी मूल तालिका पर एक लिखें लॉक लें ताकि कोई भी इसे लिख न सके।
  3. नई स्कीमा के साथ एक छाया तालिका बनाएं।
  4. मूल तालिका से सभी डेटा स्थानांतरित करें।
  5. पुरानी तालिका का नाम बदलने के लिए sp_rename निष्पादित करें।
  6. नई तालिका का नाम बदलने के लिए sp_rename निष्पादित करें
  7. अंत में, आप लेनदेन करते हैं।

इस दृष्टिकोण का लाभ यह है कि आपके पाठक लंबी प्रक्रिया के दौरान तालिका तक पहुंचने में सक्षम होंगे और आप पृष्ठभूमि में किसी प्रकार का स्कीमा परिवर्तन कर सकते हैं।

0

मुझे एक ही समस्या थी, और आपके विकल्प # 2 के लिए चला गया। इस तरह से 20 मिनट लगते हैं, 32 घंटे के विपरीत दूसरी तरह !!! भारी अंतर, टिप के लिए धन्यवाद। मैं इसके बारे में एक पूर्ण ब्लॉग प्रविष्टि लिखा था, लेकिन यहां महत्वपूर्ण एसक्यूएल है:

Alter table MyTable 
Add MyNewColumn char(10) null default '?'; 
go 

update MyTable set MyNewColumn='?' where MyPrimaryKey between 0 and 1000000 
go 
update MyTable set MyNewColumn='?' where MyPrimaryKey between 1000000 and 2000000 
go 
update MyTable set MyNewColumn='?' where MyPrimaryKey between 2000000 and 3000000 
go 
..etc.. 

Alter table MyTable 
Alter column MyNewColumn char(10) not null; 

और ब्लॉग प्रविष्टि यदि आप रुचि रखते हैं: http://splinter.com.au/adding-a-column-to-a-massive-sql-server-table

2

बस नवीनतम जानकारी के साथ इसे अपडेट करने के लिए।

SQL सर्वर 2012 इस में अब

  1. एंटरप्राइज़ संस्करण निम्न परिस्थितियों में एक ऑनलाइन आपरेशन के रूप में किया जा सकता है केवल
  2. डिफ़ॉल्ट एक क्रम निरंतर

पल के लिए होना चाहिए आवश्यकता उदाहरण एक शाब्दिक स्थिर या GETDATE() जैसे फ़ंक्शन हो सकते हैं जो सभी पंक्तियों के लिए समान मान का मूल्यांकन करता है। NEWID() का डिफ़ॉल्ट योग्य नहीं है और फिर भी वहां और फिर सभी पंक्तियों को अद्यतन करने के समाप्त हो जाएगा।

SQL सर्वर अर्हता प्राप्त करने वाले डिफ़ॉल्ट के लिए उन्हें मूल्यांकन करता है और परिणाम मेटाडेटा में डिफ़ॉल्ट मान के रूप में संग्रहीत करता है, इसलिए यह डिफ़ॉल्ट बाधा से उत्पन्न होता है (जिसे अब आवश्यक नहीं किया जा सकता है)। यह sys.system_internals_partition_columns में देखने योग्य है। जब तक वे अद्यतन होने के लिए अगली बार तक पंक्तियों तक मूल्य नहीं लिखे जाते हैं।

अधिक यह यहाँ के बारे में विवरण: online non-null with values column add in sql server 2012

0

मैं एक ऐसी ही समस्या थी और मैं संशोधित # 3 दृष्टिकोण के साथ चला गया। मेरे मामले में डेटाबेस सरल पुनर्प्राप्ति मोड में था और जिस तालिका को कॉलम जोड़ा जाना था उसे किसी भी एफके बाधाओं से संदर्भित नहीं किया गया था।

एक ही स्कीमा के साथ एक नई तालिका बनाने और मूल तालिका की सामग्री की प्रतिलिपि बनाने के बजाय, मैंने चयन किया ... वाक्यविन्यास में।

माइक्रोसॉफ्ट (http://technet.microsoft.com/en-us/library/ms188029(v=sql.105).aspx)

चयन के लिए लॉगिंग की राशि के अनुसार ... जांच डेटाबेस के लिए प्रभाव में वसूली मॉडल पर निर्भर करता है। सरल वसूली मॉडल या थोक-लॉग वसूली मॉडल के तहत, थोक संचालन न्यूनतम रूप से लॉग इन होते हैं। चयन का उपयोग करके न्यूनतम लॉगिंग के साथ ... INTO कथन एक तालिका बनाने से अधिक कुशल हो सकता है और फिर INSERT कथन के साथ तालिका को पॉप्युलेट कर सकता है। अधिक जानकारी के लिए, ऑपरेशंस देखें जो हो सकता है न्यूनतम लॉग इन।

कदम के अनुक्रम:

1.Move डिफ़ॉल्ट

SELECT table.*, cast (‘default’ as nvarchar(256)) new_column 
INTO table_copy 
FROM table 

2.Drop वर्ष तालिका

DROP TABLE table 

3 के साथ नया स्तंभ जोड़ने नया करने के लिए पुराने तालिका से डेटा जबकि .नाम नव निर्मित तालिका

नई तालिका

मेरे मामले में तालिका 100 मिलियन से अधिक पंक्तियों था पर 0

4.Create आवश्यक की कमी और अनुक्रमित और इस दृष्टिकोण तेजी से दृष्टिकोण # 2 की तुलना में पूरी की और लोग इन अंतरिक्ष विकास कम से कम था।

+0

क्या आपको नई छाया तालिका पर सभी एफके की बाधाओं को भी बनाने की ज़रूरत है? – Junto

+0

चुनें INTO स्वचालित रूप से प्रलेखन https://msdn.microsoft.com/en-us/library/ms188029.aspx अनुभाग "सीमाएं और प्रतिबंध" के अनुसार FK बाधाओं को नहीं बनाएगा। उन्हें मैन्युअल रूप से जोड़ा जाना है। –

0

स्वीकार किया गया कि यह एक पुराना सवाल है। मेरे सहयोगी ने हाल ही में मुझे बताया कि वह 13.6 एम पंक्तियों वाली तालिका में एक ही तालिका तालिका विवरण में इसे करने में सक्षम था। यह SQL Server 2012 में एक सेकंड के भीतर समाप्त हुआ। मैं 8 एम पंक्तियों वाली तालिका पर इसकी पुष्टि करने में सक्षम था। एसक्यूएल सर्वर के बाद के संस्करण में कुछ बदल गया?

Alter table mytable add mycolumn char(1) not null default('N'); 
0

1) एक डिफ़ॉल्ट मान के साथ तालिका में कॉलम जोड़ें:

ALTER TABLE MyTable ADD MyColumn int default 0 

2) मान अद्यतन संवर्द्धित स्वीकार किए जाते हैं जवाब के रूप में तालिका (एक ही प्रभाव में)। अन्य उपयोगकर्ताओं/प्रक्रियाओं को अवरुद्ध करने से बचने के लिए, अपने पर्यावरण में अपडेट किए जा रहे रिकॉर्ड की संख्या समायोजित करें।

declare @rowcount int = 1 

while (@rowcount > 0) 
begin   

    UPDATE TOP(10000) MyTable SET MyColumn = 0 WHERE MyColumn IS NULL  
    set @rowcount = @@ROWCOUNT 

end 

3) कॉलम परिभाषा को शून्य की आवश्यकता के लिए बदलें। एक पल में निम्न को चलाएं जब तालिका उपयोग में नहीं है (या डाउनटाइम के कुछ मिनट शेड्यूल करें)। मैंने सफलतापूर्वक लाखों अभिलेखों के साथ तालिकाओं के लिए इसका उपयोग किया है।

ALTER TABLE MyTable ALTER COLUMN MyColumn int NOT NULL 
संबंधित मुद्दे