पुनरावर्ती सामान्य तालिका अभिव्यक्ति में EXCEPT का उपयोग करना


33

निम्नलिखित क्वेरी अनंत पंक्तियों को क्यों लौटाती है? मुझे उम्मीद थी कि इस EXCEPTक्लॉज से रिक्रिएशन खत्म हो जाएगा।

with cte as (
    select *
    from (
        values(1),(2),(3),(4),(5)
    ) v (a)
)
,r as (
    select a
    from cte
    where a in (1,2,3)
    union all
    select a
    from (
        select a
        from cte
        except
        select a
        from r
    ) x
)
select a
from r

स्टैक ओवरफ्लो पर एक सवाल का जवाब देने की कोशिश करते हुए मैं इस पार आया ।

जवाबों:


26

एक पुनरावर्ती CTE में वर्तमान स्थिति के बारे में जानकारी के लिए मार्टिन स्मिथ का उत्तर देखें EXCEPT

यह बताने के लिए कि आप क्या देख रहे थे, और क्यों:

मैं एंकर मूल्यों और पुनरावर्ती आइटम को स्पष्ट करने के लिए (यह शब्दार्थ नहीं बदलता है) के बीच अंतर बनाने के लिए, मैं यहां एक टेबल चर का उपयोग कर रहा हूं।

DECLARE @V TABLE (a INTEGER NOT NULL)
INSERT  @V (a) VALUES (1),(2)
;
WITH rCTE AS 
(
    -- Anchor
    SELECT
        v.a
    FROM @V AS v

    UNION ALL

    -- Recursive
    SELECT
        x.a
    FROM
    (
        SELECT
            v2.a
        FROM @V AS v2

        EXCEPT

        SELECT
            r.a
        FROM rCTE AS r
    ) AS x
)
SELECT
    r2.a
FROM rCTE AS r2
OPTION (MAXRECURSION 0)

क्वेरी योजना है:

पुनरावर्ती CTE योजना

निष्पादन योजना (SELECT) की जड़ से शुरू होता है और कंट्रोल ट्री को इंडेक्स स्पूल, कॉनकेनटेशन और फिर टॉप-लेवल टेबल स्कैन में पास करता है।

स्कैन से पहली पंक्ति पेड़ के ऊपर से गुजरती है और स्टैक स्पूल में संग्रहीत (ए) होती है, और (बी) क्लाइंट को वापस कर दिया जाता है। कौन सी पंक्ति पहले परिभाषित नहीं की गई है, लेकिन मान लें कि यह तर्क के लिए मान {1} वाली पंक्ति है। इसलिए प्रकट होने वाली पहली पंक्ति {1} है।

नियंत्रण फिर से टेबल स्कैन के लिए नीचे जाता है (कॉननेशन ऑपरेटर अगले एक को खोलने से पहले इसके बाहरी इनपुट से सभी पंक्तियों का उपभोग करता है)। स्कैन दूसरी पंक्ति (मान {2}) का उत्सर्जन करता है, और यह फिर से ग्राहक को स्टैक और आउटपुट पर संग्रहीत किए जाने वाले पेड़ को पास करता है। क्लाइंट को अब अनुक्रम {1}, {2} प्राप्त हुआ है।

एक सम्मेलन को अपनाना जहां LIFO स्टैक का शीर्ष बाईं ओर है, स्टैक में अब {2, 1} है। चूंकि नियंत्रण फिर से टेबल स्कैन के पास जाता है, इसलिए यह कोई और पंक्तियां नहीं बताता है, और नियंत्रण वापस कॉन्टेनेटेशन ऑपरेटर के पास जाता है, जो इसे दूसरा इनपुट खोलता है (इसे स्टैक स्पूल तक जाने के लिए एक पंक्ति की आवश्यकता होती है), और नियंत्रण इनर से जुड़ता है पहली बार।

इनर जॉइन टेबल स्पूल को इसके बाहरी इनपुट पर कॉल करता है, जो स्टैक {2} से शीर्ष पंक्ति को पढ़ता है और इसे वर्कटेबल से हटा देता है। स्टैक में अब {1} है।

अपने बाहरी इनपुट पर एक पंक्ति प्राप्त करने के बाद, इनर जॉइन लेफ्ट एंटी-सेमी जॉइन (LASJ) के लिए अपने आंतरिक इनपुट को नियंत्रित करता है। यह अपने बाहरी इनपुट से एक पंक्ति का अनुरोध करता है, सॉर्ट करने के लिए नियंत्रण। सॉर्ट एक अवरुद्ध अवरोधक है, इसलिए यह तालिका चर से सभी पंक्तियों को पढ़ता है और उन्हें आरोही करता है (जैसा कि ऐसा होता है)।

