SQL रिकर्सन वास्तव में कैसे काम करता है?


19

अन्य प्रोग्रामिंग भाषाओं से एसक्यूएल में आ रहा है, एक पुनरावर्ती क्वेरी की संरचना बल्कि विषम लगती है। इसके साथ कदम से कदम मिलाकर चलें, और ऐसा लगता है कि यह अलग हो जाएगा।

निम्नलिखित सरल उदाहरण पर विचार करें:

CREATE TABLE #NUMS
(N BIGINT);

INSERT INTO #NUMS
VALUES (3), (5), (7);

WITH R AS
(
    SELECT N FROM #NUMS
    UNION ALL
    SELECT N*N AS N FROM R WHERE N*N < 10000000
)
SELECT N FROM R ORDER BY N;

चलो इसके माध्यम से चलते हैं।

सबसे पहले, एंकर सदस्य निष्पादित करता है और परिणाम सेट आर में डाल दिया जाता है। इसलिए आर को {3, 5, 7} से आरंभ किया जाता है।

फिर, UNION ALL के नीचे निष्पादन बूँदें और पुनरावर्ती सदस्य को पहली बार निष्पादित किया जाता है। यह R पर निष्पादित होता है (अर्थात R पर, जो वर्तमान में हमारे हाथ में है: {3, 5, 7})। इसके परिणामस्वरूप {९, २५, ४ ९} हैं।

यह इस नए परिणाम के साथ क्या करता है? क्या यह {९, २५, ४ ९} को मौजूदा {३, ५, label} तक ले जाता है, परिणामी यूनियन आर को लेबल करता है, और फिर वहाँ से पुनरावृत्ति करता है? या क्या यह नया परिणाम {9, 25, 49} होने के लिए आर को फिर से परिभाषित करता है और बाद में सभी संघ-अंतर्ग्रहण करता है?

न तो विकल्प समझ में आता है।

यदि R अब {3, 5, 7, 9, 25, 49} है और हम पुनरावृत्ति के अगले पुनरावृत्ति को निष्पादित करते हैं, तो हम {9, 25, 49, 81, 625, 2401} के साथ समाप्त हो जाएंगे और हम {3, 5, 7} खो दिया है।

यदि आर अब केवल {९, २५, ४ ९} है, तो हमें एक गलत समस्या है। R को एंकर सदस्य परिणाम सेट और उसके बाद के सभी पुनरावर्ती सदस्य परिणाम सेटों का मिलन समझा जाता है। जबकि {९, २५, ४ ९} केवल R का एक घटक है। यह पूर्ण R नहीं है जो हमने अभी तक अर्जित किया है। इसलिए, आर से चयन के रूप में पुनरावर्ती सदस्य को लिखने का कोई मतलब नहीं है।


मैं निश्चित रूप से सराहना करता हूं कि @Max Vernon और @Michael S ने नीचे क्या विस्तृत किया है। अर्थात्, (1) सभी घटकों को पुनरावर्तन सीमा या अशक्त सेट तक बनाया गया है, और फिर (2) सभी घटकों को एक साथ जोड़ा जाता है। यह है कि मैं वास्तव में काम करने के लिए SQL पुनरावर्तन को कैसे समझता हूं।

यदि हम एसक्यूएल को फिर से डिज़ाइन कर रहे थे, तो शायद हम एक अधिक स्पष्ट और स्पष्ट सिंटैक्स लागू करेंगे, कुछ इस तरह:

WITH R AS
(
    SELECT   N
    INTO     R[0]
    FROM     #NUMS
    UNION ALL
    SELECT   N*N AS N
    INTO     R[K+1]
    FROM     R[K]
    WHERE    N*N < 10000000
)
SELECT N FROM R ORDER BY N;

गणित में एक प्रेरक प्रमाण की तरह।

SQL रिकर्सन के साथ समस्या यह है कि यह वर्तमान में खड़ा है कि यह एक भ्रमित तरीके से लिखा गया है। जिस तरह से यह लिखा गया है कि प्रत्येक घटक आर से चयन करके बनता है, लेकिन इसका मतलब यह नहीं है कि अब तक का पूर्ण आर (या, प्रतीत होता है) का निर्माण किया गया है। यह सिर्फ पिछले घटक का मतलब है।


