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




हिंदी में डेटाबेस डिजाइन की प्रक्रिया (5)

एडजेंसी मॉडल + नेस्टेड सेट मॉडल

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

+-------------+----------------------+--------+-----+-----+
| 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 बीच उनकी lft होती है।
  • यदि आपको पेड़ की जड़ तक किसी भी नोड के सभी माता-पिता की आवश्यकता होती है, तो आप नोड के lft से कम lft वाले lft के लिए lft और नोड के lft से बड़ा lft और parent द्वारा क्रमबद्ध करते हैं।

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

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

https://code.i-harness.com

अच्छा अवलोकन

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

विकल्प

लोग मुझे पता है और सामान्य विशेषताएं:

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

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

माई एसक्यूएल

आकाशवाणी

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

PostgreSQL

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

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

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

एकाधिक वंशावली कॉलम

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

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

यहां एक उदाहरण का पालन किया गया है - पक्षियों के टैक्सोनोमिक पेड़, पदानुक्रम वर्ग / आदेश / परिवार / जीनस / प्रजातियां हैं - प्रजातियां निम्नतम स्तर है, 1 पंक्ति = 1 टैक्सन (जो पत्ती नोड्स के मामले में प्रजातियों से मेल खाती है):

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                   |

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


मैं अपने पदानुक्रमों के लिए बंद तालिकाओं के साथ 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 के साथ परिभाषित किया गया है। यह संभव है (और मैं भी सलाह) एन्सेस्टर और डिस्काउंटेंट के लिए समान मूल्य के साथ रिकॉर्ड स्टोर करने के लिए, और 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;

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

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

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


यह वास्तव में एक वर्ग peg, दौर छेद सवाल है।

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

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

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

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

class PartOf extends Edge {
};

class AdjacentTo extends Edge {
};

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

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

ट्रांजिटिव क्लोजर : उप-पेड़ पर चलें और ब्याज के क्षेत्र को जोड़ दें, उदाहरण के लिए "उप-असेंबली में कितना एल्यूमीनियम है?"

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





hierarchical-data