सॉर्ट द्वारा उत्सर्जित पहली पंक्ति इसलिए मान {1} है। LASJ का आंतरिक पक्ष पुनरावर्ती सदस्य (स्टैक से अभी-अभी पॉप अप किया गया मान) का वर्तमान मान लौटाता है, जो {2} है। LASJ में मान {1} और {2} हैं, इसलिए {1} को उत्सर्जित किया जाता है, क्योंकि मान मेल नहीं खाते हैं।

यह पंक्ति {1} क्वेरी प्लान ट्री को इंडेक्स (स्टैक) स्पूल तक प्रवाहित करती है जहां इसे स्टैक में जोड़ा जाता है, जिसमें अब {1, 1}, और क्लाइंट को उत्सर्जित किया जाता है। क्लाइंट को अब अनुक्रम {1}, {2}, {1} प्राप्त हुआ है।

नियंत्रण अब कॉनसेनटेशन पर वापस जाता है, आंतरिक पक्ष के पीछे (यह पिछली बार एक पंक्ति लौटाता है, फिर से कर सकता है), इनर जॉइन के माध्यम से, LASJ तक। यह अपने आंतरिक इनपुट को फिर से पढ़ता है, सॉर्ट से मान {2} प्राप्त करता है।

पुनरावर्ती सदस्य अभी भी {2} है, इसलिए इस बार LASJ {2} और {2} पाता है, जिसके परिणामस्वरूप कोई पंक्ति उत्सर्जित नहीं होती है। इसके आंतरिक इनपुट पर कोई अधिक पंक्तियाँ नहीं मिल रही हैं (सॉर्ट अब पंक्तियों से बाहर है), नियंत्रण इनर जॉइन तक वापस जाता है।

इनर जॉइन इसके बाहरी इनपुट को पढ़ता है, जिसके परिणामस्वरूप {1} स्टैक {1} से पॉपअप किया जा रहा है, स्टैक को केवल {1} के साथ छोड़ दिया जाता है। यह प्रक्रिया अब दोहराती है, टेबल स्कैन के एक नए आह्वान से मान {2} के साथ और LASJ परीक्षण पास करके सॉर्ट में जोड़ा जा रहा है, और क्लाइंट को पास कर दिया गया है, जिसे अब {1}, {2} प्राप्त हुआ है, {1}, {2} ... और गोल हम चलते हैं।

मेरा पसंदीदा स्पष्टीकरणपुनरावर्ती CTE योजनाओं में प्रयुक्त स्टैक स्पूल की क्रेग फ्रीडमैन है।


31

पुनरावर्ती CTE के BOL विवरण पुनरावर्ती निष्पादन के शब्दार्थ का वर्णन इस प्रकार है:

  1. लंगर और पुनरावर्ती सदस्यों में सीटीई की अभिव्यक्ति को विभाजित करें।
  2. पहले आह्वान या आधार परिणाम सेट (T0) बनाने वाले एंकर सदस्य (ओं) को चलाएँ।
  3. एक इनपुट के रूप में Ti और आउटपुट के रूप में Ti + 1 के साथ पुनरावर्ती सदस्य को चलाएँ।
  4. एक खाली सेट वापस आने तक चरण 3 को दोहराएं।
  5. परिणाम सेट लौटाएं। यह T0 से Tn का एक UNION ALL है।

नोट उपरोक्त एक तार्किक विवरण है।ऑपरेशन का भौतिक क्रम कुछ हद तक अलग हो सकता है जैसा कि यहाँ सचित्र है

आपके सीटीई में इसे लागू करने से मुझे निम्न पैटर्न के साथ एक अनंत लूप की उम्मीद होगी

+-----------+---------+---+---+---+
| Invocation| Results             |
+-----------+---------+---+---+---+
|         1 |       1 | 2 | 3 |   |
|         2 |       4 | 5 |   |   |
|         3 |       1 | 2 | 3 |   |
|         4 |       4 | 5 |   |   |
|         5 |       1 | 2 | 3 |   |
+-----------+---------+---+---+---+ 

इसलिये

select a
from cte
where a in (1,2,3)

एंकर अभिव्यक्ति है। यह स्पष्ट 1,2,3रूप से लौटता हैT0

इसके बाद पुनरावर्ती अभिव्यक्ति चलता है

select a
from cte
except
select a
from r

साथ 1,2,3इनपुट कि का उत्पादन निकलेगा के रूप में 4,5के रूप में T1तो प्रत्यावर्तन के अगले दौर के लिए वापस आ जाएगी में कि वापस प्लग-इन 1,2,3और इतने पर अनिश्चित काल के लिए।

हालांकि वास्तव में ऐसा नहीं होता है। ये पहले 5 आह्वान के परिणाम हैं

