2011-06-03 6 views
13

मेरे पास एक SQL क्वेरी है (LINQ से Entities द्वारा जेनरेट की गई) जो लगभग निम्न की तरह है:आदेशित कॉलम अनुक्रमित होने के बावजूद मेरा SQL सर्वर ऑर्डर धीमा क्यों है?

SELECT * FROM [mydb].[dbo].[employees] 
JOIN [mydb].[dbo].[industry] 
    ON jobs.industryId = industry.id 
JOIN [mydb].[dbo].[state] 
    ON jobs.stateId = state.id 
JOIN [mydb].[dbo].[positionType] 
    ON jobs.positionTypeId = positionType.id 
JOIN [mydb].[dbo].[payPer] 
    ON jobs.salaryPerId = payPer.id 
JOIN [mydb].[dbo].[country] 
    ON jobs.countryId = country.id 
WHERE countryName = 'US' 
ORDER BY startDatetime 

क्वेरी 1200 पंक्तियों के बारे में बताती है, जो मुझे नहीं लगता कि यह एक बड़ी राशि है। दुर्भाग्य से यह ~ 16 सेकंड भी लेता है। आदेश के बिना, क्वेरी < 1 सेकंड लेता है।

मैंने प्रारंभिक समय कॉलम पर एक इंडेक्स डालने के लिए SQL सर्वर प्रबंधन स्टूडियो का उपयोग किया है, और "cityId, industryId, startDatetime, positionTypeId, payPerId, stateId" पर एक क्लस्टर इंडेक्स भी उपयोग किया है (यानी "नौकरियों" में सभी कॉलम कि हम जॉइन और कॉलम पर उपयोग करते हैं, हम ऑर्डर द्वारा ऑर्डर करते हैं)। हमारे पास जॉइन में उपयोग किए जाने वाले प्रत्येक कॉलम पर पहले से ही व्यक्तिगत इंडेक्स हैं। दुर्भाग्यवश इसने क्वेरी को और तेज़ नहीं बनाया है।

मैंने एक शोप्लान चलाया और मिला:

 |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[cityId])) 
     |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[stateId])) 
     | |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[industryId])) 
     | | |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[positionTypeId])) 
     | | | |--Nested Loops(Inner Join, OUTER REFERENCES:([mydb].[dbo].[jobs].[salaryPerId])) 
     | | | | |--Sort(ORDER BY:([mydb].[dbo].[jobs].[issueDatetime] ASC)) 
     | | | | | |--Hash Match(Inner Join, HASH:([mydb].[dbo].[currency].[id])=([mydb].[dbo].[jobs].[salaryCurrencyId])) 
     | | | | |   |--Index Scan(OBJECT:([mydb].[dbo].[currency].[IX_currency])) 
     | | | | |   |--Nested Loops(Inner Join, WHERE:([mydb].[dbo].[jobs].[countryId]=[mydb].[dbo].[country].[id])) 
     | | | | |    |--Index Seek(OBJECT:([mydb].[dbo].[country].[IX_country]), SEEK:([mydb].[dbo].[country].[countryName]='US') ORDERED FORWARD) 
     | | | | |    |--Clustered Index Scan(OBJECT:([mydb].[dbo].[jobs].[PK_jobs])) 
     | | | | |--Clustered Index Seek(OBJECT:([mydb].[dbo].[payPer].[PK_payPer]), SEEK:([mydb].[dbo].[payPer].[id]=[mydb].[dbo].[jobs].[salaryPerId]) ORDERED FORWARD) 
     | | | |--Clustered Index Seek(OBJECT:([mydb].[dbo].[positionType].[PK_positionType]), SEEK:([mydb].[dbo].[positionType].[id]=[mydb].[dbo].[jobs].[positionTypeId]) ORDERED FORWARD) 
     | | |--Clustered Index Seek(OBJECT:([mydb].[dbo].[industry].[PK_industry]), SEEK:([mydb].[dbo].[industry].[id]=[mydb].[dbo].[jobs].[industryId]) ORDERED FORWARD) 
     | |--Clustered Index Seek(OBJECT:([mydb].[dbo].[state].[PK_state]), SEEK:([mydb].[dbo].[state].[id]=[mydb].[dbo].[jobs].[stateId]) ORDERED FORWARD) 
     |--Clustered Index Seek(OBJECT:([mydb].[dbo].[city].[PK_city]), SEEK:([mydb].[dbo].[city].[id]=[mydb].[dbo].[jobs].[cityId]) ORDERED FORWARD) 

महत्वपूर्ण रेखा "| --ोर्ट (ऑर्डर द्वारा: ([mydb]। [dbo]। [jobs]। [issueDatetime] ASC)) "— उस कॉलम पर किसी इंडेक्स के किसी भी उल्लेख के बिना।

मेरी क्वेरी इतनी धीमी गति से मेरे ऑर्डर क्यों है, और मैं अपनी क्वेरी को कैसे बढ़ा सकता हूं?

+1

