नेस्टेड लूप्स पर धीमी गति से चलने वाली क्वेरी को कैसे अनुकूलित करें (इनर जॉइन)


39

टी एल; डॉ

चूँकि इस प्रश्न पर विचार हो रहे हैं, मैं इसे यहाँ संक्षेप में प्रस्तुत करूँगा ताकि नए लोगों को इतिहास का सामना न करना पड़े:

JOIN table t ON t.member = @value1 OR t.member = @value2 -- this is slow as hell
JOIN table t ON t.member = COALESCE(@value1, @value2)    -- this is blazing fast
-- Note that here if @value1 has a value, @value2 is NULL, and vice versa

मुझे एहसास है कि यह हर किसी की समस्या नहीं हो सकती है, लेकिन ओएन क्लॉस की संवेदनशीलता को उजागर करके, यह आपको सही दिशा में देखने में मदद कर सकता है। किसी भी मामले में मूल पाठ यहां भविष्य के मानवविज्ञानी के लिए है:

मूल लेख

निम्नलिखित सरल क्वेरी पर विचार करें (केवल 3 तालिकाएँ शामिल हैं)

    SELECT

        l.sku_id AS ProductId,
        l.is_primary AS IsPrimary,
        v1.category_name AS Category1,
        v2.category_name AS Category2,
        v3.category_name AS Category3,
        v4.category_name AS Category4,
        v5.category_name AS Category5

    FROM category c4
    JOIN category_voc v4 ON v4.category_id = c4.category_id and v4.language_code = 'en'

    JOIN category c3 ON c3.category_id = c4.parent_category_id
    JOIN category_voc v3 ON v3.category_id = c3.category_id and v3.language_code = 'en'

    JOIN category c2 ON c2.category_id = c3.category_id
    JOIN category_voc v2 ON v2.category_id = c2.category_id and v2.language_code = 'en'

    JOIN category c1 ON c1.category_id = c2.parent_category_id
    JOIN category_voc v1 ON v1.category_id = c1.category_id and v1.language_code = 'en'

    LEFT OUTER JOIN category c5 ON c5.parent_category_id = c4.category_id
    LEFT OUTER JOIN category_voc v5 ON v5.category_id = c5.category_id and v5.language_code = @lang

    JOIN category_link l on l.sku_id IN (SELECT value FROM #Ids) AND
    (
        l.category_id = c4.category_id OR
        l.category_id = c5.category_id
    )

    WHERE c4.[level] = 4 AND c4.version_id = 5

यह एक बहुत ही सरल क्वेरी है, केवल भ्रमित करने वाला हिस्सा अंतिम श्रेणी में शामिल है, यह इस तरह है क्योंकि श्रेणी स्तर 5 मौजूद हो सकता है या नहीं। क्वेरी के अंत में मैं उत्पाद जानकारी प्रति उत्पाद ID (SKU ID) की तलाश कर रहा हूं, और यह वह जगह है जहां बहुत बड़ी तालिका श्रेणी_लिंक आती है। अंत में, तालिका #Ids सिर्फ एक अस्थायी तालिका है जिसमें 10'000 Ids शामिल हैं।

निष्पादित होने पर, मुझे निम्नलिखित वास्तविक निष्पादन योजना मिलती है:

वास्तविक निष्पादन योजना

जैसा कि आप देख सकते हैं, नेस्टेड लूप्स (इनर जॉइन) में लगभग 90% समय व्यतीत होता है। यहां उन नेस्टेड लूप्स के बारे में अतिरिक्त जानकारी दी गई है:

नेस्टेड लूप्स (इनर जॉइन)

ध्यान दें कि तालिका नाम ठीक से मेल नहीं खाते क्योंकि मैंने पठनीयता के लिए क्वेरी तालिका नामों को संपादित किया है, लेकिन यह (ads_alt_category = श्रेणी) से मिलान करना बहुत आसान है। क्या इस क्वेरी को ऑप्टिमाइज़ करने का कोई तरीका है? यह भी ध्यान रखें कि उत्पादन में, टेम्प टेबल # मौजूद नहीं है, यह एक समान 10'000 आइडी का टेबल वैलिड पैरामीटर है, जो संग्रहीत प्रक्रिया पर पारित किया गया है।

अतिरिक्त जानकारी:

  • category_id और parent_category_id पर श्रेणी सूचकांकों
  • कैटेगरी_वॉक इंडेक्स पर कैटेगरी_आईडी, भाषा_कोड
  • sku_id पर category_link इंडेक्स, category_id

संपादित करें (हल)

जैसा कि स्वीकृत उत्तर द्वारा बताया गया है, समस्या श्रेणी_क्लीन जॉइन में OR क्लॉज थी। हालाँकि, स्वीकृत उत्तर में सुझाया गया कोड बहुत धीमा है, मूल कोड की तुलना में भी धीमा है। एक बहुत तेज और बहुत अधिक क्लीनर समाधान वर्तमान जिन्न की स्थिति को निम्नलिखित के साथ बदलने के लिए है:

JOIN category_link l on l.sku_id IN (SELECT value FROM @p1) AND l.category_id = COALESCE(c5.category_id, c4.category_id)

यह मिनट ट्विक सबसे तेज़ समाधान है, जिसे स्वीकार किए गए उत्तर से दोहरे जुड़ाव के खिलाफ परीक्षण किया गया है और साथ ही सीआरएसपीएलपीएलवाई के खिलाफ भी परीक्षण किया गया है जैसा कि वालिज द्वारा सुझाया गया है।


हमें बाकी क्वेरी प्लान देखने की आवश्यकता होगी।
RBarryYoung

बस एक टिप्पणी: इसके साथ कई आश्रित कार्डिनैलिटी अनुमान त्रुटियों में शामिल हो जाते हैं। सबसे अधिक बार, क्वेरी प्रदर्शन कार्डिनैलिटी कम करके आंका जाता है।
यूएसआर

क्या निष्पादन योजना सूचकांक के लिए सुझाव देती है? इसके अलावा, यह मत भूलो कि आप अपनी अस्थायी टेबलों पर प्राथमिक कुंजी और अनुक्रमित सेट कर सकते हैं (अधिक जानकारी यहां )

@rbarry यदि वर्तमान समाधानों को आज़माने के बाद मुझे कुछ नहीं मिलता है, तो मैं इस प्रश्न पर सुधार करूंगा

1
यूनिअन के साथ क्वेरी को डुप्लिकेट करने और OR से छुटकारा पाने के बारे में क्या है

जवाबों:


17

समस्या कोड के इस भाग में दिखाई देती है:

JOIN category_link l on l.sku_id IN (SELECT value FROM #Ids) AND
(
    l.category_id = c4.category_id OR
    l.category_id = c5.category_id
)

orमें शामिल होने की स्थिति हमेशा संदिग्ध है। एक सुझाव यह है कि इसे दो हिस्सों में विभाजित किया जाए:

JOIN category_link l1 on l1.sku_id in (SELECT value FROM #Ids) and l1.category_id = cr.category_id
left outer join
category_link l1 on l2.sku_id in (SELECT value FROM #Ids) and l2.category_id = cr.category_id

फिर आपको इसे संभालने के लिए शेष क्वेरी को संशोधित करना होगा। । । coalesce(l1.sku_id, l2.sku_id)उदाहरण के लिए selectखंड में।


फ़िल्टर करने से उस विशेष पर किया जा रहा की राशि में शामिल होने के साथ, मैं भी बदल रहा है परीक्षण था JOINएक के CROSS APPLYसाथ INएक पर स्विच करने EXISTSमें APPLYकी WHEREधारा।

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

3
मैं इस जवाब को स्वीकार कर रहा हूं क्योंकि यह पहला जवाब था जिसने मुझे समस्या की ओर इशारा किया। हालांकि सुझाया गया समाधान मूल कोड की तुलना में बेहद धीमा है। हालाँकि, यह जानते हुए कि OR क्लॉज मुद्दा था, बस इसे बदलकर ON l.category_id = ISNULL(c5.category_id, c4.category_idचाल चली गई।
लुइस फेरो

1
@LuisFerrao । । अतिरिक्त सूचना के लिए आपका धन्यवाद। यह जानना उपयोगी है कि coalesce()ऑप्टिमाइज़र को सही दिशा में धकेलता है।
गॉर्डन लिनोफ़

9

जैसा कि एक अन्य उपयोगकर्ता ने उल्लेख किया है, यह जुड़ने का कारण है:

JOIN category_link l on l.sku_id IN (SELECT value FROM #Ids) AND
(
    l.category_id = c4.category_id OR
    l.category_id = c5.category_id
)

इनको कई जोड़ो में विभाजित करने के अलावा, आप एक कोशिश भी कर सकते हैं CROSS APPLY

CROSS APPLY (
    SELECT [some column(s)]
    FROM category_link x
    WHERE EXISTS(SELECT value FROM #Ids WHERE value = x.sku_id)
    AND (x.category_id = c4.category_id OR x.category_id = c5.category_id)        
) l

ऊपर MSDN लिंक से:

तालिका-मूल्यवान फ़ंक्शन सही इनपुट के रूप में कार्य करता है और बाहरी तालिका अभिव्यक्ति बाएं इनपुट के रूप में कार्य करती है। बाएं इनपुट से प्रत्येक पंक्ति के लिए सही इनपुट का मूल्यांकन किया जाता है और उत्पादित पंक्तियों को अंतिम आउटपुट के लिए संयुक्त किया जाता है

मूल रूप से, APPLYएक उपश्रेणी की तरह है जो पहले दाईं ओर रिकॉर्ड्स को फ़िल्टर करता है, और फिर उन्हें आपकी शेष क्वेरी पर लागू करता है।

यह लेख यह बताने का एक बहुत अच्छा काम करता है कि यह क्या है और इसका उपयोग कब करना है: http://explainextended.com/2009/07/16/inner-join-vs-cross-apply/

हालांकि, यह नोट करना महत्वपूर्ण है कि CROSS APPLYहमेशा एक से अधिक तेजी से प्रदर्शन नहीं होता है INNER JOIN। कई स्थितियों में, यह शायद उसी के बारे में होगा। दुर्लभ मामलों में, हालांकि, मैंने वास्तव में इसे धीमा देखा है (फिर से, यह सब आपकी तालिका संरचना और क्वेरी पर ही निर्भर करता है)।

अंगूठे के एक सामान्य नियम के रूप में, यदि मैं अपने आप को बहुत अधिक सशर्त बयानों के साथ एक तालिका में शामिल होने के लिए पाता हूं, तो मैं झुकाव की ओर जाता हूं APPLY

इसके अलावा एक मजेदार नोट: OUTER APPLYएक की तरह काम करेगाLEFT JOIN

इसके अलावा, कृपया मेरी पसंद का उपयोग करने के EXISTSबजाय ध्यान दें IN। जब INएक उपकुंजी पर कर रहे हों , तो याद रखें कि यह पूरे परिणाम सेट को लौटा देगा, इसके बाद भी यह आपके मूल्य को पाया है। साथ EXISTSहै, हालांकि, यह सबक्वेरी तत्काल यह एक मैच पाता है बंद हो जाएगा।


मैंने इस समाधान को अच्छी तरह से परखा। जैसा कि आपने इसे लिखा है, यह बहुत धीमा है, लेकिन आप उस सलाह को लागू करना भूल गए हैं जिसके साथ आपने अपना संदेश शुरू किया था। जगह AND x.cat = c4.cat OR x.cat = c5.catसे x.cat = ISNULL(c5.cat, c4.cat)और में खंड से छुटकारा पाने, यह दूसरी सबसे तेजी से समाधान है, और एक वोट दें के योग्य हो पाता है क्योंकि यह बहुत जानकारीपूर्ण है।
लुइस फेरो

धन्यवाद। वास्तव में IN लाइन वहाँ नहीं थी (IN का उपयोग करने या OR के साथ चिपके रहने पर निर्णय नहीं ले सकती), मैं इसे हटा दूँगा।
वाल्वरिज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.