अब जब MySQL 8.0 पुनरावर्ती प्रश्नों का समर्थन करता है , तो हम कह सकते हैं कि सभी लोकप्रिय SQL डेटाबेस मानक सिंटैक्स में पुनरावर्ती प्रश्नों का समर्थन करते हैं ।
WITH RECURSIVE MyTree AS (
SELECT * FROM MyTable WHERE ParentId IS NULL
UNION ALL
SELECT m.* FROM MyTABLE AS m JOIN MyTree AS t ON m.ParentId = t.Id
)
SELECT * FROM MyTree;
मैंने 2017 में अपनी प्रस्तुति Recursive Query Throwdown में MySQL 8.0 में पुनरावर्ती प्रश्नों का परीक्षण किया ।
नीचे 2008 से मेरा मूल उत्तर है:
रिलेशनल डेटाबेस में ट्री-स्ट्रक्चर्ड डेटा को स्टोर करने के कई तरीके हैं। आप अपने उदाहरण में जो दिखाते हैं वह दो विधियों का उपयोग करता है:
- निकटता सूची ("पैरेंट" कॉलम) और
- पथ गणना (आपके नाम कॉलम में बिंदीदार संख्याएँ)।
एक अन्य समाधान को नेस्टेड सेट कहा जाता है , और इसे उसी तालिका में भी संग्रहीत किया जा सकता है। इन डिजाइनों के बारे में और अधिक जानकारी के लिए जो सेल्को द्वारा " ट्रीज़ एंड हायरार्कीज़ इन एसक्यूएल फॉर स्मार्टिज़ " पढ़ें ।
मैं आमतौर पर ट्री-स्ट्रक्चर्ड डेटा को स्टोर करने के लिए क्लोजर टेबल (उर्फ "एडजेंसी रिलेशन") नामक डिज़ाइन पसंद करता हूं । इसके लिए एक और तालिका की आवश्यकता होती है, लेकिन फिर पेड़ों की क्वेरी करना बहुत आसान है।
मैं अपनी प्रस्तुति में क्लोजर टेबल को कवर करता हूं मॉडल में पदानुक्रमित डेटा के लिए SQL और PHP के साथ और मेरी पुस्तक SQL Antipatterns में: डेटाबेस प्रोग्रामिंग के नुकसान से बचना ।
CREATE TABLE ClosureTable (
ancestor_id INT NOT NULL REFERENCES FlatTable(id),
descendant_id INT NOT NULL REFERENCES FlatTable(id),
PRIMARY KEY (ancestor_id, descendant_id)
);
क्लोजर टेबल में सभी रास्तों को स्टोर करें, जहाँ एक नोड से दूसरे में सीधा वंश होता है। प्रत्येक नोड के लिए खुद को संदर्भित करने के लिए एक पंक्ति शामिल करें। उदाहरण के लिए, आपके द्वारा अपने प्रश्न में दिखाए गए डेटा सेट का उपयोग करना:
INSERT INTO ClosureTable (ancestor_id, descendant_id) VALUES
(1,1), (1,2), (1,4), (1,6),
(2,2), (2,4),
(3,3), (3,5),
(4,4),
(5,5),
(6,6);
अब आप इस तरह से नोड 1 पर एक पेड़ प्राप्त कर सकते हैं:
SELECT f.*
FROM FlatTable f
JOIN ClosureTable a ON (f.id = a.descendant_id)
WHERE a.ancestor_id = 1;
आउटपुट (MySQL क्लाइंट में) निम्न की तरह दिखता है:
+----+
| id |
+----+
| 1 |
| 2 |
| 4 |
| 6 |
+----+
दूसरे शब्दों में, नोड 3 और 5 को बाहर रखा गया है, क्योंकि वे एक अलग पदानुक्रम का हिस्सा हैं, नोड 1 से नहीं उतरते।
पुन: तत्काल बच्चों (या तत्काल माता-पिता) के बारे में ई-सतियों से टिप्पणी करें। आप विशेष रूप से तत्काल बच्चे या माता-पिता (या किसी अन्य दूरी) के लिए क्वेरी करना आसान बनाने path_length
के ClosureTable
लिए " " कॉलम जोड़ सकते हैं ।
INSERT INTO ClosureTable (ancestor_id, descendant_id, path_length) VALUES
(1,1,0), (1,2,1), (1,4,2), (1,6,1),
(2,2,0), (2,4,1),
(3,3,0), (3,5,1),
(4,4,0),
(5,5,0),
(6,6,0);
फिर आप दिए गए नोड के तत्काल बच्चों को क्वेरी करने के लिए अपनी खोज में एक शब्द जोड़ सकते हैं। ये वंशज हैं जिनकी path_length
संख्या 1 है।
SELECT f.*
FROM FlatTable f
JOIN ClosureTable a ON (f.id = a.descendant_id)
WHERE a.ancestor_id = 1
AND path_length = 1;
+----+
| id |
+----+
| 2 |
| 6 |
+----+
@ अशरफ से टिप्पणी: "कैसे पूरे पेड़ को छाँटने के बारे में [नाम से]?"
यहां नोड 1 के वंशज हैं सभी नोड्स को वापस करने के लिए एक उदाहरण क्वेरी है, उन्हें फ्लैटटेबल में शामिल करें जिसमें अन्य नोड विशेषताएँ शामिल हैं जैसे कि name
, और नाम के आधार पर छाँटें।
SELECT f.name
FROM FlatTable f
JOIN ClosureTable a ON (f.id = a.descendant_id)
WHERE a.ancestor_id = 1
ORDER BY f.name;
@ टिप्पणी से पुन: टिप्पणी करें:
SELECT f.name, GROUP_CONCAT(b.ancestor_id order by b.path_length desc) AS breadcrumbs
FROM FlatTable f
JOIN ClosureTable a ON (f.id = a.descendant_id)
JOIN ClosureTable b ON (b.descendant_id = a.descendant_id)
WHERE a.ancestor_id = 1
GROUP BY a.descendant_id
ORDER BY f.name
+------------+-------------+
| name | breadcrumbs |
+------------+-------------+
| Node 1 | 1 |
| Node 1.1 | 1,2 |
| Node 1.1.1 | 1,2,4 |
| Node 1.2 | 1,6 |
+------------+-------------+
एक उपयोगकर्ता ने आज एक संपादन का सुझाव दिया। एसओ मध्यस्थों ने संपादन को मंजूरी दे दी, लेकिन मैं इसे उलट रहा हूं।
संपादन ने सुझाव दिया कि ऊपर दिए गए अंतिम प्रश्न में ORDER BY होना चाहिए ORDER BY b.path_length, f.name
, संभवतः यह सुनिश्चित करने के लिए कि आदेश पदानुक्रम से मेल खाता है। लेकिन यह काम नहीं करता है, क्योंकि यह "नोड 1.2" के बाद "नोड 1.1.1" का आदेश देगा।
यदि आप पदानुक्रम को एक समझदार तरीके से मिलान करने के लिए आदेश चाहते हैं, तो यह संभव है, लेकिन केवल पथ की लंबाई के अनुसार आदेश देकर नहीं। उदाहरण के लिए, MySQL क्लोजर टेबल पदानुक्रमित डेटाबेस के लिए मेरा जवाब देखें - सही क्रम में जानकारी कैसे खींचनी है ।