और क्या वे सभी विदेशी कुंजी कॉलम हैं जिन्हें आप अनुक्रमित करते हैं ?? –

+0

@marc_s: हाँ, अलग-अलग तालिकाओं पर सभी आईडी कॉलम भी अनुक्रमित हैं। मैं 99.9% निश्चित हूं कि यह शामिल होने वाले जोड़ों में धीमा नहीं है, क्योंकि ORDER BY (और जॉइन छोड़कर) को हटाकर ~ 16s से 1s से बहुत कम समय तक गिर जाता है। – George

उत्तर

10

आपकी क्वेरी के आदेश शामिल नहीं है तो यह डेटा जो कुछ oreder में यह पाया गया वापस आ जाएगी है। इस बात की कोई गारंटी नहीं है कि जब आप क्वेरी फिर से चलाते हैं तो डेटा उसी क्रम में भी वापस कर दिया जाएगा।

जब आप खंड द्वारा एक आदेश में शामिल हैं, dabatase सही क्रम में पंक्तियों की एक सूची का निर्माण और फिर इसी क्रम में डेटा लौटाने के लिए है। यह बहुत अधिक प्रसंस्करण ले सकता है जो अतिरिक्त समय में अनुवाद करता है।

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

लौटने का प्रयास करें कम कॉलम (कॉलम आप * का चयन करने के बजाय जरूरत उल्लेख करें) और देखें कि क्या क्वेरी तेजी से चलाता है।

+0

तो मुझे लगता है कि यह एक "सही" उत्तर था - मेरा विवरण कॉलम बहुत बड़ा था (यह HTML का एक हिस्सा संग्रहीत किया गया था), जिसने प्रत्येक पंक्ति के आकार को आकार दिया, जिसका मतलब था कि इस प्रकार को डिस्क पर जाना पड़ा। – George

+0

** ** कॉलम की बड़ी संख्या ** की वजह से +1। आपको पहले क्रमबद्ध कॉलम के 'आईडी' वापस करना चाहिए, फिर आप प्रत्येक आईडी के लिए संपूर्ण टुपल का चयन कर सकते हैं। –

1

समाचार फ्लैश: कॉलम को इंडेक्स करना इस प्रकार को तेज़ी से बनाने में मदद नहीं करता है।

यदि आप अपनी क्वेरी बनाना चाहते हैं तो बहुत तेज़ी से अपनी टेबल के क्रम को उलट दें। विशेष रूप से, अपनी तालिका में पहले तालिका country सूची तालिका। कारण? जहां खंड उन सभी जुड़ने के बजाय पंक्तियों को पहली तालिका से फ़िल्टर कर सकता है, फिर पंक्तियों को फ़िल्टर करना।

+0

क्रमबद्ध क्रम में संग्रहित सूचकांक नहीं है? इसे जोड़ते समय मेरे पास "क्रमबद्ध: आरोही" और "क्रमबद्ध: अवरोही" के बीच एक विकल्प होता है। मैंने SQLite में इंडेक्स का उपयोग क्वेरी में ऑर्डर करने के लिए बहुत तेजी से किया है। मैंने चारों ओर शामिल होने का आदेश बदल दिया और क्वेरी समय 16 से 7 तक घटा दिया - लेकिन ऑर्डर बीई अभी भी उस समय के सभी 7s ले रहा है। क्या किसी भी तेजी से ऑर्डर करने का कोई तरीका नहीं है? – George

+1

@ जॉर्ज: इंडेक्स क्रमबद्ध क्रम में संग्रहीत है, हां, लेकिन सामान्य रूप से प्रति तालिका केवल एक इंडेक्स को चुना जा सकता है, और इस मामले में यह क्लस्टर इंडेक्स 'पीके_jobs' चुनता है क्योंकि यह एक कवर इंडेक्स है। इंडेक्स की उपस्थिति में 'ऑर्डर बाय' की मदद नहीं होती है, यदि इसमें शामिल होने जैसी अधिक दबाव वाली चिंताएं हैं। –

2

क्लस्टर्ड इंडेक्स में फ़ील्ड किस क्रम में शामिल हैं? आप (, परोक्ष रूप से countryName के माध्यम से) के बाद से आप एक ही countryId का चयन करना चाहते ORDER BY के लिए आदेश में यह मैच के लिए, या इस मामले (countryId, startDateTime) इसी क्रम में सामने में में पहली startDateTime क्षेत्र रखना चाहते हैं और फिर startDateTime द्वारा आदेश।

+0

मैंने अपने पहले क्लस्टर इंडेक्स में कॉलम को फिर से दर्ज किया है ताकि देश आईडी पहले हो और स्टार्टडेट टाइम दूसरा हो, और देश में एक अलग इंडेक्स भी जोड़ा जाए और एक साथ डेटटाइम शुरू करें। मेरी QUERY योजना को देखते हुए, यह क्वेरी मेरे 'कर्मचारियों' तालिका पर टेबल और पीके में शामिल पीके इंडेक्स पर टक्कर मार रही है, लेकिन कुछ और नहीं (और मेरी क्लस्टर इंडेक्स नहीं)। गति में सुधार नहीं हुआ है। – George

