SQL सर्वर एक इंडेक्स को क्यों अनदेखा करेगा?


16

मेरे पास एक टेबल है, CustPassMasterजिसमें 16 कॉलम हैं, जिनमें से एक है CustNum varchar(8), और मैंने एक इंडेक्स बनाया है IX_dbo_CustPassMaster_CustNum। जब मैं अपना SELECTबयान चलाता हूं :

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

यह सूचकांक को पूरी तरह से अनदेखा करता है। यह मुझे भ्रमित करता है क्योंकि मेरे पास एक और तालिका है CustDataMasterजिसमें अधिक कॉलम (55) हैं, जिनमें से एक है CustNum varchar(8)। मैंने IX_dbo_CustDataMaster_CustNumइस तालिका में इस कॉलम ( ) पर एक इंडेक्स बनाया है , और व्यावहारिक रूप से समान क्वेरी का उपयोग करें:

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

और यह मेरे द्वारा बनाए गए सूचकांक का उपयोग करता है।

क्या इसके पीछे कोई खास तर्क है? यह सूचकांक का उपयोग क्यों करेगा CustDataMaster, लेकिन इससे नहीं CustPassMaster? क्या यह कम कॉलम की गिनती के कारण है?

पहली क्वेरी में 66 पंक्तियाँ हैं। दूसरे के लिए, 1 पंक्ति वापस आ गई है।

इसके अलावा, अतिरिक्त नोट: CustPassMaster4991 रिकॉर्ड है, और CustDataMaster5376 रिकॉर्ड है। क्या सूचकांक को नजरअंदाज करने के पीछे यह तर्क हो सकता है? CustPassMasterइसके पास डुप्लिकेट रिकॉर्ड भी हैं जो समान CustNumमान भी रखते हैं। क्या यह एक और कारक है?

मैं दोनों दावों के वास्तविक निष्पादन योजना परिणामों पर इस दावे को आधार बना रहा हूं।

यहां डीडीएल के लिए CustPassMaster(अप्रयुक्त सूचकांक के साथ एक) है:

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

और CustDataMaster(मैं बहुत अप्रासंगिक क्षेत्रों को छोड़ दिया है) के लिए DDL :

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

मेरे पास उन तालिकाओं में से कोई भी एक अव्यवस्थित सूचकांक नहीं है, केवल एक गैर-अनुक्रमित सूचकांक है।

इस तथ्य को अनदेखा करें कि डेटाटाइप्स पूरी तरह से संग्रहीत किए जा रहे डेटा से मेल नहीं खाते हैं। ये फ़ील्ड IBM AS / 400 DB2 डेटाबेस से बैकअप हैं, और ये इसके लिए संगत डेटाटाइप हैं। (मुझे इस बैकअप डेटाबेस को सटीक प्रश्नों के साथ क्वेरी करने में सक्षम होना चाहिए , और सटीक समान परिणाम प्राप्त करना चाहिए ।)

यह डेटा केवलSELECT बयानों के लिए उपयोग किया जाता है । मैं इस पर कोई INSERT/ UPDATE/ DELETEबयान नहीं करता , सिवाय इसके कि जब बैकअप एप्लिकेशन एएस / 400 से डेटा कॉपी कर रहा हो।


इस लेख को नॉनस्टस्टर्ड से क्लस्टर्ड तक टिपिंग पॉइंट के बारे में पढ़ने लायक हो सकता है। sqlskills.com/blogs/kimberly/the-tipping-point-query-answers
मार्क सिंकिनसन

3
तो बस यही फर्क है। यदि पहली क्वेरी आपके सूचकांक का उपयोग करती है, तो उसे 65 लुकअप करने होंगे। ये कीमती है। दूसरी क्वेरी को केवल एक प्रदर्शन करना है।
हारून बर्ट्रेंड

जवाबों:


18

आमतौर पर अनुक्रमणिका का उपयोग SQL सर्वर द्वारा किया जाएगा यदि यह अनुक्रमणिका का उपयोग करने के लिए अंतर्निहित तालिका का सीधे उपयोग करने की तुलना में अधिक समीचीन है।

ऐसा लगता है कि लागत-आधारित ऑप्टिमाइज़र को लगता है कि यह वास्तव में प्रश्न में सूचकांक का उपयोग करने के लिए अधिक महंगा होगा। आप देख सकते हैं कि यदि आप इसके बजाय सूचकांक का उपयोग करते हैं SELECT *, तो आप बस SELECT T1Col1

