सबसे पहले, आइए हम मैनुअल पेज पर दिए गए एल्गोरिथम विवरण को सरल और स्पष्ट करने का प्रयास करें। इसे सरल बनाने के लिए केवल यूनियन ऑल
. पर विचार करें पुनरावर्ती के साथ
. में अभी के लिए खंड (और संघ
बाद में):
WITH RECURSIVE pseudo-entity-name(column-names) AS (
Initial-SELECT
UNION ALL
Recursive-SELECT using pseudo-entity-name
)
Outer-SELECT using pseudo-entity-name
इसे स्पष्ट करने के लिए आइए हम छद्म कोड में क्वेरी निष्पादन प्रक्रिया का वर्णन करें:
working-recordset = result of Initial-SELECT
append working-recordset to empty outer-recordset
while( working-recordset is not empty ) begin
new working-recordset = result of Recursive-SELECT
taking previous working-recordset as pseudo-entity-name
append working-recordset to outer-recordset
end
overall-result = result of Outer-SELECT
taking outer-recordset as pseudo-entity-name
या इससे भी छोटा - डेटाबेस इंजन प्रारंभिक चयन को निष्पादित करता है, इसके परिणाम पंक्तियों को कार्यशील सेट के रूप में लेता है। फिर यह बार-बार काम करने वाले सेट पर पुनरावर्ती चयन को निष्पादित करता है, हर बार काम करने वाले सेट की सामग्री को प्राप्त क्वेरी परिणाम के साथ बदल देता है। यह प्रक्रिया तब समाप्त होती है जब पुनरावर्ती चयन द्वारा खाली सेट लौटाया जाता है। और सभी परिणाम पंक्तियों को पहले प्रारंभिक चयन द्वारा और फिर पुनरावर्ती चयन द्वारा एकत्रित किया जाता है और बाहरी चयन को खिलाया जाता है, जो परिणाम समग्र क्वेरी परिणाम बन जाता है।
यह क्वेरी फैक्टोरियल calculating की गणना कर रही है 3 का:
WITH RECURSIVE factorial(F,n) AS (
SELECT 1 F, 3 n
UNION ALL
SELECT F*n F, n-1 n from factorial where n>1
)
SELECT F from factorial where n=1
प्रारंभिक चयन 1 एफ, 3 एन चुनें
हमें प्रारंभिक मान देता है:तर्क के लिए 3 और फ़ंक्शन मान के लिए 1। बताता है कि हर बार हमें अंतिम फ़ंक्शन मान को अंतिम तर्क मान और कमी तर्क मान से गुणा करने की आवश्यकता होती है।
डेटाबेस इंजन इसे इस तरह निष्पादित करता है:
सबसे पहले यह initail चयन को निष्पादित करता है, जो कार्यशील रिकॉर्डसेट की प्रारंभिक स्थिति देता है:
F | n
--+--
1 | 3
फिर यह पुनरावर्ती क्वेरी के साथ काम कर रहे रिकॉर्डसेट को बदल देता है और इसकी दूसरी स्थिति प्राप्त करता है:
F | n
--+--
3 | 2
फिर तीसरी अवस्था:
F | n
--+--
6 | 1
तीसरे राज्य में n>1
. का अनुसरण करने वाली कोई पंक्ति नहीं है रिकर्सिव चयन में स्थिति, आगे काम करने वाला सेट लूप से बाहर निकलता है।
बाहरी रिकॉर्डसेट अब सभी पंक्तियों को रखता है, प्रारंभिक और पुनरावर्ती चयन द्वारा लौटाया जाता है:
F | n
--+--
1 | 3
3 | 2
6 | 1
बाहरी चयन बाहरी रिकॉर्डसेट से सभी मध्यवर्ती परिणामों को फ़िल्टर करता है, केवल अंतिम फ़ैक्टोरियल मान दिखाता है जो समग्र क्वेरी परिणाम बन जाता है:
F
--
6
और अब आइए तालिका forest(id,parent_id,name)
. पर विचार करें :
id | parent_id | name
---+-----------+-----------------
1 | | item 1
2 | 1 | subitem 1.1
3 | 1 | subitem 1.2
4 | 1 | subitem 1.3
5 | 3 | subsubitem 1.2.1
6 | | item 2
7 | 6 | subitem 2.1
8 | | item 3
'पूर्ण वृक्ष का विस्तार करना ' यहां पेड़ की वस्तुओं को मानव-पठनीय गहराई-पहले क्रम में क्रमबद्ध करना है, जबकि उनके स्तर और (शायद) पथों की गणना करते समय। दोनों कार्य (सही छँटाई और स्तर या पथ की गणना) एक (या किसी भी स्थिर संख्या) में हल करने योग्य नहीं हैं, बिना रिकर्सिव क्लॉज (या ओरेकल कनेक्ट बाय क्लॉज, जो पोस्टग्रेएसक्यूएल द्वारा समर्थित नहीं है) का उपयोग किए बिना चयन करें। लेकिन यह पुनरावर्ती क्वेरी काम करती है (ठीक है, लगभग करता है, नीचे नोट देखें):
WITH RECURSIVE fulltree(id,parent_id,level,name,path) AS (
SELECT id, parent_id, 1 as level, name, name||'' as path from forest where parent_id is null
UNION ALL
SELECT t.id, t.parent_id, ft.level+1 as level, t.name, ft.path||' / '||t.name as path
from forest t, fulltree ft where t.parent_id = ft.id
)
SELECT * from fulltree order by path
डेटाबेस इंजन इसे इस तरह निष्पादित करता है:
सबसे पहले, यह initail select को क्रियान्वित करता है, जो forest
से सभी उच्चतम स्तर के आइटम (जड़) देता है तालिका:
id | parent_id | level | name | path
---+-----------+-------+------------------+----------------------------------------
1 | | 1 | item 1 | item 1
8 | | 1 | item 3 | item 3
6 | | 1 | item 2 | item 2
फिर, यह पुनरावर्ती चयन को निष्पादित करता है, जो forest
. से सभी द्वितीय स्तर के आइटम देता है तालिका:
id | parent_id | level | name | path
---+-----------+-------+------------------+----------------------------------------
2 | 1 | 2 | subitem 1.1 | item 1 / subitem 1.1
3 | 1 | 2 | subitem 1.2 | item 1 / subitem 1.2
4 | 1 | 2 | subitem 1.3 | item 1 / subitem 1.3
7 | 6 | 2 | subitem 2.1 | item 2 / subitem 2.1
फिर, यह पुनरावर्ती चयन को फिर से निष्पादित करता है, 3डी स्तर की वस्तुओं को पुनः प्राप्त करता है:
id | parent_id | level | name | path
---+-----------+-------+------------------+----------------------------------------
5 | 3 | 3 | subsubitem 1.2.1 | item 1 / subitem 1.2 / subsubitem 1.2.1
और अब यह पुनरावर्ती चयन को फिर से निष्पादित करता है, चौथे स्तर की वस्तुओं को पुनः प्राप्त करने की कोशिश कर रहा है, लेकिन उनमें से कोई भी नहीं है, इसलिए लूप बाहर निकलता है।
बाहरी SELECT सही मानव-पठनीय पंक्ति क्रम सेट करता है, पथ स्तंभ पर छँटाई करता है:
id | parent_id | level | name | path
---+-----------+-------+------------------+----------------------------------------
1 | | 1 | item 1 | item 1
2 | 1 | 2 | subitem 1.1 | item 1 / subitem 1.1
3 | 1 | 2 | subitem 1.2 | item 1 / subitem 1.2
5 | 3 | 3 | subsubitem 1.2.1 | item 1 / subitem 1.2 / subsubitem 1.2.1
4 | 1 | 2 | subitem 1.3 | item 1 / subitem 1.3
6 | | 1 | item 2 | item 2
7 | 6 | 2 | subitem 2.1 | item 2 / subitem 2.1
8 | | 1 | item 3 | item 3
नोट :परिणामी पंक्ति क्रम केवल तभी सही रहेगा जब कोई विराम चिह्न वर्ण संयोजन-पूर्ववर्ती /
न हो आइटम के नाम में। अगर हम आइटम 2
. का नाम बदलते हैं आइटम 1 *
. में , यह आइटम 1
. के बीच खड़े होकर, पंक्ति क्रम को तोड़ देगा और उसके वंशज।
अधिक स्थिर समाधान टैब वर्ण का उपयोग कर रहा है (E'\t'
) क्वेरी में पथ विभाजक के रूप में (जिसे बाद में अधिक पठनीय पथ विभाजक द्वारा प्रतिस्थापित किया जा सकता है:बाहरी चयन में, मानव या आदि को विस्थापित करने से पहले)। टैब से अलग किए गए पथ सही क्रम बनाए रखेंगे जब तक कि आइटम नामों में टैब या नियंत्रण वर्ण न हों - जिन्हें आसानी से जांचा जा सकता है और प्रयोज्यता के नुकसान के बिना खारिज किया जा सकता है।
किसी भी मनमाना सबट्री का विस्तार करने के लिए अंतिम क्वेरी को संशोधित करना बहुत आसान है - आपको केवल स्थिति को प्रतिस्थापित करने की आवश्यकता है parent_id is null
perent_id=1
. के साथ (उदाहरण के लिए)। ध्यान दें कि यह क्वेरी प्रकार के सापेक्ष . सभी स्तरों और पथों को लौटाएगा आइटम 1
।
और अब सामान्य गलतियों . के बारे में . पुनरावर्ती प्रश्नों के लिए विशिष्ट सबसे उल्लेखनीय विशिष्ट गलती पुनरावर्ती चयन में बीमार स्टॉप स्थितियों को परिभाषित कर रही है, जिसके परिणामस्वरूप अनंत लूपिंग होती है।
उदाहरण के लिए, यदि हम जहां n>1
. छोड़ देते हैं उपरोक्त तथ्यात्मक नमूने में स्थिति, पुनरावर्ती चयन का निष्पादन कभी भी एक खाली सेट नहीं देगा (क्योंकि हमारे पास एकल पंक्ति को फ़िल्टर करने की कोई शर्त नहीं है) और लूपिंग अनंत रूप से जारी रहेगी।
यही सबसे संभावित कारण है कि आपके कुछ प्रश्न लटके हुए हैं (अन्य गैर-विशिष्ट लेकिन फिर भी संभावित कारण बहुत अप्रभावी चयन है, जो सीमित लेकिन बहुत लंबे समय में निष्पादित होता है)।
बहुत अधिक पुनरावर्ती-विशिष्ट क्वेरी नहीं हैं दिशा-निर्देश उल्लेख करने के लिए, जहाँ तक मुझे पता है। लेकिन मैं चरण-दर-चरण पुनरावर्ती क्वेरी निर्माण प्रक्रिया (बल्कि स्पष्ट) का सुझाव देना चाहूंगा।
-
अपना प्रारंभिक चयन अलग से बनाएं और डीबग करें।
-
इसे रिकर्सिव कंस्ट्रक्शन के साथ मचान के साथ लपेटें
और अपने पुनरावर्ती चयन को बनाना और डीबग करना शुरू करें।
अनुशंसित मचान निर्माण इस प्रकार है:
WITH RECURSIVE rec( <Your column names> ) AS (
<Your ready and working initial SELECT>
UNION ALL
<Recursive SELECT that you are debugging now>
)
SELECT * from rec limit 1000
यह सबसे सरल बाहरी चयन पूरे बाहरी रिकॉर्डसेट को आउटपुट करेगा, जैसा कि हम जानते हैं, इसमें प्रारंभिक चयन से सभी आउटपुट पंक्तियां होती हैं और पुनरावर्ती चयन का प्रत्येक निष्पादन उनके मूल आउटपुट क्रम में लूप में होता है - ठीक ऊपर के नमूने की तरह! सीमा 1000
भाग लटकने से रोकेगा, इसे बड़े आकार के आउटपुट से बदल देगा जिसमें आप छूटे हुए स्टॉप पॉइंट को देख पाएंगे।
- प्रारंभिक और पुनरावर्ती डिबगिंग के बाद अपने बाहरी चयन का निर्माण और डीबग करें।
और अब आखिरी बात का उल्लेख करना है - संघ
. का उपयोग करने में अंतर यूनियन ऑल
. के बजाय पुनरावर्ती के साथ
. में खंड। यह पंक्ति विशिष्टता बाधा का परिचय देता है जिसके परिणामस्वरूप हमारे निष्पादन छद्म कोड में दो अतिरिक्त लाइनें होती हैं:
working-recordset = result of Initial-SELECT
discard duplicate rows from working-recordset /*union-specific*/
append working-recordset to empty outer-recordset
while( working-recordset is not empty ) begin
new working-recordset = result of Recursive-SELECT
taking previous working-recordset as pseudo-entity-name
discard duplicate rows and rows that have duplicates in outer-recordset
from working-recordset /*union-specific*/
append working-recordset to outer-recordset
end
overall-result = result of Outer-SELECT
taking outer-recordset as pseudo-entity-name