"यदि R अब {3, 5, 7, 9, 25, 49} है और हम पुनरावृत्ति की अगली पुनरावृत्ति को अंजाम देते हैं, तो हम {9, 25, 49, 81, 625, 2401} के साथ समाप्त हो जाएंगे और हम ' ve खो {3, 5, 7}। " यदि आप इस तरह से काम करते हैं तो मैं यह नहीं देखता कि आप {3,5,7} कैसे खो देते हैं।
ypercube y

@ yper-madhat-cubeᵀᴹ - मैं उस पहली परिकल्पना का पालन कर रहा था जिसे मैंने प्रस्तावित किया था, अर्थात्, यदि मध्यवर्ती आर उस बिंदु तक गणना की गई सब कुछ का एक संचय है? फिर, पुनरावर्ती सदस्य के अगले पुनरावृत्ति पर, आर के प्रत्येक तत्व को चुकता किया जाता है। इस प्रकार, {3, 5, 7} बन जाता है {9, 25, 49} और हमारे पास फिर कभी {3, 5, 7} नहीं है। R. दूसरे शब्दों में, {3, 5, 7} आर से खो गया है
UnLogicGuys

जवाबों:


26

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

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

इसलिए प्रत्येक स्तर पर इनपुट के रूप में केवल ऊपर का स्तर होता है, न कि पूरा परिणाम सेट होता है।

उपरोक्त यह है कि यह तार्किक रूप से कैसे काम करता है । शारीरिक रूप से पुनरावर्ती CTEs हमेशा नेस्टेड लूप और SQL सर्वर में स्टैक स्पूल के साथ लागू होते हैं। यह यहां और यहां वर्णित है और इसका मतलब है कि व्यवहार में प्रत्येक पुनरावर्ती तत्व केवल पिछले स्तर से मूल पंक्ति के साथ काम कर रहा है, न कि पूरे स्तर पर। लेकिन पुनरावर्ती CTE में स्वीकार्य सिंटैक्स पर विभिन्न प्रतिबंधों का मतलब है कि यह दृष्टिकोण काम करता है।

यदि आप ORDER BYअपनी क्वेरी से हटाते हैं तो परिणाम निम्नानुसार दिए जाते हैं

+---------+
|    N    |
+---------+
|       3 |
|       5 |
|       7 |
|      49 |
|    2401 |
| 5764801 |
|      25 |
|     625 |
|  390625 |
|       9 |
|      81 |
|    6561 |
+---------+

ऐसा इसलिए है क्योंकि निष्पादन योजना निम्नलिखित के समान ही संचालित होती है C#

using System;
using System.Collections.Generic;
using System.Diagnostics;

public class Program
{
    private static readonly Stack<dynamic> StackSpool = new Stack<dynamic>();

    private static void Main(string[] args)
    {
        //temp table #NUMS
        var nums = new[] { 3, 5, 7 };

        //Anchor member
        foreach (var number in nums)
            AddToStackSpoolAndEmit(number, 0);

        //Recursive part
        ProcessStackSpool();

        Console.WriteLine("Finished");
        Console.ReadLine();
    }

    private static void AddToStackSpoolAndEmit(long number, int recursionLevel)
    {
        StackSpool.Push(new { N = number, RecursionLevel = recursionLevel });
        Console.WriteLine(number);
    }

    private static void ProcessStackSpool()
    {
        //recursion base case
        if (StackSpool.Count == 0)
            return;

        var row = StackSpool.Pop();

        int thisLevel = row.RecursionLevel + 1;
        long thisN = row.N * row.N;

        Debug.Assert(thisLevel <= 100, "max recursion level exceeded");

        if (thisN < 10000000)
            AddToStackSpoolAndEmit(thisN, thisLevel);

        ProcessStackSpool();
    }
}

NB1: लंगर सदस्य के पहले बच्चे के ऊपर के रूप में समय से 3अपने भाई बहन, के बारे में सभी जानकारी संसाधित किया जा रहा है 5और 7, और उनके वंशजों को पहले से ही स्पूल से निकाल दिया गया है और अब वह पहुँचा जा सकता है।

NB2: C # ऊपर निष्पादन योजना के रूप में एक ही समग्र शब्दार्थ है, लेकिन निष्पादन योजना में प्रवाह समान नहीं है, क्योंकि वहां ऑपरेटर एक पाइपलाइन किए गए परिच्छेदन फैशन में काम करते हैं। यह दृष्टिकोण का सार प्रदर्शित करने के लिए एक सरलीकृत उदाहरण है। योजना के बारे में अधिक जानकारी के लिए पहले के लिंक देखें।