+-----------+---------+---+---+---+
| Invocation| Results             |
+-----------+---------+---+---+---+
|         1 |       1 | 2 | 3 |   |
|         2 |       1 | 2 | 4 | 5 |
|         3 |       1 | 2 | 3 | 4 |
|         4 |       1 | 2 | 3 | 5 |
|         5 |       1 | 2 | 3 | 4 |
+-----------+---------+---+---+---+

उपयोग OPTION (MAXRECURSION 1)करने से और वृद्धि में समायोजन से 1यह देखा जा सकता है कि यह एक चक्र में प्रवेश करता है जहां प्रत्येक क्रमिक स्तर लगातार आउटपुट 1,2,3,4और के बीच टॉगल करेगा 1,2,3,5

जैसा कि इस ब्लॉग पोस्ट में @Quassnoi ने चर्चा की है । देखे गए परिणामों का पैटर्न ऐसा है मानो प्रत्येक मंगलाचरण कहाँ कर रहा है(1),(2),(3),(4),(5) EXCEPT (X)X पिछले आह्वान से अंतिम पंक्ति है।

संपादित करें: एसक्यूएल कीवी के उत्कृष्ट उत्तर को पढ़ने के बाद यह दोनों स्पष्ट है कि ऐसा क्यों होता है और यह पूरी कहानी नहीं है कि स्टैक पर अभी भी सामान का भार है जो कभी भी संसाधित नहीं हो सकता है।

एंकर उत्सर्जन करता है 1,2,3 क्लाइंट स्टैक सामग्री का है3,2,1

स्टैक से 3 पॉपअप, स्टैक सामग्री 2,1

LASJ रिटर्न 1,2,4,5 , स्टैक सामग्री5,4,2,1,2,1

5 ढेर, ढेर सामग्री बंद 4,2,1,2,1

LASJ रिटर्न 1,2,3,4 स्टैक सामग्री4,3,2,1,5,4,2,1,2,1

4 ढेर, ढेर सामग्री बंद 3,2,1,5,4,2,1,2,1

LASJ रिटर्न 1,2,3,5 स्टैक सामग्री5,3,2,1,3,2,1,5,4,2,1,2,1

5 ढेर, ढेर सामग्री बंद 3,2,1,3,2,1,5,4,2,1,2,1

LASJ 1,2,3,4 स्टैक सामग्री लौटाता है4,3,2,1,3,2,1,3,2,1,5,4,2,1,2,1

यदि आप पुनरावर्ती सदस्य को तार्किक रूप से समकक्ष (डुप्लिकेट / NULLs की अनुपस्थिति में) अभिव्यक्ति से प्रतिस्थापित करते हैं

select a
from (
    select a
    from cte
    where a not in 
    (select a
    from r)
) x

यह अनुमति नहीं है और त्रुटि उठाती है "पुनरावर्ती संदर्भों को उपश्रेणियों में अनुमति नहीं है।" तो शायद यह एक निरीक्षण है जो EXCEPTइस मामले में भी अनुमति है।

परिवर्धन: Microsoft ने अब नीचे दिए अनुसार मेरी कनेक्ट प्रतिक्रिया पर प्रतिक्रिया दी है

जैक का अनुमान सही है: यह एक वाक्यविन्यास त्रुटि होनी चाहिए थी; पुनरावर्ती संदर्भों को EXCEPTखंडों में वास्तव में अनुमति नहीं दी जानी चाहिए । हम इस बग को आगामी सेवा रिलीज में संबोधित करने की योजना बना रहे हैं। इस बीच, मैं EXCEPT खंडों में पुनरावर्ती संदर्भों से बचने का सुझाव दूंगा।

EXCEPTएएनएसआई एसक्यूएल मानक का पालन करने पर हम पुनरावृत्ति को प्रतिबंधित करते हैं, जिसमें इस प्रतिबंध को तब से शामिल किया गया है जब से पुनरावृत्ति पेश की गई थी (1999 में मेरा मानना ​​है)। EXCEPTइस तरह की एसक्यूएल के रूप में घोषणात्मक भाषाओं में पुनर्विचार (जिसे "अप्रतिबंधित निषेध" भी कहा जाता है) के लिए शब्दार्थ क्या होना चाहिए, इस पर कोई व्यापक समझौता नहीं हुआ है । इसके अलावा, आरडीबीएमएस प्रणाली में ऐसे शब्दार्थों को कुशलतापूर्वक (यथोचित आकार के डेटाबेस के लिए) कार्यान्वित करना कठिन (यदि असंभव नहीं है)।

और ऐसा लगता है कि अंतिम कार्यान्वयन 2014 में डेटाबेस के लिए 120 या उससे अधिक की संगतता स्तर के साथ किया गया था ।

EXCEPT क्लॉज में पुनरावर्ती संदर्भ ANSI SQL मानक के अनुपालन में एक त्रुटि उत्पन्न करता है।

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