+1

आपको जानकारी होना चाहिए कि हालांकि स्तंभ (रों) संकुल अनुक्रमणिका में भी प्रत्येक के लिए और कहा कि मेज पर प्रत्येक के हर प्रवेश और हर गैर क्लस्टर सूचकांक जुड़ जाते हैं - अपने क्लस्टर सूचकांक फूला हुआ मिलता है, उदाहरण के लिए और कई बड़े स्तंभों से बना है, तो आपकी पूरी तालिका की अनुक्रमणिका संरचनाएं फूली हुई हैं और इस प्रकार समग्र प्रदर्शन बुरी तरह चोट पहुंचाता है। यदि संभव हो तो मैं मिश्रित क्लस्टर कुंजी से बचने की कोशिश करता हूं, और सबसे निश्चित रूप से, मैं वर्चर कॉलम> = 10 वर्णों को हर कीमत पर लम्बाई से बचूंगा। –

+0

@marc_s: हाँ, यह था - मेरा विवरण फ़ील्ड विशाल था (इंडेक्स एक लाल हेरिंग थे)। मदद के लिए धन्यवाद ढेर। – George

1

आप नीचे दिए गए कोड की कोशिश करनी चाहिए भी

सम्मिलित अस्थायी तालिका में रिकॉर्ड खंड द्वारा आदेश का उपयोग कर

SELECT * into #temp FROM [mydb].[dbo].[employees] 
JOIN [mydb].[dbo].[industry] 
    ON jobs.industryId = industry.id 
JOIN [mydb].[dbo].[state] 
    ON jobs.stateId = state.id 
JOIN [mydb].[dbo].[positionType] 
    ON jobs.positionTypeId = positionType.id 
JOIN [mydb].[dbo].[payPer] 
    ON jobs.salaryPerId = payPer.id 
JOIN [mydb].[dbo].[country] 
    ON jobs.countryId = country.id 
WHERE countryName = 'US' 

अब बयान आदेश का उपयोग कर चलाए खण्ड तक बिना

Select * from #temp ORDER BY startDatetime 
+0

यह काफी तेजी से चलता है - पूरी क्वेरी अब <1s लेती है। क्या LINQ से SQL को उस क्वेरी को उत्पन्न करने के लिए कोई तरीका है, या क्या मुझे कुछ एसक्यूएल हाथ से लिखना होगा? मैं कुछ गुगल कर रहा हूं - क्या एक भौतिक/अनुक्रमित दृश्य मदद करेगा? – George

+0

क्या आप इसे अलग थ्रेड में पोस्ट कर सकते हैं? :) अगर यह आपकी मदद करता है, तो कृपया इसे एक उत्तर के रूप में चिह्नित करें। :) – Pankaj

+0

मुझे यकीन नहीं है कि यह वास्तव में अब मदद कर रहा है - शुरुआत में ऐसा लगता है कि यह चीजों को बहुत तेजी से बना रहा था, लेकिन: चुनें * [एटीआर] से #temp में चुनें। [Dbo]। [Jobs] doesn ' टी काम - "कॉलम नाम अद्वितीय होना चाहिए" (एकाधिक "आईडी" कॉलम) इसलिए मैंने कोशिश की: चयन शीर्षक, शहर नाम, राज्य नाम INTO #temp ... यह तेज़ (<1s) था, लेकिन यदि मैं जोड़ता हूं "विवरण" (वर्कर (MAX)) कॉलम में, मैं धीमा हो जाता हूं: चयन शीर्षक, शहर नाम, राज्य नाम, विवरण INTO #temp ... तो ऐसा लगता है कि क्वेरी धीमी है अगर (1) ऑर्डर BY वर्तमान है * और * (2) एक वचर (MAX) फ़ील्ड मौजूद है। – George

6

Bec आपकी क्वेरी को सभी कॉलम प्रोजेक्ट करें (*), इसमें शामिल स्थितियों के लिए 5 कॉलम की आवश्यकता है और इसमें एक अनइलेक्टीव WHERE क्लॉज है जो किसी तालिका तालिका में संभावित रूप से हो सकता है, यह Index Tipping Point पर हिट करने का कारण बनता है: ऑप्टिमाइज़र निर्णय लेता है कि यह कम महंगा है पूरी तालिका को स्कैन करने के लिए, इसे फ़िल्टर करें और इसे सॉर्ट करें कि यह इंडेक्स स्कैन करने के लिए होगा और उसके बाद आवश्यक अतिरिक्त कॉलम (जुड़ने के लिए 5 और * के लिए बाकी) को पुनर्प्राप्त करने के लिए तालिका में प्रत्येक कुंजी को देखना होगा।

एक बेहतर सूचकांक आंशिक रूप से इस प्रश्न को कवर करने से हो सकता है:

CREATE INDEX ... ON .. (countryId, startDatetime); 

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

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