NB3: स्टैक स्पूल स्वयं जाहिरा तौर पर गैर-अद्वितीय क्लस्टर इंडेक्स के रूप में कार्यान्वित किया जाता है जिसमें रिकर्सन स्तर के प्रमुख कॉलम और यूनीकफायर को आवश्यकतानुसार जोड़ा जाता है ( स्रोत )


6
SQL सर्वर में पुनरावर्ती क्वेरी को पार्स करने के दौरान पुनरावृत्ति से पुनरावृत्ति (स्टैकिंग के साथ) में हमेशा परिवर्तित किया जाता है। यात्रा के लिए कार्यान्वयन नियम है IterateToDepthFirst- Iterate(seed,rcsv)->PhysIterate(seed,rcsv)। सिर्फ आपकी जानकारी के लिए। बहुत बढ़िया जवाब।
पॉल व्हाइट का कहना है GoFundMonica

संयोग से, UNION को UNION ALL के बजाय अनुमति दी गई है लेकिन SQL सर्वर ऐसा नहीं करेगा।
जोशुआ

5

यह केवल एक (अर्ध) शिक्षित अनुमान है, और शायद पूरी तरह से गलत है। दिलचस्प सवाल है, वैसे।

टी-एसक्यूएल एक घोषणात्मक भाषा है; शायद एक पुनरावर्ती CTE को कर्सर-शैली के ऑपरेशन में अनुवादित किया जाता है, जहाँ UNION ALL के बाईं ओर से परिणाम एक अस्थायी तालिका में जोड़े जाते हैं, फिर UNION ALL के दाईं ओर बाईं ओर के मानों पर लागू किया जाता है।

तो, पहले हम परिणाम सेट में UNION ALL के बाईं ओर का आउटपुट सम्मिलित करते हैं, फिर हम बाईं ओर लागू UNION ALL के दाईं ओर के परिणाम सम्मिलित करते हैं, और परिणाम सेट में सम्मिलित करते हैं। फिर बाईं ओर को दाईं ओर से आउटपुट के साथ बदल दिया जाता है, और दाईं ओर फिर से "नया" बाईं ओर लागू किया जाता है। कुछ इस तरह:

  1. {3,5,7} -> परिणाम सेट
  2. पुनरावर्ती कथन {3,5,7} पर लागू होते हैं, जो {9,25,49} है। {9,25,49} को परिणाम सेट में जोड़ा जाता है, और UNION ALL के बाईं ओर प्रतिस्थापित किया जाता है।
  3. पुनरावर्ती कथन {9,25,49} पर लागू होते हैं, जो {81,625,2401} है। {81,625,2401} को परिणाम सेट में जोड़ा जाता है, और UNION ALL के बाईं ओर प्रतिस्थापित किया जाता है।
  4. पुनरावर्ती कथन {81,625,2401} पर लागू होते हैं, जो {6561,390625,5764801} है। {6561,390625,5764801} परिणाम सेट में जोड़ा जाता है।
  5. कर्सर पूरा हो गया है, क्योंकि अगले पुनरावृत्ति परिणाम में जहां खंड गलत है।

आप पुनरावर्ती CTE के लिए निष्पादन योजना में इस व्यवहार को देख सकते हैं:

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

यह चरण 1 से ऊपर है, जहां आउटपुट में UNION ALL के बाईं ओर जोड़ा गया है:

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

यह UNION ALL का दाईं ओर है, जहां आउटपुट परिणाम सेट पर पहुंच जाता है:

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


4

एसक्यूएल सर्वर प्रलेखन , जो उल्लेख टी मैं और टी i + 1 , न तो बहुत समझा जा सकता है, और न ही वास्तविक क्रियान्वयन का सटीक वर्णन।

मूल विचार यह है कि क्वेरी का पुनरावर्ती भाग पिछले सभी परिणामों को देखता है, लेकिन केवल एक बार

यह देखना उपयोगी हो सकता है कि अन्य डेटाबेस इसे कैसे लागू करते हैं ( समान परिणाम प्राप्त करने के लिए )। Postgres प्रलेखन का कहना है:

