2009-11-09 18 views
14

मैं कुछ सिस्टम संचालन के विवरण रिकॉर्ड करने के लिए एमएस एसक्यूएल सर्वर 2005 में एक टेबल बनाना चाहता हूं। जैसा कि आप नीचे दिए गए टेबल डिज़ाइन से देख सकते हैं, Details के अलावा प्रत्येक कॉलम गैर शून्य है।क्या मुझे इनलाइन वर्कर (अधिकतम) कॉलम का उपयोग करना चाहिए या इसे एक अलग तालिका में स्टोर करना चाहिए?

CREATE TABLE [Log] 
(
[LogID] [int] IDENTITY(1,1) NOT NULL, 
[ActionID] [int] NOT NULL, 
[SystemID] [int] NOT NULL, 
[UserID] [int] NOT NULL, 
[LoggedOn] [datetime] NOT NULL, 
[Details] [varchar](max) NULL 
) 

क्योंकि Details कॉलम में हमेशा डेटा नहीं होगा। क्या इस कॉलम को एक अलग टेबल में स्टोर करना और इसके बजाय एक लिंक प्रदान करना अधिक कुशल है?

CREATE TABLE [Log] 
(
[LogID] [int] IDENTITY(1,1) NOT NULL, 
[ActionID] [int] NOT NULL, 
[SystemID] [int] NOT NULL, 
[UserID] [int] NOT NULL, 
[LoggedOn] [datetime] NOT NULL, 
[DetailID] [int] NULL 
)  

CREATE TABLE [Detail] 
(
[DetailID] [int] IDENTITY(1,1) NOT NULL, 
[Details] [varchar](max) NOT NULL 
) 

मैं वास्तव में यह विचार नहीं होगा एक छोटे डेटा प्रकार के लिए, लेकिन एक varchar(max) के लिए इस मदद तालिका आकार छोटे रखने कर रहा है? या मैं सिर्फ डेटाबेस को समझने और कुछ हासिल करने की कोशिश नहीं कर रहा हूं?

उत्तर

25

इसे इनलाइन रखें। कवर के तहत SQL सर्वर पहले से ही SQL 2005 के बाद से एक अलग 'आवंटन इकाई' में MAX कॉलम संग्रहीत करता है। Table and Index Organization देखें। यह वास्तव में MAX तालिका को अपनी तालिका में रखने जैसा ही है, लेकिन स्पष्ट रूप से ऐसा करने का कोई नुकसान है।

एक स्पष्ट मेज के बाद वास्तव में दोनों धीमी (विदेशी कुंजी बाधा की वजह से) और (DetaiID दोहराव की वजह से) और अधिक स्थान का उपभोग होगा। उल्लेख नहीं है कि इसके लिए अधिक कोड की आवश्यकता है, और बग्स ... कोड लिखकर पेश किए जाते हैं।

alt text http://i.msdn.microsoft.com/ms189051.3be61595-d405-4b30-9794-755842d7db7e(en-us,SQL.100).gif

अद्यतन

डेटा की वास्तविक स्थिति की जांच करने के लिए, एक साधारण परीक्षण यह दिखा सकते हैं:

use tempdb; 
go 

create table a (
    id int identity(1,1) not null primary key, 
    v_a varchar(8000), 
    nv_a nvarchar(4000), 
    m_a varchar(max), 
    nm_a nvarchar(max), 
    t text, 
    nt ntext); 
go 

insert into a (v_a, nv_a, m_a, nm_a, t, nt) 
values ('v_a', N'nv_a', 'm_a', N'nm_a', 't', N'nt'); 
go 

select %%physloc%%,* from a 
go 

%%physloc%% छद्म स्तंभ के वास्तविक भौतिक स्थान दिखाएगा पंक्ति, मेरे मामले में यह पृष्ठ 200 था:

dbcc traceon(3604) 
dbcc page(2,1, 200, 3) 