जब आप SELECT *तालिका में सभी कॉलम वापस करने के लिए SQL सर्वर से कह रहे हैं। उन स्तंभों को वापस करने के लिए SQL सर्वर को पंक्तियों के लिए पृष्ठों को पढ़ना चाहिएWHERE जो तालिका से ही बयान मानदंड से मेल खाते हैं (क्लस्टर इंडेक्स या हीप)। SQL सर्वर संभवतः तालिका से शेष स्तंभों को प्राप्त करने के लिए आवश्यक रीड्स की मात्रा सोच रहा है इसका मतलब है कि यह तालिका को सीधे स्कैन कर सकता है। वास्तविक क्वेरी और क्वेरी द्वारा उपयोग की जाने वाली वास्तविक निष्पादन योजना को देखना उपयोगी होगा।


3
इसलिए मेरे द्वारा चुने गए स्तंभों को सीमित करने और उन्हें INCLUDEसूचकांक के खंड में शामिल करने के लिए एक अधिक स्पष्ट और इष्टतम समाधान होगा ?
डेर कोमिसर

1
यह बहुत अच्छी तरह से एक बड़ा अंतर बना सकता है। INCLUDEक्‍लॉज द्वारा क्‍वेरी द्वारा लौटाए गए सभी कॉलम जोड़ने से संभवतः SQL सर्वर इंडेक्स का उपयोग करेगा। ऐसा कहने के बाद, आप क्या अनुकूलन करने का प्रयास कर रहे हैं? यह मुझे लगता है कि यदि आपकी तालिका में 100 बाइट्स की औसत पंक्ति का आकार है, तो 5000 पंक्तियां केवल 500kb डेटा के बारे में हैं, और अच्छी तरह से किसी भी समय खर्च करने के लायक नहीं हो सकती हैं।
मैक्स वर्नोन

1
औसत पंक्ति का आकार 0.30KB है Table1, और 0.53KB के लिए Table2। यह सभी डेटा एक AS / 400 (IBM System i) से आयात किया गया है और किसी भी चीज़ पर NO PK नहीं है। मैंने मैन्युअल रूप से आज सभी इंडेक्स बनाए हैं जब लोग उल्लेख कर रहे थे कि आवेदन कई बार काफी धीमा है।
डेर कोमिसार

10

इंडेक्स का उपयोग करने के लिए, क्योंकि आप कर रहे हैं select *, तो SQL सर्वर को पहले इंडेक्स से प्रत्येक पंक्तियों को पढ़ना चाहिए जो आपके उस खंड में मौजूद मूल्य से मेल खाते हैं। इसके आधार पर, यह पंक्ति के प्रत्येक के लिए संकुल सूचकांक मान प्राप्त करेगा, और फिर उनमें से प्रत्येक को संकुल सूचकांक (= कुंजी लुकअप) से अलग से तलाश करना होगा। चूँकि आपने कहा था कि मान अद्वितीय नहीं हैं, इसलिए SQL सर्वर आँकड़ों का उपयोग यह अनुमान लगाने के लिए करता है कि यह कुंजी लुकअप कितनी बार करना है।

गैर-क्लस्टर इंडेक्स + कुंजी लुकअप को स्कैन करने के लिए सबसे अधिक संभावना लागत अनुमान क्लस्टर इंडेक्स स्कैन के लिए लागत अनुमान से अधिक है, और इसीलिए सूचकांक को अनदेखा किया गया है।

यदि आप set statistics io onइंडेक्स का उपयोग करते समय वास्तव में छोटा है या नहीं यह देखने के लिए आप एक इंडेक्स संकेत का उपयोग करने और फिर उपयोग करने का प्रयास कर सकते हैं। यदि अंतर बड़ा है, तो आप आंकड़ों पर गौर कर सकते हैं, यदि वे पुराने हैं।

इसके अलावा, यदि आपका SQL वास्तव में चर का उपयोग कर रहा है और सटीक मान नहीं है, तो यह पैरामीटर सूँघने के कारण भी हो सकता है (योजना बनाने के लिए उपयोग किए जाने वाले पिछले मूल्य = तालिका में बहुत पंक्तियाँ थीं)।


1

यही कारण हो सकता है। ऑप्टिमाइज़र लागत आधारित होते हैं और यह तय करते हैं कि प्रत्येक निष्पादन पथ के 'लागत' के आधार पर क्या रास्ता चुनना है। 'सबसे बड़ी' लागत डिस्क से मेमोरी तक डेटा प्राप्त कर रही है। यदि ऑप्टिमाइज़र गणना करता है कि सूचकांक और डेटा दोनों को पढ़ने में अधिक समय लगता है तो यह सूचकांक को छोड़ने का निर्णय ले सकता है। बड़ी पंक्तियाँ अधिक डिस्क ब्लॉक हैं जो वे लेते हैं।

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