पुनरावर्ती प्रश्न का मूल्यांकन

  1. गैर-पुनरावर्ती शब्द का मूल्यांकन करें। के लिए UNION(लेकिन नहीं UNION ALL), डुप्लिकेट पंक्तियों को त्यागें। पुनरावर्ती क्वेरी के परिणाम में सभी शेष पंक्तियों को शामिल करें, और उन्हें एक अस्थायी कार्य तालिका में भी रखें ।
  2. इसलिए जब तक काम करने वाली मेज खाली न हो, इन चरणों को दोहराएं:
    1. पुनरावर्ती शब्द का मूल्यांकन करें, पुनरावर्ती स्व-संदर्भ के लिए कार्य तालिका की वर्तमान सामग्री को प्रतिस्थापित करें। के लिए UNION(लेकिन नहीं UNION ALL), डुप्लिकेट पंक्तियों और पंक्तियों को त्यागें जो किसी भी पिछले परिणाम पंक्ति को डुप्लिकेट करते हैं। पुनरावर्ती क्वेरी के परिणाम में सभी शेष पंक्तियों को शामिल करें, और उन्हें एक अस्थायी मध्यवर्ती तालिका में भी रखें ।
    2. मध्यवर्ती तालिका की सामग्री के साथ कार्य तालिका की सामग्री को बदलें, फिर मध्यवर्ती तालिका को खाली करें।

नोट
सख्ती से बोल रहा है, यह प्रक्रिया पुनरावृत्ति नहीं है, लेकिन RECURSIVESQL मानक समिति द्वारा चुनी गई शब्दावली है।

SQLite प्रलेखन एक अलग कार्यान्वयन पर संकेत, और यह एक पंक्ति-में-एक-समय एल्गोरिथ्म के लिए सबसे आसान समझने के लिए हो सकता है:

पुनरावर्ती तालिका की सामग्री की गणना के लिए मूल एल्गोरिथ्म इस प्रकार है:

  1. भागो initial-selectऔर एक कतार में परिणाम जोड़ें।
  2. जबकि कतार खाली नहीं है:
    1. कतार से एकल पंक्ति निकालें।
    2. पुनरावर्ती तालिका में उस एकल पंक्ति को सम्मिलित करें
    3. यह बताएं कि केवल निकाली गई एकल पंक्ति पुनरावर्ती तालिका में एकमात्र पंक्ति है और recursive-selectकतार में सभी परिणामों को जोड़ते हुए दौड़ें ।

उपरोक्त मूल प्रक्रिया निम्नलिखित अतिरिक्त नियमों द्वारा संशोधित की जा सकती है:

  • यदि कोई UNION ऑपरेटर के initial-selectसाथ जुड़ता है recursive-select, तो केवल कतार में पंक्तियों को जोड़ें यदि कतार में कोई समान पंक्ति पहले नहीं जोड़ी गई है। दोहराया पंक्तियों को कतार में जोड़े जाने से पहले ही छोड़ दिया जाता है, भले ही दोहराई गई पंक्तियों को पहले से ही पुनरावर्ती चरण द्वारा कतार से निकाला गया हो। यदि ऑपरेटर UNION ALL है, तो दोनों initial-selectऔर द्वारा उत्पन्न सभी पंक्तियों recursive-selectको हमेशा कतार में जोड़ा जाता है, भले ही वे दोहराएं।
    [...]

0

मेरा ज्ञान विशेष रूप से DB2 में है, लेकिन स्पष्टीकरण आरेखों को देखकर SQL सर्वर के साथ समान है।

योजना यहाँ से आती है:

इसे पेस्ट प्लान पर देखें

SQL सर्वर स्पष्टीकरण योजना

ऑप्टिमाइज़र प्रत्येक पुनरावर्ती क्वेरी के लिए सचमुच एक यूनियन नहीं चलाता है। यह क्वेरी की संरचना लेता है और संघ के पहले भाग को सभी "लंगर सदस्य" को सौंपता है, फिर यह संघ के दूसरे भाग के माध्यम से सभी ("पुनरावर्ती सदस्य" कहा जाता है, जब तक कि यह निर्धारित सीमाओं तक नहीं पहुंच जाता है।) पुनरावृत्ति पूरी हो गई है, अनुकूलक सभी रिकॉर्ड एक साथ जोड़ देता है।

ऑप्टिमाइज़र सिर्फ एक पूर्व-परिभाषित ऑपरेशन करने के सुझाव के रूप में लेता है।

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