CROSS APPLY बाहरी जुड़ाव का उत्पादन करता है


17

विभाजन पर एसक्यूएल गिनती के जवाब में एरिक डार्लिंग ने इस कोड को इस कमी के लिए काम करने के लिए पोस्ट किया है COUNT(DISTINCT) OVER ():

SELECT      *
FROM        #MyTable AS mt
CROSS APPLY (   SELECT COUNT(DISTINCT mt2.Col_B) AS dc
                FROM   #MyTable AS mt2
                WHERE  mt2.Col_A = mt.Col_A
                -- GROUP BY mt2.Col_A 
            ) AS ca;

क्वेरी का उपयोग करता है CROSS APPLY(नहीं OUTER APPLY) तो आंतरिक जोड़ के बजाय निष्पादन योजना में एक बाहरी सम्मिलित क्यों है ?

यहाँ छवि विवरण दर्ज करें

इसके अलावा समूह को अव्यवस्थित करके समूह में शामिल होने का परिणाम क्या है?

यहाँ छवि विवरण दर्ज करें

मुझे नहीं लगता कि डेटा महत्वपूर्ण है, लेकिन दूसरे प्रश्न पर केविनवाट द्वारा दी गई नकल से:

create table #MyTable (
Col_A varchar(5),
Col_B int
)

insert into #MyTable values ('A',1)
insert into #MyTable values ('A',1)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',2)
insert into #MyTable values ('A',3)

insert into #MyTable values ('B',4)
insert into #MyTable values ('B',4)
insert into #MyTable values ('B',5)

जवाबों:


23

सारांश

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 <> फिडेल डेमो


-3

क्रॉस अप्लाई डेटा पर एक लॉजिकल ऑपरेशन है। उस डेटा को कैसे प्राप्त किया जाए, यह तय करते समय SQL सर्वर आपके इच्छित डेटा को प्राप्त करने के लिए उपयुक्त भौतिक ऑपरेटर को चुनता है।

कोई भौतिक अनुप्रयोग ऑपरेटर नहीं है और SQL सर्वर इसे उपयुक्त और उम्मीद के मुताबिक कुशल ऑपरेटर में अनुवाद करता है।

आप नीचे दिए गए लिंक में भौतिक ऑपरेटरों की एक सूची पा सकते हैं।

https://docs.microsoft.com/en-us/sql/relational-databases/showplan-logical-and-physical-operators-reference?view=sql-server-2017

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

आमतौर पर, एक तार्किक ऑपरेशन कई भौतिक ऑपरेटरों द्वारा लागू किया जा सकता है। हालांकि, दुर्लभ मामलों में, एक भौतिक ऑपरेटर कई तार्किक कार्यों को भी लागू कर सकता है।

संपादित करें / ऐसा लगता है कि मैंने आपके प्रश्न को गलत समझा। SQL सर्वर सामान्य रूप से सबसे उपयुक्त ऑपरेटर का चयन करेगा। आपकी क्वेरी को दोनों तालिकाओं के सभी संयोजनों के लिए मानों को वापस करने की आवश्यकता नहीं है, जब एक क्रॉस जॉइन का उपयोग किया जाएगा। प्रत्येक पंक्ति के लिए इच्छित मूल्य की गणना करना जो यहाँ किया जाता है।

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