IF EXISTS को लंबे समय से अटैच किए गए चुनिंदा स्टेटमेंट से लिया जा रहा है


35

जब मैं निम्नलिखित कोड चलाता हूं तो इसमें 22.5 मिनट लगते हैं और 106 मिलियन पढ़ता है। हालाँकि, अगर मैं सिर्फ इनर सेलेक्ट स्टेटमेंट चलाऊं तो इसमें केवल 15 सेकंड का समय लगता है और यह 264k रीड करता है। साइड नोट के रूप में, चयन क्वेरी कोई रिकॉर्ड नहीं देती है।

किसी भी विचार क्यों IF EXISTSयह इतना लंबे समय तक चलेगा और इतने अधिक पढ़ेगा? मैंने करने के लिए चुनिंदा बयान भी बदले SELECT TOP 1 [dlc].[id]और मैंने 2 मिनट के बाद इसे मार दिया।

एक अस्थायी फिक्स के रूप में मैंने इसे एक काउंट (*) करने के लिए बदल दिया है और उस मान को एक वैरिएबल पर असाइन किया है @cnt। तब यह एक IF 0 <> @cntबयान करता है । लेकिन मुझे लगा EXISTSकि यह बेहतर होगा, क्योंकि यदि चयन कथन में रिकॉर्ड्स वापस आ जाते हैं, तो यह स्कैन / प्रदर्शन करना बंद कर देगा, क्योंकि यह कम से कम एक रिकॉर्ड मिला है, जबकि count(*)पूरी क्वेरी को पूरा करेगा। मुझे किसकी याद आ रही है?

IF EXISTS
   (SELECT [dlc].[ID]
   FROM TableDLC [dlc]
   JOIN TableD [d]
   ON [d].[ID] = [dlc].[ID]
   JOIN TableC [c]
   ON [c].[ID] = [d].[ID2]
   WHERE [c].[Name] <> [dlc].[Name])
BEGIN
   <do something>
END

4
पंक्ति लक्ष्य समस्या से बचने के लिए, एक और विचार (अनटाइटेड, माइंड यू!) विलोम का प्रयास करना हो सकता है - IF NOT EXISTS (...) BEGIN END ELSE BEGIN <do something> END
हारून बर्ट्रेंड

जवाबों:


32

किसी भी विचार क्यों IF EXISTSयह इतना लंबे समय तक चलेगा और इतने अधिक पढ़ेगा? मैंने करने के लिए चुनिंदा बयान भी बदले SELECT TOP 1 [dlc].[id]और मैंने 2 मिनट के बाद इसे मार दिया।

जैसा कि मैंने इस संबंधित प्रश्न के उत्तर में बताया:

कैसे और क्यों) TOP एक निष्पादन योजना को प्रभावित करता है?

EXISTSएक पंक्ति लक्ष्य का उपयोग करना , जहां ऑप्टिमाइज़र पहली पंक्ति को जल्दी से पता लगाने के उद्देश्य से एक निष्पादन योजना बनाता है। ऐसा करने में, यह मानता है कि डेटा समान रूप से वितरित किया गया है। उदाहरण के लिए, यदि आंकड़े बताते हैं कि 100,000 पंक्तियों में 100 अपेक्षित मैच हैं, तो यह मान लेगा कि पहला मैच खोजने के लिए इसे केवल 1,000 पंक्तियों को पढ़ना होगा।

यह अपेक्षित निष्पादन समय से अधिक समय के लिए परिणाम देगा यदि यह धारणा दोषपूर्ण हो। उदाहरण के लिए, यदि SQL सर्वर एक एक्सेस मेथड (जैसे अनऑर्डर्ड स्कैन) चुनता है, जो कि सर्च में बहुत देर से पहली मैचिंग वैल्यू का पता लगाने के लिए होता है, तो यह लगभग पूरा स्कैन हो सकता है। दूसरी ओर, यदि एक मिलान पंक्ति पहले कुछ पंक्तियों के बीच पाई जाती है, तो प्रदर्शन बहुत अच्छा होगा। यह पंक्ति लक्ष्यों के साथ मौलिक जोखिम है - असंगत प्रदर्शन।

एक अस्थायी फिक्स के रूप में मैंने इसे एक काउंट (*) करने के लिए बदल दिया है और उस मान को एक वैरिएबल पर असाइन किया है

