एक संबंधपरक डेटाबेस में पदानुक्रमित डेटा संग्रहीत करने के लिए विकल्प क्या हैं? [बन्द है]


1333

अच्छा ओवरव्यू

सामान्यतया, आप तेजी से पढ़े जाने वाले समय (उदाहरण के लिए, नेस्टेड सेट) या तेजी से लिखने के समय (आसन्न सूची) के बीच निर्णय ले रहे हैं। आमतौर पर, आप नीचे दिए गए विकल्पों के संयोजन के साथ समाप्त होते हैं जो आपकी आवश्यकताओं के लिए सबसे उपयुक्त हैं। निम्नलिखित कुछ गहराई से पढ़ने को प्रदान करता है:

विकल्प

मैं और सामान्य सुविधाओं से अवगत हूँ:

  1. आसन्न सूची :
    • कॉलम: आईडी, पेरेंटआईडी
    • लागू करने में आसान।
    • सस्ता नोड चाल, आवेषण और हटाता है।
    • स्तर, वंश और वंश को खोजने के लिए महंगा
    • डेटाबेस में कॉमन टेबल एक्सप्रेशंस के माध्यम से एन + 1 से बचें जो उनका समर्थन करते हैं
  2. नेस्टेड सेट (उर्फ संशोधित Preorder ट्री ट्रैवर्सल )
    • कॉलम: वाम, अधिकार
    • सस्ता वंश, वंशज
    • बहुत महंगी O(n/2)चालें, आवेषण, अस्थिर एन्कोडिंग के कारण हटाते हैं
  3. ब्रिज टेबल (उर्फ क्लोजर टेबल / डब्ल्यू ट्रिगर )
    • पूर्वज, वंशज, गहराई (वैकल्पिक) के साथ अलग ज्वाइन टेबल का उपयोग करता है
    • सस्ता वंश और वंश
    • O(log n)सम्मिलित करने, अद्यतन करने, हटाने के लिए लागत (सबट्री का आकार) लिखता है
    • सामान्यीकृत एन्कोडिंग: RDBMS आँकड़ों और क्वेरी प्लानर के लिए अच्छा है
    • प्रति नोड कई पंक्तियों की आवश्यकता होती है
  4. वंश स्तंभ (उर्फ भौतिक पथ , पथ गणना)
    • कॉलम: वंश (जैसे / माता-पिता / बच्चे / पोते / आदि ...)
    • उपसर्ग क्वेरी के माध्यम से सस्ते वंशज (जैसे LEFT(lineage, #) = '/enumerated/path')
    • O(log n)सम्मिलित करने, अद्यतन करने, हटाने के लिए लागत (सबट्री का आकार) लिखता है
    • गैर-संबंधपरक: ऐरे डेटैटाइप या धारावाहिक रूप से स्ट्रिंग प्रारूप पर निर्भर करता है
  5. नेस्टेड अंतराल
    • नेस्टेड सेट की तरह, लेकिन वास्तविक / फ्लोट / दशमलव के साथ ताकि एन्कोडिंग अस्थिर नहीं हो (सस्ती चाल / डालें / हटाएं)
    • वास्तविक / फ्लोट / दशमलव प्रतिनिधित्व / सटीक मुद्दे हैं
    • मैट्रिक्स एन्कोडिंग संस्करण "फ्री" के लिए पूर्वज एन्कोडिंग (भौतिक पथ) जोड़ता है, लेकिन रैखिक बीजगणित की अतिरिक्त चाल के साथ।
  6. फ्लैट टेबल
    • एक संशोधित आसन्न सूची जो प्रत्येक रिकॉर्ड में एक स्तर और रैंक (जैसे आदेश) कॉलम जोड़ती है।
    • इटरेट / पैगिनेट पर सस्ता
    • महंगी चाल और हटाना
    • अच्छा उपयोग: थ्रेडेड चर्चा - मंचों / ब्लॉग टिप्पणी
  7. एकाधिक वंश स्तंभ
    • कॉलम: प्रत्येक वंश स्तर के लिए एक, सभी माता-पिता को रूट तक संदर्भित करता है, आइटम के स्तर से नीचे स्तर पूर्ण सेट किए जाते हैं
    • सस्ते पूर्वज, वंशज, स्तर
    • सस्ता डालने, हटाने, पत्तियों की चाल
    • आंतरिक नोड्स की महंगी डालने, हटाने, चाल
    • पदानुक्रम कितना गहरा हो सकता है, इसकी कठोर सीमा

डेटाबेस विशिष्ट नोट्स

माई एसक्यूएल

आकाशवाणी

  • आसन्न सूचियों को पार करने के लिए कनेक्ट बाय का उपयोग करें

PostgreSQL

एस क्यू एल सर्वर

  • सामान्य सारांश
  • 2008 ऑफर करता है कि HierarchyId डेटा टाइप वंशावली दृष्टिकोण के साथ मदद करता है और उस गहराई का विस्तार करता है जिसका प्रतिनिधित्व किया जा सकता है।

5
के अनुसार slideshare.net/billkarwin/sql-antipatterns-strike-back पेज 77, Closure Tablesसे बेहतर हैं Adjacency List, Path Enumerationऔर Nested Setsउपयोग में आसानी के मामले में (और मैं अच्छी तरह से प्रदर्शन अनुमान लगा रहा हूँ)।
गिल्ली

मुझे यहाँ एक बहुत ही सरल संस्करण याद आता है: एक साधारण BLOB। यदि आपकी पदानुक्रम में केवल कुछ दर्जन आइटम हैं, तो आईडी का क्रमबद्ध पेड़ सबसे अच्छा विकल्प हो सकता है।
लोथर

@ लोथर: प्रश्न एक सामुदायिक विकी है इसलिए इस पर बेझिझक विचार करें। उस संबंध में मेरा विचार है कि मैं केवल उन डेटाबेसों के साथ करूँगा जो किसी प्रकार की बूँद संरचना का समर्थन करते हैं जैसे कि XML एक स्थिर क्वेरी भाषा जैसे XPATH। अन्यथा मुझे पुनः प्राप्त करने, डिसर्विलाइज़ करने और कोड में मुंज करने का एक अच्छा तरीका नहीं दिख रहा है, एसक्यूएल नहीं। और अगर आपको वास्तव में कोई समस्या है, जहाँ आपको बहुत सारे मनमाने तत्वों की ज़रूरत है, तो आप Neo4J जैसे नोड डेटाबेस का उपयोग करने से बेहतर हो सकते हैं, जिसका मैंने उपयोग किया है और पसंद किया है, यद्यपि कभी भी उत्पादन के माध्यम से नहीं लिया गया।
ऑरेंजपाइप्स 12


2
"सामान्य सारांश" के लिए MSDN लिंक अब लेख नहीं दिखाता है। यह MSDN मैगज़ीन के सितंबर 2008 के संस्करण में था, जिसे आप CHM फ़ाइल के रूप में डाउनलोड कर सकते हैं, या वेब संग्रह के माध्यम से देख सकते हैं: web.archive.org/web/20080913041559/http://msdn.microsoft.com.80/ …
ͩ

जवाबों:


66

मेरा पसंदीदा उत्तर यह है कि इस सूत्र में पहला वाक्य क्या सुझाया गया है। पदानुक्रम को बनाए रखने के लिए एक निकटता सूची का उपयोग करें और पदानुक्रम को क्वेरी करने के लिए नेस्टेड सेट का उपयोग करें।

अब तक की समस्या यह है कि एक Adjacecy List से Nested सेट्स तक कोवर्जन विधि बहुत ही धीमी है क्योंकि अधिकांश लोग रूपांतरण करने के लिए "पुश स्टैक" के रूप में ज्ञात चरम RBAR विधि का उपयोग करते हैं और इसे महंगा माना जाता है। आसन्न सूची और नेस्टेड सेट के भयानक प्रदर्शन द्वारा रखरखाव की सादगी के निर्वाण तक पहुंचने के लिए। नतीजतन, ज्यादातर लोग अंत में एक या दूसरे के लिए बसने के लिए समाप्त हो जाते हैं, खासकर अगर वहाँ से अधिक, कहते हैं, एक घटिया 100,000 नोड्स या तो। पुश स्टैक विधि का उपयोग करने के लिए एक पूरे दिन का समय लग सकता है जो कि MLM'ers एक छोटे से मिलियन नोड पदानुक्रम पर विचार करेगा।

मुझे लगा कि मैं सेल्को को प्रतियोगिता की एक बिट दे दूंगा जो कि आसन्न सूची को नेस्टेड सेट में गति में परिवर्तित करने की एक विधि है जो असंभव लगता है। यहाँ मेरे i5 लैपटॉप पर पुश स्टैक विधि का प्रदर्शन है।

Duration for     1,000 Nodes = 00:00:00:870 
Duration for    10,000 Nodes = 00:01:01:783 (70 times slower instead of just 10)
Duration for   100,000 Nodes = 00:49:59:730 (3,446 times slower instead of just 100) 
Duration for 1,000,000 Nodes = 'Didn't even try this'

और यहां नई विधि के लिए अवधि (कोष्ठक में पुश स्टैक विधि के साथ) है।

Duration for     1,000 Nodes = 00:00:00:053 (compared to 00:00:00:870)
Duration for    10,000 Nodes = 00:00:00:323 (compared to 00:01:01:783)
Duration for   100,000 Nodes = 00:00:03:867 (compared to 00:49:59:730)
Duration for 1,000,000 Nodes = 00:00:54:283 (compared to something like 2 days!!!)

हाँ, यह सही है। 1 मिलियन नोड्स एक मिनट से भी कम समय में और 100,000 नोड्स 4 सेकंड से कम में परिवर्तित हो गए।

आप नई विधि के बारे में पढ़ सकते हैं और निम्नलिखित URL पर कोड की एक प्रति प्राप्त कर सकते हैं। http://www.sqlservercentral.com/articles/Hierarchy/94040/

मैंने इसी तरह के तरीकों का उपयोग करके "पूर्व-एकत्रित" पदानुक्रम भी विकसित किया। MLM'ers और सामग्री के बिल बनाने वाले लोग इस लेख में विशेष रूप से रुचि लेंगे। http://www.sqlservercentral.com/articles/T-SQL/94570/

यदि आप किसी भी लेख पर एक नज़र डालने के लिए रुकते हैं, तो "चर्चा में शामिल हों" लिंक पर जाएं और मुझे बताएं कि आप क्या सोचते हैं।


MLMer क्या है?
डेविड मैन

एमएलएम = "मल्टी-लेवल मार्केटिंग"। एमवे, शाकेली, एसीएन, आदि, आदि
जेफ मोदीन

31

यह आपके प्रश्न का बहुत ही आंशिक उत्तर है, लेकिन मुझे आशा है कि अभी भी उपयोगी है।

Microsoft SQL Server 2008 दो सुविधाएँ लागू करता है जो पदानुक्रमित डेटा के प्रबंधन के लिए अत्यंत उपयोगी हैं:

  • HierarchyId डेटा प्रकार।
  • कीवर्ड के साथ सामान्य टेबल एक्सप्रेशन ।

प्रारंभ के लिए MSDN पर Kent Tegels द्वारा "मॉडल योर डेटा हायरार्कीज़ विद एसक्यूएल सर्वर 2008" पर एक नज़र डालें । मेरा अपना प्रश्न भी देखें: SQL Server 2008 में पुनरावर्ती एक ही तालिका क्वेरी


2
दिलचस्प है, HierarchyId, उस एक के बारे में नहीं जानता था: msdn.microsoft.com/en-us/library/bb677290.aspx
Orangepips

1
वास्तव में। मैं कई पुनरावर्ती पदानुक्रमित डेटा के साथ काम करता हूं, और मुझे सामान्य तालिका अभिव्यक्ति अत्यंत उपयोगी लगती हैं। एक परिचय के लिए msdn.microsoft.com/en-us/library/ms186243.aspx देखें ।
सीजरगॉन

28

इस डिजाइन का अभी तक उल्लेख नहीं किया गया था:

एकाधिक वंश स्तंभ

हालाँकि इसकी सीमाएँ हैं, यदि आप इन्हें सहन कर सकते हैं, तो यह बहुत ही सरल और बहुत ही कुशल है। विशेषताएं:

  • कॉलम: प्रत्येक वंश स्तर के लिए एक, मूल तक के सभी माता-पिता को संदर्भित करता है, वर्तमान वस्तुओं के स्तर से नीचे के स्तर 0 (या NULL) पर सेट होते हैं
  • पदानुक्रम कितना गहरा हो सकता है, इसकी एक निश्चित सीमा है
  • सस्ते पूर्वज, वंशज, स्तर
  • सस्ता डालने, हटाने, पत्तियों की चाल
  • आंतरिक नोड्स की महंगी डालने, हटाने, चाल

यहाँ एक उदाहरण दिया गया है - पक्षियों का कर-वृक्ष

CREATE TABLE `taxons` (
  `TaxonId` smallint(6) NOT NULL default '0',
  `ClassId` smallint(6) default NULL,
  `OrderId` smallint(6) default NULL,
  `FamilyId` smallint(6) default NULL,
  `GenusId` smallint(6) default NULL,
  `Name` varchar(150) NOT NULL default ''
);

और डेटा का उदाहरण:

+---------+---------+---------+----------+---------+-------------------------------+
| TaxonId | ClassId | OrderId | FamilyId | GenusId | Name                          |
+---------+---------+---------+----------+---------+-------------------------------+
|     254 |       0 |       0 |        0 |       0 | Aves                          |
|     255 |     254 |       0 |        0 |       0 | Gaviiformes                   |
|     256 |     254 |     255 |        0 |       0 | Gaviidae                      |
|     257 |     254 |     255 |      256 |       0 | Gavia                         |
|     258 |     254 |     255 |      256 |     257 | Gavia stellata                |
|     259 |     254 |     255 |      256 |     257 | Gavia arctica                 |
|     260 |     254 |     255 |      256 |     257 | Gavia immer                   |
|     261 |     254 |     255 |      256 |     257 | Gavia adamsii                 |
|     262 |     254 |       0 |        0 |       0 | Podicipediformes              |
|     263 |     254 |     262 |        0 |       0 | Podicipedidae                 |
|     264 |     254 |     262 |      263 |       0 | Tachybaptus                   |

यह बहुत अच्छा है क्योंकि इस तरह आप सभी आवश्यक कार्यों को बहुत आसान तरीके से पूरा करते हैं, जब तक कि आंतरिक श्रेणियां पेड़ में अपना स्तर नहीं बदलती हैं।


22

आसन्न मॉडल + नेस्टेड सेट मॉडल

मैं इसके लिए चला गया क्योंकि मैं आसानी से पेड़ में नई वस्तुओं को सम्मिलित कर सकता हूं (इसके लिए आपको एक नया आइटम सम्मिलित करने के लिए बस एक शाखा की आईडी की आवश्यकता है) और यह भी काफी तेजी से क्वेरी करता है।

+-------------+----------------------+--------+-----+-----+
| category_id | name                 | parent | lft | rgt |
+-------------+----------------------+--------+-----+-----+
|           1 | ELECTRONICS          |   NULL |   1 |  20 |
|           2 | TELEVISIONS          |      1 |   2 |   9 |
|           3 | TUBE                 |      2 |   3 |   4 |
|           4 | LCD                  |      2 |   5 |   6 |
|           5 | PLASMA               |      2 |   7 |   8 |
|           6 | PORTABLE ELECTRONICS |      1 |  10 |  19 |
|           7 | MP3 PLAYERS          |      6 |  11 |  14 |
|           8 | FLASH                |      7 |  12 |  13 |
|           9 | CD PLAYERS           |      6 |  15 |  16 |
|          10 | 2 WAY RADIOS         |      6 |  17 |  18 |
+-------------+----------------------+--------+-----+-----+
  • हर बार जब आप किसी भी माता-पिता के सभी बच्चों की आवश्यकता होती है, तो आप केवल parentकॉलम को क्वेरी करते हैं ।
  • आप किसी भी माता-पिता के सभी सन्तान की जरूरत है, तो आप आइटम उनकी है जो के लिए क्वेरी lftके बीच lftऔर rgtमाता पिता की।
  • यदि आपको पेड़ की जड़ तक किसी भी नोड के सभी माता-पिता की जरूरत है, तो आप lftनोड के मुकाबले कम मात्रा वाले आइटम के लिए क्वेरी करते हैं lftऔर नोड की rgtतुलना में बड़ा होता rgtहै parent

मुझे आवेषण की तुलना में पेड़ को तेजी से एक्सेस करने और क्वेरी करने की आवश्यकता थी, इसीलिए मैंने इसे चुना

केवल समस्या को ठीक करने के लिए है leftऔर rightजब नए आइटम डालने कॉलम। अच्छी तरह से मैंने इसके लिए एक संग्रहीत प्रक्रिया बनाई और हर बार जब मैंने एक नया आइटम डाला, जो मेरे मामले में दुर्लभ था, लेकिन यह वास्तव में तेज़ है। मुझे जो सेल्को की पुस्तक, और संग्रहीत प्रक्रिया से विचार मिला और मैं इसके साथ कैसे आया, यह डीबीए एसई https://dba.stackexchange.com/q/89051/41481 में यहाँ बताया गया है।


3
+1 यह एक कानूनी तरीका है। मेरे अपने अनुभव से कुंजी तय कर रही है कि क्या बड़े अपडेट ऑपरेशन होने पर आप गंदे रीड्स के साथ ठीक हैं। यदि नहीं, तो यह एक मामला बन जाता है या लोगों को सीधे तालिकाओं को क्वेरी करने से रोकता है और हमेशा एपीआई - डीबी स्प्रोक्स / फ़ंक्शन या कोड से गुजरता है।
ऑरेंजपाइप्स 12

1
यह एक दिलचस्प समाधान है; हालांकि, मुझे यकीन नहीं है कि बच्चों को खोजने का प्रयास करते समय माता-पिता कॉलम को वास्तव में कोई बड़ा लाभ देता है - यही कारण है कि हमारे पास बाएं और दाएं कॉलम हैं, पहली जगह में।
थॉमस

2
@ तोमास, childrenऔर के बीच अंतर है descendantsleftऔर rightवंशज को खोजने के लिए उपयोग किया जाता है।
अजराफती

14

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

विशेष रूप से Postgres के साथ आप पदानुक्रम को क्वेरी करने के लिए सेट ऑपरेटर्स का उपयोग कर सकते हैं, और GIN सूचकांकों के साथ उत्कृष्ट प्रदर्शन प्राप्त कर सकते हैं। यह माता-पिता, बच्चों और एक ही क्वेरी में बहुत तुच्छ खोज करता है। अद्यतन के रूप में अच्छी तरह से प्रबंधनीय हैं।

अगर आप जिज्ञासु हैं, तो मुझे भौतिक मार्गों के लिए सरणियों का उपयोग करने का पूरा लिखना है ।


9

यह वास्तव में एक वर्ग खूंटी, गोल छेद सवाल है।

यदि रिलेशनल डेटाबेस और SQL एकमात्र हथौड़ा है जो आपके पास है या उपयोग करने के लिए तैयार है, तो इस प्रकार अब तक पोस्ट किए गए उत्तर पर्याप्त हैं। हालांकि, पदानुक्रमित डेटा को संभालने के लिए डिज़ाइन किए गए टूल का उपयोग क्यों नहीं किया जाता है? ग्राफ डेटाबेस जटिल पदानुक्रमित डेटा के लिए आदर्श होते हैं।

किसी भी कोड / क्वेरी समाधान की जटिलताओं के साथ संबंधपरक मॉडल की अक्षमता एक संबंधपरक मॉडल पर एक ग्राफ / श्रेणीबद्ध मॉडल को मैप करने के लिए बस उस प्रयास के लायक नहीं है जब तुलना आसानी से की जाती है जिसके साथ एक ग्राफ डेटाबेस समाधान एक ही समस्या को हल कर सकता है।

एक सामान्य श्रेणीबद्ध डेटा संरचना के रूप में सामग्री के बिल पर विचार करें।

class Component extends Vertex {
    long assetId;
    long partNumber;
    long material;
    long amount;
};

class PartOf extends Edge {
};

class AdjacentTo extends Edge {
};

दो उप-विधानसभाओं के बीच सबसे छोटा रास्ता : सरल ग्राफ ट्रैवर्सल एल्गोरिथम। स्वीकार्य पथ मानदंड के आधार पर योग्य हो सकते हैं।

समानता : दो विधानसभाओं के बीच समानता की डिग्री क्या है? दो उप-वृक्षों के चौराहे और संघ की गणना करने वाले दोनों उप-पेड़ों पर एक ट्रावेल करें। इसी तरह का प्रतिशत संघ द्वारा विभाजित चौराहा है।

सकर्मक समापन : उप-पेड़ पर चलें और ब्याज के क्षेत्र (राशि) को जमा करें, जैसे "उप-विधानसभा में एल्यूमीनियम कितना है?"

हां, आप SQL और एक रिलेशनल डेटाबेस के साथ समस्या को हल कर सकते हैं। हालांकि, यदि आप नौकरी के लिए सही उपकरण का उपयोग करने के इच्छुक हैं तो बहुत बेहतर तरीके हैं।


5
यह उत्तर बेहद उपयोगी होगा यदि उपयोग के मामलों का प्रदर्शन, या बेहतर अभी तक विपरीत है, तो एक RDBMS में SQL के बजाय SPARQL के साथ ग्राफ़ डेटाबेस को क्वेरी कैसे करें।
नारंगीजप्स

1
SPARQL RDF डेटाबेस के लिए प्रासंगिक है जो ग्राफ़ डेटाबेस के बड़े डोमेन का उपवर्ग हैं। मैं InfiniteGraph के साथ काम करता हूं जो RDF डेटाबेस नहीं है और वर्तमान में SPARQL का समर्थन नहीं करता है। InfiniteGraph कई अलग-अलग क्वेरी तंत्रों का समर्थन करता है: (1) विचार, फ़िल्टर, पथ क्वालिफायर और परिणाम संचालकों की स्थापना के लिए एक ग्राफ़ नेविगेशन API, (2) एक जटिल ग्राफ़ पथ पथ मिलान भाषा और (3) Gremlin।
djhallx

6

मैं अपने पदानुक्रम के लिए क्लोजर टेबल के साथ PostgreSQL का उपयोग कर रहा हूं। मेरे पास पूरे डेटाबेस के लिए एक सार्वभौमिक संग्रहीत कार्यविधि है:

CREATE FUNCTION nomen_tree() RETURNS trigger
    LANGUAGE plpgsql
    AS $_$
DECLARE
  old_parent INTEGER;
  new_parent INTEGER;
  id_nom INTEGER;
  txt_name TEXT;
BEGIN
-- TG_ARGV[0] = name of table with entities with PARENT-CHILD relationships (TBL_ORIG)
-- TG_ARGV[1] = name of helper table with ANCESTOR, CHILD, DEPTH information (TBL_TREE)
-- TG_ARGV[2] = name of the field in TBL_ORIG which is used for the PARENT-CHILD relationship (FLD_PARENT)
    IF TG_OP = 'INSERT' THEN
    EXECUTE 'INSERT INTO ' || TG_ARGV[1] || ' (child_id,ancestor_id,depth) 
        SELECT $1.id,$1.id,0 UNION ALL
      SELECT $1.id,ancestor_id,depth+1 FROM ' || TG_ARGV[1] || ' WHERE child_id=$1.' || TG_ARGV[2] USING NEW;
    ELSE                                                           
    -- EXECUTE does not support conditional statements inside
    EXECUTE 'SELECT $1.' || TG_ARGV[2] || ',$2.' || TG_ARGV[2] INTO old_parent,new_parent USING OLD,NEW;
    IF COALESCE(old_parent,0) <> COALESCE(new_parent,0) THEN
      EXECUTE '
      -- prevent cycles in the tree
      UPDATE ' || TG_ARGV[0] || ' SET ' || TG_ARGV[2] || ' = $1.' || TG_ARGV[2]
        || ' WHERE id=$2.' || TG_ARGV[2] || ' AND EXISTS(SELECT 1 FROM '
        || TG_ARGV[1] || ' WHERE child_id=$2.' || TG_ARGV[2] || ' AND ancestor_id=$2.id);
      -- first remove edges between all old parents of node and its descendants
      DELETE FROM ' || TG_ARGV[1] || ' WHERE child_id IN
        (SELECT child_id FROM ' || TG_ARGV[1] || ' WHERE ancestor_id = $1.id)
        AND ancestor_id IN
        (SELECT ancestor_id FROM ' || TG_ARGV[1] || ' WHERE child_id = $1.id AND ancestor_id <> $1.id);
      -- then add edges for all new parents ...
      INSERT INTO ' || TG_ARGV[1] || ' (child_id,ancestor_id,depth) 
        SELECT child_id,ancestor_id,d_c+d_a FROM
        (SELECT child_id,depth AS d_c FROM ' || TG_ARGV[1] || ' WHERE ancestor_id=$2.id) AS child
        CROSS JOIN
        (SELECT ancestor_id,depth+1 AS d_a FROM ' || TG_ARGV[1] || ' WHERE child_id=$2.' 
        || TG_ARGV[2] || ') AS parent;' USING OLD, NEW;
    END IF;
  END IF;
  RETURN NULL;
END;
$_$;

फिर प्रत्येक तालिका के लिए जहां मुझे पदानुक्रम है, मैं एक ट्रिगर बनाता हूं

CREATE TRIGGER nomenclature_tree_tr AFTER INSERT OR UPDATE ON nomenclature FOR EACH ROW EXECUTE PROCEDURE nomen_tree('my_db.nomenclature', 'my_db.nom_helper', 'parent_id');

मौजूदा पदानुक्रम से एक क्लोजर टेबल को आबाद करने के लिए मैं इस संग्रहित प्रक्रिया का उपयोग करता हूं:

CREATE FUNCTION rebuild_tree(tbl_base text, tbl_closure text, fld_parent text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    EXECUTE 'TRUNCATE ' || tbl_closure || ';
    INSERT INTO ' || tbl_closure || ' (child_id,ancestor_id,depth) 
        WITH RECURSIVE tree AS
      (
        SELECT id AS child_id,id AS ancestor_id,0 AS depth FROM ' || tbl_base || '
        UNION ALL 
        SELECT t.id,ancestor_id,depth+1 FROM ' || tbl_base || ' AS t
        JOIN tree ON child_id = ' || fld_parent || '
      )
      SELECT * FROM tree;';
END;
$$;

क्लोजर टेबल को 3 कॉलम - ANCESTOR_ID, DESCENDANT_ID, DEPTH से परिभाषित किया गया है। यह संभव है (और मैं भी सलाह) ANCESTOR और DESCENDANT के लिए समान मूल्य और DEPTH के लिए शून्य का मान संग्रहीत करने के लिए। यह पदानुक्रम की पुनर्प्राप्ति के लिए प्रश्नों को सरल करेगा। और वे वास्तव में बहुत सरल हैं:

-- get all descendants
SELECT tbl_orig.*,depth FROM tbl_closure LEFT JOIN tbl_orig ON descendant_id = tbl_orig.id WHERE ancestor_id = XXX AND depth <> 0;
-- get only direct descendants
SELECT tbl_orig.* FROM tbl_closure LEFT JOIN tbl_orig ON descendant_id = tbl_orig.id WHERE ancestor_id = XXX AND depth = 1;
-- get all ancestors
SELECT tbl_orig.* FROM tbl_closure LEFT JOIN tbl_orig ON ancestor_id = tbl_orig.id WHERE descendant_id = XXX AND depth <> 0;
-- find the deepest level of children
SELECT MAX(depth) FROM tbl_closure WHERE ancestor_id = XXX;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.