Slot 0 Column 2 Offset 0x19 Length 3 Length (physical) 3 
v_a = v_a        
Slot 0 Column 3 Offset 0x1c Length 8 Length (physical) 8 
nv_a = nv_a       
m_a = [BLOB Inline Data] Slot 0 Column 4 Offset 0x24 Length 3 Length (physical) 3 
m_a = 0x6d5f61      
nm_a = [BLOB Inline Data] Slot 0 Column 5 Offset 0x27 Length 8 Length (physical) 8 
nm_a = 0x6e006d005f006100    
t = [Textpointer] Slot 0 Column 6 Offset 0x2f Length 16 Length (physical) 16 
TextTimeStamp = 131137536   RowId = (1:182:0)      
nt = [Textpointer] Slot 0 Column 7 Offset 0x3f Length 16 Length (physical) 16 
TextTimeStamp = 131203072   RowId = (1:182:1) 

सभी कॉलम मान लेकिन टेक्स्ट और NTEXT को MAX प्रकारों सहित इनलाइन संग्रहित किया गया था।

sp_tableoption 'a' , 'large value types out of row', '1'; 
insert into a (v_a, nv_a, m_a, nm_a, t, nt) 
values ('2v_a', N'2nv_a', '2m_a', N'2nm_a', '2t', N'2nt');  
dbcc page(2,1, 200, 3); 

नोट कैसे m_a और nm_a स्तंभों में अब एक Textpointer हैं:
तालिका विकल्प बदलने और एक नई पंक्ति सम्मिलित (sp_tableoption मौजूदा पंक्तियों को प्रभावित नहीं करता), मैक्स प्रकार अपने स्वयं के भंडारण में निकाला गया था, के बाद LOB आवंटन इकाई:

Slot 1 Column 2 Offset 0x19 Length 4 Length (physical) 4 
v_a = 2v_a       
Slot 1 Column 3 Offset 0x1d Length 10 Length (physical) 10 
nv_a = 2nv_a       
m_a = [Textpointer] Slot 1 Column 4 Offset 0x27 Length 16 Length (physical) 16 
TextTimeStamp = 131268608   RowId = (1:182:2)      
nm_a = [Textpointer] Slot 1 Column 5 Offset 0x37 Length 16 Length (physical) 16 
TextTimeStamp = 131334144   RowId = (1:182:3)      
t = [Textpointer] Slot 1 Column 6 Offset 0x47 Length 16 Length (physical) 16 
TextTimeStamp = 131399680   RowId = (1:182:4)      
nt = [Textpointer] Slot 1 Column 7 Offset 0x57 Length 16 Length (physical) 16 
TextTimeStamp = 131465216   RowId = (1:182:5)      

पूरा होने sakeness के लिए हम भी पंक्ति से बाहर गैर अधिकतम क्षेत्रों में से एक के लिए मजबूर कर सकते हैं:

update a set v_a = replicate('X', 8000); 
dbcc page(2,1, 200, 3); 

नोट कैसे v_a स्तंभ पंक्ति-ओवरफ्लो संग्रहण में संग्रहीत किया जाता है:

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4 
v_a = [BLOB Inline Root] Slot 0 Column 2 Offset 0x19 Length 24 Length (physical) 24 
Level = 0       Unused = 99       UpdateSeq = 1 
TimeStamp = 1098383360    
Link 0 
Size = 8000       RowId = (1:176:0) 

तो, के रूप में अन्य पहले से ही टिप्पणी की है, मैक्स प्रकार इनलाइन डिफ़ॉल्ट रूप से, संग्रहीत अगर वे फिट कर रहे हैं। कई डीडब्ल्यू परियोजनाओं के लिए यह अस्वीकार्य होगा क्योंकि सामान्य डीडब्ल्यू लोड को स्कैन करना चाहिए या कम से कम स्कैन स्कैन करना चाहिए, इसलिए sp_tableoption ..., 'large value types out of row', '1' का उपयोग किया जाना चाहिए। ध्यान दें कि यह मौजूदा पंक्तियों को प्रभावित नहीं करता है, मेरे परीक्षण में इंडेक्स पुनर्निर्माण पर भी नहीं, इसलिए विकल्प को जल्दी चालू करना होगा।