आमतौर पर क्वेरी को सुधारना संभव है जैसे कि एक पंक्ति लक्ष्य असाइन नहीं किया गया है। पंक्ति लक्ष्य के बिना, क्वेरी तब भी समाप्त हो सकती है जब पहली मिलान पंक्ति का सामना किया गया हो (यदि सही तरीके से लिखा गया हो), लेकिन निष्पादन योजना की रणनीति अलग होने की संभावना है (और उम्मीद है, अधिक प्रभावी)। जाहिर है, गिनती (*) को सभी पंक्तियों को पढ़ने की आवश्यकता होगी, इसलिए यह एक सही विकल्प नहीं है।

यदि आप SQL Server 2008 R2 या उसके बाद चल रहे हैं, तो आप पंक्ति लक्ष्य के बिना निष्पादन योजना प्राप्त करने के लिए आमतौर पर प्रलेखित और समर्थित ट्रेस ध्वज 4138 का उपयोग कर सकते हैं । इस ध्वज को समर्थित संकेत का उपयोग करके भी निर्दिष्ट किया जा सकता है OPTION (QUERYTRACEON 4138), हालांकि यह ध्यान रखें कि यह एक योजना मार्गदर्शिका के साथ उपयोग किए जाने तक क्रम सिस्मैडिन अनुमति की आवश्यकता होती है ।

दुर्भाग्य से

उपरोक्त में से कोई भी एक IF EXISTSसशर्त विवरण के साथ कार्यात्मक नहीं है । यह केवल नियमित डीएमएल पर लागू होता है। यह आपके द्वारा किए गए वैकल्पिक सूत्रीकरण के साथ काम करेगाSELECT TOP (1) । यह प्रयोग करने से बेहतर हो सकता है COUNT(*), जिसे पहले बताई गई सभी योग्य पंक्तियों को गिनना है।

उस ने कहा, इस आवश्यकता को व्यक्त करने के लिए कई तरीके हैं जो आपको खोज को जल्दी समाप्त करते हुए पंक्ति लक्ष्य से बचने या नियंत्रित करने की अनुमति देंगे। एक अंतिम उदाहरण:

DECLARE @Exists bit;

SELECT @Exists =
    CASE
        WHEN EXISTS
        (
            SELECT [dlc].[ID]
            FROM TableDLC [dlc]
            JOIN TableD [d]
            ON [d].[ID] = [dlc].[ID]
            JOIN TableC [c]
            ON [c].[ID] = [d].[ID2]
            WHERE [c].[Name] <> [dlc].[Name]
        )
        THEN CONVERT(bit, 1)
        ELSE CONVERT(bit, 0)
    END
OPTION (QUERYTRACEON 4138);

IF @Exists = 1
BEGIN
    ...
END;

आपके द्वारा प्रदान किया गया उच्चतम उदाहरण 3.75 मिनट में चला और 46 मीटर रीड किया। इसलिए, अपनी मूल क्वेरी की तुलना में तेज़ होने पर, मुझे लगता है कि इस मामले में मैं @cnt = count (*) के साथ चिपका रहूंगा और इसके बाद वाले वेरिएबल का मूल्यांकन करूंगा। खासकर जब से 99% इस समय चलता है, इसमें कुछ भी नहीं होगा। यह आपके और आरओबी के जवाबों के आधार पर लगता है कि एक्ज़िस्ट केवल तभी अच्छा है जब आप वास्तव में किसी प्रकार के परिणाम की उम्मीद करते हैं और यह परिणाम आपके डेटा के भीतर समान रूप से वितरित किया जाता है।
क्रिस वुड्स

3
@ क्रिसहुड्स: आपने कहा "विशेष रूप से उस समय के 99% के बाद से इसमें कुछ भी नहीं होगा"। यह बहुत अधिक गारंटी देता है कि एक का पंक्ति लक्ष्य एक बुरा विचार है, क्योंकि आप उम्मीद करते हैं कि आम तौर पर कोई पंक्तियां नहीं होनी चाहिए, और यह पता लगाने के लिए सब कुछ स्कैन करना होगा कि कोई भी नहीं है। यदि आप कुछ चालाक सूचकांक नहीं जोड़ सकते हैं, तो COUNT (*) के साथ रहें।
रॉस प्रेसर

25

क्योंकि EXISTS को केवल एक पंक्ति खोजने की आवश्यकता है, यह एक पंक्ति लक्ष्य का उपयोग करेगा। यह कभी-कभी कम-से-आदर्श योजना का उत्पादन कर सकता है। यदि आप उम्मीद करते हैं कि यह आपके लिए वैसा ही होगा, तो चर के परिणाम के साथ एक चर आबाद करें COUNT(*)और फिर उस चर का परीक्षण करें कि यह 0 से अधिक है या नहीं।

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

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