सारांश
SQL सर्वर सही जॉइन (आंतरिक या बाहरी) का उपयोग करता है और उन अनुमानों को जोड़ता है जहां लागू होने और शामिल होने के बीच आंतरिक अनुवाद करते समय मूल क्वेरी के सभी शब्दार्थों का सम्मान करना आवश्यक होता है ।
योजनाओं के अंतर को SQL सर्वर में क्लॉज द्वारा समूह के साथ और बिना समूह के विभिन्न शब्दार्थों द्वारा समझाया जा सकता है ।
विवरण
सम्मिलित हों बनाम लागू करें
हम एक आवेदन और एक जुड़ने के बीच अंतर करने में सक्षम होने की आवश्यकता होगी :
लागू
भीतरी (कम) के इनपुट लागू , बाहरी (ऊपरी) इनपुट के प्रत्येक पंक्ति के लिए चलाया जाता है एक या अधिक अंदरूनी हिस्से पैरामीटर वर्तमान बाहरी पंक्ति द्वारा प्रदान मूल्यों के साथ। लागू होने का समग्र परिणाम पैरामीटराइज्ड इनर साइड एक्जीक्यूटर्स द्वारा निर्मित सभी पंक्तियों का संयोजन (यूनियन ऑल) है। मापदंडों की उपस्थिति का मतलब है लागू करना कभी-कभी सहसंबद्ध जुड़ने के रूप में जाना जाता है।
नेस्टेड लूप्स द्वारा निष्पादन योजनाओं में एक आवेदन हमेशा लागू किया जाता है ऑपरेटर । ऑपरेटर के पास विधेय में शामिल होने के बजाय एक बाहरी संदर्भ संपत्ति होगी । बाहरी संदर्भ लूप के प्रत्येक पुनरावृत्ति पर बाहरी तरफ से आंतरिक तरफ से पारित होने वाले पैरामीटर हैं।
शामिल हों
एक जॉइन ज्वाइन ऑपरेटर पर अपनी जॉइन प्रेडिकेट का मूल्यांकन करता है। आम तौर पर हैश मैच द्वारा लागू किया जा सकता है ,SQL सर्वर में मर्ज या नेस्टेड लूप ऑपरेटरों ।
जब नेस्टेड लूप्स चुना जाता है, तो इसे बाहरी संदर्भों की कमी (और आमतौर पर एक सम्मिलित विधेयकों की उपस्थिति) द्वारा लागू किया जा सकता है । किसी जुड़ने का आंतरिक इनपुटबाहरी इनपुट से कभी भी मूल्यों को संदर्भित नहीं करता है - आंतरिक पक्ष को अभी भी प्रत्येक बाहरी पंक्ति के लिए एक बार निष्पादित किया जाता है, लेकिन आंतरिक पक्ष निष्पादन वर्तमान बाहरी पंक्ति से किसी भी मान पर निर्भर नहीं होता है।
अधिक जानकारी के लिए मेरी पोस्ट देखें बनाम नेस्टेड लूप्स जॉइन करें ।
... क्यों निष्पादन योजना में एक बाहरी सम्मिलित है बजाय एक भीतरी में शामिल होने?
बाहरी जुड़ाव तब उत्पन्न होता है जब ऑप्टिमाइज़र किसी अपॉइंटमेंट को ज्वाइन (रूल नामक नियम का उपयोग करके ApplyHandler
) करता है, यह देखने के लिए कि क्या यह एक सस्ता जॉइन-बेस्ड प्लान मिल सकता है। शुद्धता के लिए बाहरी जुड़ाव होना आवश्यक है जब लागू एक होता है अदिश कुल । एक आंतरिक जुड़ाव की गारंटी नहीं दी जाएगी, क्योंकि हम जो मूल आवेदन देखेंगे , वही परिणाम देंगे।
स्केलर और वेक्टर समुच्चय
- संबंधित
GROUP BY
खण्ड के बिना एक समुच्चय एक अदिश समुच्चय है।
- इसी
GROUP BY
खंड के साथ एक समुच्चय एक वेक्टर समुच्चय है।
SQL सर्वर में, एक स्केलर एग्रीगेट हमेशा एक पंक्ति उत्पन्न करेगा, भले ही उसे एग्रीगेट करने के लिए कोई पंक्तियाँ न दी गई हों। उदाहरण के लिए, COUNT
बिना पंक्तियों के स्केलर एग्रीगेट शून्य है। बिना पंक्तियों का एक वेक्टर COUNT
समुच्चय खाली सेट (कोई पंक्तियाँ नहीं है)।
निम्नलिखित खिलौना प्रश्नों में अंतर का वर्णन है। आप स्केलर और वेक्टर समुच्चय के साथ मेरे लेख फन में स्केलर और वेक्टर समुच्चय के बारे में अधिक पढ़ सकते हैं ।
-- Produces a single zero value
SELECT COUNT_BIG(*) FROM #MyTable AS MT WHERE 0 = 1;
-- Produces no rows
SELECT COUNT_BIG(*) FROM #MyTable AS MT WHERE 0 = 1 GROUP BY ();
db <> फिडेल डेमो
जुड़ने के लिए ट्रांसफ़ॉर्मिंग अप्लाई
मैंने पहले उल्लेख किया है कि जब मूल लागू एक स्केलर समुच्चय होता है , तो शुद्धता के लिए बाहरी जुड़ने की आवश्यकता होती है । यह दिखाने के लिए कि यह मामला विस्तार से है, मैं प्रश्न क्वेरी के सरलीकृत उदाहरण का उपयोग करूंगा:
DECLARE @A table (A integer NULL, B integer NULL);
DECLARE @B table (A integer NULL, B integer NULL);
INSERT @A (A, B) VALUES (1, 1);
INSERT @B (A, B) VALUES (2, 2);
SELECT * FROM @A AS A
CROSS APPLY (SELECT c = COUNT_BIG(*) FROM @B AS B WHERE B.A = A.A) AS CA;
स्तंभ के लिए सही परिणाम c
है शून्य है, क्योंकि COUNT_BIG
एक है अदिश कुल। प्रपत्र से जुड़ने के लिए इस लागू क्वेरी का अनुवाद करते समय, SQL सर्वर एक आंतरिक विकल्प बनाता है जो निम्न के समान दिखाई देगा यदि इसे T-SQL में व्यक्त किया गया हो:
SELECT A.*, c = COALESCE(J1.c, 0)
FROM @A AS A
LEFT JOIN
(
SELECT B.A, c = COUNT_BIG(*)
FROM @B AS B
GROUP BY B.A
) AS J1
ON J1.A = A.A;
एक असंबंधित जॉइन के रूप में आवेदन को फिर से लिखने के लिए, हमें GROUP BY
व्युत्पन्न तालिका में परिचय देना होगा (अन्यथा इसमें A
शामिल होने के लिए कोई कॉलम नहीं हो सकता है )। जुड़ने के लिए बाहरी जुड़ाव होना चाहिए इसलिए तालिका से प्रत्येक पंक्ति @A
आउटपुट में एक पंक्ति का उत्पादन जारी रखती है। लेफ्ट जॉइन NULL
कॉलम के लिए उत्पादन करेगा c
जब ज्वाइन पेडेडेट सच का मूल्यांकन नहीं करता है। इसका NULL
अनुवाद शून्य से करने की आवश्यकता हैCOALESCE
से एक सही परिवर्तन पूरा करने के लिए लागू होते हैं ।
नीचे दिया गया डेमो दिखाता है कि दोनों बाहरी जुड़ते हैं और मूल लागू क्वेरी के रूप में जुड़नेCOALESCE
का उपयोग करके एक ही परिणाम का उत्पादन करने की आवश्यकता होती है :
db <> फिडेल डेमो
उसके साथ GROUP BY
... क्लॉज के द्वारा समूह को असहज करने का परिणाम अंदरूनी जुड़ाव क्यों होता है?
सरलीकृत उदाहरण जारी है, लेकिन एक जोड़ने GROUP BY
:
DECLARE @A table (A integer NULL, B integer NULL);
DECLARE @B table (A integer NULL, B integer NULL);
INSERT @A (A, B) VALUES (1, 1);
INSERT @B (A, B) VALUES (2, 2);
-- Original
SELECT * FROM @A AS A
CROSS APPLY
(SELECT c = COUNT_BIG(*) FROM @B AS B WHERE B.A = A.A GROUP BY B.A) AS CA;
COUNT_BIG
अब एक है वेक्टर सकल, यह है, इसलिए एक खाली इनपुट सेट के लिए सही परिणाम नहीं रह गया है शून्य है सब पर कोई पंक्ति । दूसरे शब्दों में, ऊपर दिए गए बयानों को चलाने से कोई आउटपुट नहीं मिलता है।
ये अर्थ विज्ञान जब से अनुवाद बहुत सम्मान करने के लिए आसान कर रहे हैं लागू करने के लिए शामिल होने के बाद से, CROSS APPLY
स्वाभाविक रूप से किसी भी बाहरी पंक्ति है कि कोई अंदरूनी हिस्से पंक्तियों उत्पन्न करता है को खारिज कर दिया। इसलिए हम बिना किसी अतिरिक्त अभिव्यक्ति के प्रक्षेपण के साथ अब आंतरिक रूप से जुड़ने का उपयोग कर सकते हैं:
-- Rewrite
SELECT A.*, J1.c
FROM @A AS A
JOIN
(
SELECT B.A, c = COUNT_BIG(*)
FROM @B AS B
GROUP BY B.A
) AS J1
ON J1.A = A.A;
नीचे दिए गए डेमो से पता चलता है कि आंतरिक रूप से फिर से लिखना वैसा ही परिणाम उत्पन्न करता है जैसा कि वेक्टर समुच्चय के साथ मूल लागू होता है:
db <> फिडेल डेमो
ऑप्टिमाइज़र छोटी मेज के साथ एक मर्ज इनर ज्वाइन चुनने के लिए होता है क्योंकि यह एक सस्ती जॉइन पाता है प्लान को जल्दी से (अच्छी पर्याप्त योजना मिली)। लागत आधारित ऑप्टिमाइज़र एक आवेदन में वापस जुड़ने के लिए फिर से लिख सकता है - शायद एक सस्ता लागू योजना ढूंढना, क्योंकि यह यहां होगा यदि एक लूप शामिल होता है या forceseek संकेत का उपयोग किया जाता है - लेकिन यह इस मामले में प्रयास के लायक नहीं है।
टिप्पणियाँ
सरलीकृत अंतर को अधिक स्पष्ट रूप से दिखाने के लिए सरलीकृत उदाहरण विभिन्न सामग्रियों के साथ विभिन्न तालिकाओं का उपयोग करते हैं।
कोई यह तर्क दे सकता है कि ऑप्टिमाइज़र को किसी भी बेमेल (गैर-जॉइनिंग) पंक्तियों को बनाने में सक्षम नहीं होने के बारे में तर्क करने में सक्षम होना चाहिए, लेकिन इसमें वह तर्क शामिल नहीं है। एक ही तालिका को एक क्वेरी में कई बार एक्सेस करना सामान्य रूप से अलगाव स्तर और समवर्ती गतिविधि के आधार पर सामान्य रूप से समान परिणाम उत्पन्न करने की गारंटी नहीं है।
ऑप्टिमाइज़र इन शब्दार्थों और किनारे के मामलों के बारे में चिंता करता है ताकि आपको नहीं करना पड़े।
बोनस: इनर लागू योजना
एसक्यूएल सर्वर कर सकते हैं उत्पादन एक आंतरिक लागू योजना (एक आंतरिक नहीं शामिल होने के उदाहरण क्वेरी के लिए योजना!), यह सिर्फ लागत कारणों के लिए के लिए नहीं चुनता है। प्रश्न में दिखाया गया बाहरी जॉइन प्लान की लागत मेरे लैपटॉप के SQL सर्वर 2017 उदाहरण पर 0.02898 यूनिट है।
आप केवल चित्रण के लिए अनिर्दिष्ट और असमर्थित ट्रेस ध्वज 9114 (जो अक्षम करता है आदि) का उपयोग करके एक लागू (सहसंबंधित जुड़ाव) योजना को बाध्य कर सकते हैं ApplyHandler
:
SELECT *
FROM #MyTable AS mt
CROSS APPLY
(
SELECT COUNT_BIG(DISTINCT mt2.Col_B) AS dc
FROM #MyTable AS mt2
WHERE mt2.Col_A = mt.Col_A
--GROUP BY mt2.Col_A
) AS ca
OPTION (QUERYTRACEON 9114);
यह एक का उत्पादन लागू एक आलसी सूचकांक स्पूल के साथ नेस्टेड छोरों योजना। कुल अनुमानित लागत 0.0463983 (चयनित योजना से अधिक):
ध्यान दें कि एप्लाइड नेस्टेड लूप्स का उपयोग करने वाली निष्पादन योजना GROUP BY
क्लॉज की उपस्थिति की परवाह किए बिना "इनर जॉइन" शब्दार्थों का उपयोग करके सही परिणाम प्रस्तुत करती है ।
वास्तविक दुनिया में, हमारे पास आमतौर पर उदाहरण के लिए, इस विकल्प को स्वाभाविक रूप से चुनने के लिए SQL सर्वर को प्रोत्साहित करने के लिए आवेदन के अंदरूनी हिस्से पर एक समर्थन का समर्थन करने के लिए एक सूचकांक होगा :
CREATE INDEX i ON #MyTable (Col_A, Col_B);
db <> फिडेल डेमो