अधिकांश ओएलटीपी प्रकार लोड के लिए हालांकि तथ्य यह है कि MAX प्रकारों को इनलाइन रूप से इनलाइन रूप से संग्रहीत किया जाता है, वास्तव में एक फायदा है, क्योंकि ओएलटीपी एक्सेस पैटर्न खोजना है और पंक्ति चौड़ाई उस पर थोड़ा प्रभाव डालती है।

मूल प्रश्न के संबंध में कोई भी कम नहीं: अलग तालिका आवश्यक नहीं है। large value types out of row विकल्प को चालू करने से विकास/परीक्षण के लिए एक निःशुल्क लागत पर एक ही परिणाम प्राप्त होता है।

+1

+! SQL सर्वर एक अलग आवंटन इकाई में 17 वर्णों के साथ एक वर्चर (अधिकतम) स्टोर करेगा? – Andomar

+3

डिफ़ॉल्ट व्यवहार यह है कि जब तक आप आवंटन को पार नहीं करते हैं, तब तक यह वर्चर (अधिकतम) को लोब स्टोरेज/आवंटन में संग्रहीत नहीं करेगा, कैड रूक्स ने इसे बदलने के लिए सेटिंग के लिए एक लिंक पोस्ट किया है। – Andrew

+0

बहुत विस्तृत - आपकी पूर्णता रीमस –

0

इसे इनलाइन रखें। varchar का पूरा बिंदु यह है कि यह 0 बाइट्स लेता है, यदि यह खाली है, 'हैलो' के लिए 4 बाइट, और इसी तरह।

+1

खाली होने पर यह 2 बाइट्स है और मुझे लगता है कि आपका मतलब है "हैलो" के लिए 5 बाइट्स, लेकिन यह 5 + 2 होना चाहिए। http://msdn.microsoft.com/en-us/library/ms176089.aspx – RichardTheKiwi

+0

@cyberkiwi: आप "हैलो" के बारे में सही हैं, लेकिन एक शून्य वर्कर वास्तव में 0 बाइट्स लेता है। प्रत्येक पंक्ति में प्रति कॉलम के साथ एक शून्य बिटमैप होता है। Http://weblogs.sqlteam.com/mladenp/archive/2007/09/06/How_does_SQL_Server_really_store_NULL-s.aspx – Andomar

+1

मैं खाली स्ट्रिंग के बारे में सोच रहा था '' शून्य नहीं, क्योंकि आपने कहा था "अगर यह खाली है" अगर यह शून्य है "। – RichardTheKiwi

0

मैं विस्तार तालिका बनाकर इसे सामान्य कर दूंगा। मुझे लगता है कि लॉग में कुछ प्रविष्टियों में एक ही विवरण होगा? इसलिए यदि आप इसे सामान्यीकृत करते हैं तो आप केवल विवरण तालिका पर पाठ संग्रहीत करते समय प्रत्येक घटना के लिए पाठ के बजाय एक एफके आईडी INTEGER संग्रहित करेंगे। यदि आपके पास इसे सामान्य करने के कारण हैं, लेकिन आपके प्रश्न से मुझे यह नहीं लगता कि यह मामला है।

+0

@ स्टारशिप 3000, क्या आप सुझाव दे रहे हैं कि विस्तार तालिका एक आयाम तालिका हो जहां वह पूर्वनिर्धारित विस्तार मूल्यों को लिंक करने के लिए संग्रहीत करता है? यदि ऐसा है, तो मुझे नहीं लगता कि यह एक लॉग टेबल के लिए व्यावहारिक है (क्योंकि उसके विवरणों को वर्चर (अधिकतम) होना आवश्यक है, विवरण शायद पूर्वनिर्धारित नहीं किए जा सकते हैं)। मुझे लगता है कि वह मेज को दो-एक-एक हिस्सों में विभाजित करने के बारे में पूछ रहा है, जो वास्तव में एक सामान्यीकरण मुद्दा नहीं होगा, केवल एक पृष्ठ-स्तरीय संग्रहण समस्या है। –

+0

मैं सुझाव देता हूं कि केवल तभी मूल्यों को दोहराया जाए और सभी ज्ञात हों। यदि यह कोई सामान्य लॉग प्रकार है तो मैं इसका सुझाव नहीं दूंगा। – Kuberchaun

7

विरोधाभासी रूप से, यदि आपका डेटा सामान्यतः 8000 वर्णों से कम है, तो मैं इसे एक अलग तालिका में संग्रहीत करता हूं, जबकि डेटा 8000 वर्णों से अधिक है, तो मैं इसे उसी तालिका में रखूंगा।

ऐसा इसलिए है क्योंकि क्या होता है यह है कि SQL सर्वर पृष्ठ में डेटा रखता है यदि यह पंक्ति को एकल पृष्ठ में बैठने की अनुमति देता है, लेकिन जब डेटा बड़ा हो जाता है, तो यह इसे टेक्स्ट डेटा प्रकार और पत्तियों की तरह ही बाहर ले जाता है पंक्ति में एक सूचक। तो 3000 वर्ण पंक्तियों के समूह के लिए, आप प्रति पृष्ठ कम पंक्तियों को फिट कर रहे हैं, जो वास्तव में अक्षम है, लेकिन 12000 वर्ण पंक्तियों के समूह के लिए, डेटा पंक्ति से बाहर है, इसलिए यह वास्तव में अधिक कुशल है।

यह कहकर, आम तौर पर आपके पास लंबाई की विस्तृत श्रृंखला होती है और इस प्रकार मैं इसे अपनी तालिका में ले जाऊंगा। यह आपको इस तालिका को एक अलग फ़ाइल समूह आदि में स्थानांतरित करने के लिए लचीलापन देता है।

ध्यान दें कि sp_tableoption का उपयोग करके आप can also specify it to force the data out of the row पर ध्यान दें। वर्कर (अधिकतम) मूल रूप से टेक्स्ट डेटा प्रकार के समान होता है, जिसमें पंक्ति (डेटा के लिए) (डेटा के लिए) से डेटा को डिफॉल्ट करने के बजाय पंक्ति में डेटा (वर्चर (अधिकतम) के लिए) को डिफ़ॉल्ट रूप से डिफ़ॉल्ट किया जाता है।

+0

मैंने सोचा कि पाठ, छवि और दोस्तों को डिफ़ॉल्ट रूप से पंक्ति से बाहर रखा गया था? –

+0

टेक्स्ट डिफ़ॉल्ट रूप से पंक्ति से बाहर संग्रहीत किया जाता है, जबकि वर्चर (अधिकतम) डिफ़ॉल्ट रूप से पंक्ति में संग्रहीत किया जाता है। इसके अलावा, वे भंडारण के मामले में बहुत समान व्यवहार करते हैं। –

2

आपको अपने डेटा को सबसे तार्किक संरचना में जो कुछ भी दिखता है उसे ढंकना चाहिए और SQL सर्वर को डेटा को भौतिक रूप से संग्रहीत करने के तरीके के बारे में अपने अनुकूलन करने की अनुमति देना चाहिए।

यदि आपको प्रदर्शन विश्लेषण के माध्यम से पता चलता है कि आपकी संरचना एक प्रदर्शन समस्या है, तो अपनी संरचना में या स्टोरेज सेटिंग्स में परिवर्तन करने पर विचार करें।

0

उनमें से प्रत्येक 16 के लिए एक शून्य कॉलम लागत 2 बाइट्स होने के कारण। यदि यह तालिका में एकमात्र (या 17 वां, या 33 वां, आदि) शून्य कॉलम है, तो यह आपको प्रति पंक्ति 2 बाइट्स खर्च करेगा, अन्यथा कुछ भी नहीं।

+0

आईओओ के लिए धन्यवाद, इससे कोई फर्क नहीं पड़ता। – erikkallen

संबंधित मुद्दे