2011-09-23 15 views
7

कॉलेजों के पास अपने विभागों का आयोजन करने के विभिन्न तरीके हैं। कुछ स्कूल School -> Term -> Department पर जाते हैं। दूसरों के बीच में सबसे लंबे समय तक School -> Sub_Campus -> Program -> Term -> Division -> Department है।दिलचस्प पेड़/पदानुक्रमित डेटा संरचना समस्या

School, Term, और Department केवल जो कि हमेशा एक स्कूल के विभागों के "पेड़" में मौजूद हैं। इन श्रेणियों का क्रम कभी भी नहीं बदलता है, दूसरे उदाहरण के साथ मैंने आपको सबसे लंबा बताया है। प्रत्येक चरण नीचे 1: एन संबंध है।

अब, मुझे यकीन नहीं है कि तालिकाओं के बीच संबंध कैसे स्थापित करें। उदाहरण के लिए, Term में कौन से कॉलम हैं? इसके माता-पिता Program, Sub_Campus, या School हो सकते हैं। यह कौन सा है स्कूल की प्रणाली पर निर्भर करता है। मैं उन सभी के लिए विदेशी कुंजी रखने के लिए Term तालिका सेट अप करने की कल्पना कर सकता हूं (जो सभी NULL पर डिफ़ॉल्ट होंगे), लेकिन मुझे यकीन नहीं है कि यह चीजों को करने का कैननिक तरीका है।

यह विकल्प आपके विशेष की कमी का लाभ लेता है:

+0

मुझे यकीन है कि तुम क्या कह रहे हैं नहीं कर रहा हूँ - आप के लिए एक समाधान चाहते हैं एक ही डेटाबेस कार्यान्वयन में कई अलग-अलग पदानुक्रमित डेटा मॉडल को फ़िट करने की समस्या? या एक समाधान जो दिखाता है कि एक पदानुक्रमित मॉडल को कैसे लागू किया जाए? –

+0

कोई भी, जो भी इस समस्या को बेहतर ढंग से फिट करेगा। – babonk

उत्तर

3

यहाँ एक डिजाइन संभावना है। असल में आप सामान्य पदानुक्रमों को सामान्य नोड्स पेश करके सबसे लंबे रूप के रूप में सामान्यीकृत करते हैं। यदि स्कूल में "उप परिसर" नहीं है तो बस इसे "मुख्य" नामक एक सामान्य उप परिसर असाइन करें। उदाहरण के लिए, School -> Term -> Department को School -> Sub_Campus = Main -> Program=Main -> Term -> Division=Main -> Department के समान माना जा सकता है। इस मामले में, हम "मुख्य" नामक एक नोड को डिफ़ॉल्ट रूप से असाइन करते हैं जब स्कूल में नोड्स नहीं होते हैं। अब आप इन जेनेरिक नोड्स के लिए केवल एक बूलियन ध्वज संपत्ति प्राप्त कर सकते हैं जो इंगित करता है कि वे केवल प्लेसहोल्डर्स हैं और यह ध्वज आपको मध्यम परत में या यदि आवश्यक हो तो यूएक्स में फ़िल्टर करने की अनुमति देगा।

यह डिज़ाइन आपको सामान्य रूप से सभी संबंधित बाधाओं का लाभ उठाने और आपके कोड में अनुपलब्ध नोड प्रकारों को संभालने में आसान बनाने की अनुमति देगा।

3

मेरा सुझाव है कि आप बेहतर तालिका का उपयोग करें, जिसे उदा। इकाई जिसमें आईडी फ़ील्ड और एक आत्म-संदर्भ पैरेंट फ़ील्ड होगा।

प्रत्येक प्रासंगिक तालिका में एंटीटी की आईडी (1: 1) की ओर इशारा करते हुए एक फ़ील्ड होगा। एक तरह से प्रत्येक तालिका इकाई तालिका का एक बच्चा होगा।

+0

एक _parent_ फ़ील्ड ठीक है, लेकिन इसे एक हेरार्किकल क्वेरी करने के लिए डेटाबेस-विशिष्ट समर्थन की आवश्यकता होती है (उदाहरण के लिए, ओरेकल का 'पूर्व' निर्माण से कनेक्ट)। एक वैकल्पिक दृष्टिकोण जो पूछताछ को अधिक आसान बनाता है, एक एकल एन्कोडेड कॉलम में विरासत का प्रतिनिधित्व करता है जहां एक उपट्री को उस कॉलम के स्ट्रिंग उपसर्ग के रूप में परिभाषित किया जाता है। –

+0

हाँ, लेकिन यह सवाल नहीं था, और यह जोड़ना इतना बड़ा सौदा नहीं है। मैंने हाल ही में इस लड़के के लिए इस बारे में लिखा है http://stackoverflow.com/questions/7181489/problem-with-hierarchical-database-model/7181872#7181872 – MarianP

+0

@ ईविल ओटो: गलत। डेटाबेस _model_ रिकर्सिव हो सकता है, लेकिन डेटाबेस तालिकाओं से डेटा खींचने वाला _query_ बस तब तक पुन: सक्रिय हो सकता है जब तक कि कोई और अभिभावक रिकॉर्ड नहीं मिला "। यही तरीका है कि अधिकांश क्लाइंट ऐप्स वैसे भी करेंगे। – wildplasser

1

मैं एक एकल पदानुक्रमित मॉडल (केवल 1: एन संबंध) को लागू करने पर चर्चा करके शुरू करने जा रहा हूं।

चलिए आपके उदाहरण School -> Term -> Department का उपयोग करते हैं।

कोड यह रहा है कि मैं MySQLWorkbench उपयोग करते हुए उत्पन्न (मैं इसे स्पष्ट करने के लिए कुछ चीजें हटा दिया):

-- ----------------------------------------------------- 
-- Table `mydb`.`school` 
-- ----------------------------------------------------- 
-- each of these tables would have more attributes in a real implementation 
-- using varchar(50)'s for PKs because I can -- :) 

CREATE TABLE IF NOT EXISTS `mydb`.`school` (
    `school_name` VARCHAR(50) NOT NULL , 
    PRIMARY KEY (`school_name`) 
); 

-- ----------------------------------------------------- 
-- Table `mydb`.`term` 
-- ----------------------------------------------------- 
CREATE TABLE IF NOT EXISTS `mydb`.`term` (
    `term_name` VARCHAR(50) NOT NULL , 
    `school_name` VARCHAR(50) NOT NULL , 
    PRIMARY KEY (`term_name`, `school_name`) , 
    FOREIGN KEY (`school_name`) 
    REFERENCES `mydb`.`school` (`school_name`) 
); 

-- ----------------------------------------------------- 
-- Table `mydb`.`department` 
-- ----------------------------------------------------- 
CREATE TABLE IF NOT EXISTS `mydb`.`department` (
    `dept_name` VARCHAR(50) NOT NULL , 
    `term_name` VARCHAR(50) NOT NULL , 
    `school_name` VARCHAR(50) NOT NULL , 
    PRIMARY KEY (`dept_name`, `term_name`, `school_name`) , 
    FOREIGN KEY (`term_name` , `school_name`) 
    REFERENCES `mydb`.`term` (`term_name` , `school_name`) 
); 

यहाँ डेटा मॉडल की MySQLWorkbench संस्करण है:
MySQLWorkbench version

आप कर सकते हैं के रूप में देखें, school, पदानुक्रम के शीर्ष पर, केवल school_name है इसकी कुंजी के रूप में, जबकि department में अपने सभी माता-पिता की चाबियाँ शामिल हैं जिनमें तीन भाग वाली कुंजी है।

इस समाधान के मुख्य बिंदु

  • प्राकृतिक कुंजी का उपयोग करता है - लेकिन किराए की कुंजी का उपयोग करने के लिए पुनर्संशोधित जा सकता है - हर स्तर
  • (SO question बहु-स्तंभ विदेशी कुंजी पर UNIQUE की कमी के साथ) घोंसले की कुंजी को
  • प्रत्येक तालिका का पीके इसके ऊपर की तालिका का पूरा पीके है, साथ ही उस तालिका के लिए एक अतिरिक्त कॉलम

अब आपके प्रश्न के दूसरे भाग के लिए।

सवाल
की मेरी व्याख्या एक पदानुक्रमित डेटा मॉडल नहीं है। हालांकि, कुछ अनुप्रयोगों को सभी तालिकाओं की आवश्यकता होती है, जबकि अन्य केवल कुछ तालिकाओं का उपयोग करते हैं, जो दूसरों को छोड़ देते हैं। हम 1 एकल डेटा मॉडल लागू करने और इन दोनों मामलों के लिए इसका उपयोग करने में सक्षम होना चाहते हैं।

आप ऊपर दिए गए समाधान का उपयोग कर सकते हैं, और जैसा कि शितालशह ने उल्लेख किया है, किसी भी तालिका में डिफ़ॉल्ट मान जोड़ें जिसका उपयोग नहीं किया जाएगा। चलो कुछ उदाहरण डेटा देखते हैं, मॉडल ऊपर दिए गए, जहां हम केवल School और Department जानकारी (कोई Term रों) सहेजना चाहते का उपयोग कर:

+-------------+ 
| school_name | 
+-------------+ 
| hogwarts | 
| uCollege | 
| uMatt  | 
+-------------+ 
3 rows in set (0.00 sec) 

+-----------+-------------+ 
| term_name | school_name | 
+-----------+-------------+ 
| default | hogwarts | 
| default | uCollege | 
| default | uMatt  | 
+-----------+-------------+ 
3 rows in set (0.00 sec) 

+-------------------------------+-----------+-------------+ 
| dept_name      | term_name | school_name | 
+-------------------------------+-----------+-------------+ 
| defense against the dark arts | default | hogwarts | 
| potions      | default | hogwarts | 
| basket-weaving    | default | uCollege | 
| history of magic    | default | uMatt  | 
| science      | default | uMatt  | 
+-------------------------------+-----------+-------------+ 
5 rows in set (0.00 sec) 

प्रमुख बिंदु

  • कोई डिफ़ॉल्ट मान है में school में प्रत्येक मान के लिए - यह बहुत परेशान हो सकता है यदि आपके पास पदानुक्रम में गहराई से एक टेबल है जिसे किसी एप्लिकेशन को
  • की आवश्यकता नहीं है क्योंकि तालिका स्कीमा नहीं बदलेगा , एक ही प्रश्नों इस्तेमाल किया जा सकता
  • प्रश्नों लिखने के लिए आसान और पोर्टेबल
  • कर रहे हैं ताकि सोचने के लिए default अलग ढंग से

रंग का होना चाहिए वहाँ डेटाबेस में पेड़ के भंडारण के लिए एक और उपाय है लगता है। बिल करविन ने here, starting around slide 49 पर चर्चा की, लेकिन मुझे नहीं लगता कि यह वह समाधान है जिसे आप चाहते हैं। कार्विन का समाधान किसी भी आकार के पेड़ों के लिए है, जबकि आपके उदाहरण अपेक्षाकृत स्थिर हैं। इसके अलावा, उनके समाधान समस्याओं के अपने सेट के साथ आते हैं (लेकिन सबकुछ नहीं है?)।


मुझे आशा है कि अपने प्रश्न के साथ मदद करता है।

+0

मैट, आपने उप_Campuses, प्रोग्राम्स और डिवीजनों को छोड़ दिया है? – babonk

+0

@babonk आप सही हैं; मैं अंतरिक्ष को बचाने और उदाहरण को बहुत लंबा होने से रोकना चाहता था। क्या यह बहुत अलग है कि यह स्पष्ट नहीं है कि यह ओपी के लिए कैसे मानचित्र करता है? –

1

एक संबंधपरक डेटाबेस में पदानुक्रमित डेटा फिटिंग की सामान्य समस्या के लिए, सामान्य समाधान आसन्नता सूची (आपके उदाहरण की तरह अभिभावक-बच्चे लिंक) और nested sets हैं। जैसा कि विकिपीडिया लेख में उल्लेख किया गया है, ओरेकल के ट्रोपैशको ने एक वैकल्पिक nested interval solution का प्रस्ताव दिया लेकिन यह अभी भी काफी अस्पष्ट है।

आपकी स्थिति के लिए सबसे अच्छी पसंद इस बात पर निर्भर करती है कि आप संरचना से पूछताछ कैसे करेंगे, और आप किस डीबी का उपयोग कर रहे हैं।चेरी लेख उठा:

नेस्टेड सेट उपयोग करते हुए प्रश्नों एक निकटता सूची पार करने के लिए एक संग्रहीत प्रक्रिया का उपयोग कर तेजी से प्रश्नों की तुलना में होने की उम्मीद की जा सकती है, और इसलिए डेटाबेस जो देशी पुनरावर्ती पूछताछ निर्माणों की कमी के लिए तेजी से विकल्प हैं , इस तरह के रूप में MySQL

हालांकि:

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

फिर, कैसे अपने संरचना पूछे हो जाएगा पर निर्भर करता है, तो आप एक NoSQL शैली Department तालिका denormalized, सभी संभव माता पिता के लिए nullable विदेशी कुंजी के साथ, पुनरावर्ती प्रश्नों को पूरी तरह से परहेज चुन सकते हैं।

3
-- Enforcing a taxonomy by self-referential (recursive) tables. 
-- Both the classes and the instances have a recursive structure. 
-- The taxonomy is enforced mostly based on constraints on the classes, 
-- the instances only need to check that {their_class , parents_class} 
-- form a valid pair. 
-- 
DROP schema school CASCADE; 
CREATE schema school; 

CREATE TABLE school.category 
    (id INTEGER NOT NULL PRIMARY KEY 
    , category_name VARCHAR 
); 
INSERT INTO school.category(id, category_name) VALUES 
    (1, 'School') 
    , (2, 'Sub_campus') 
    , (3, 'Program') 
    , (4, 'Term') 
    , (5, 'Division') 
    , (6, 'Department') 
    ; 

-- This table contains a list of all allowable {child->parent} pairs. 
-- As a convention, the "roots" of the trees point to themselves. 
-- (this also avoids a NULL FK) 
CREATE TABLE school.category_valid_parent 
    (category_id INTEGER NOT NULL REFERENCES school.category (id) 
    , parent_category_id INTEGER NOT NULL REFERENCES school.category (id) 
); 
ALTER TABLE school.category_valid_parent 
    ADD PRIMARY KEY (category_id, parent_category_id) 
    ; 

INSERT INTO school.category_valid_parent(category_id, parent_category_id) 
    VALUES 
    (1,1) -- school -> school 
    , (2,1) -- subcampus -> school 
    , (3,1) -- program -> school 
    , (3,2) -- program -> subcampus 
    , (4,1) -- term -> school 
    , (4,2) -- term -> subcampus 
    , (4,3) -- term -> program 
    , (5,4) -- division --> term 
    , (6,4) -- department --> term 
    , (6,5) -- department --> division 
    ; 

CREATE TABLE school.instance 
    (id INTEGER NOT NULL PRIMARY KEY 
    , category_id INTEGER NOT NULL REFERENCES school.category (id) 
    , parent_id INTEGER NOT NULL REFERENCES school.instance (id) 
    -- NOTE: parent_category_id is logically redundant 
    -- , but needed to maintain the constraint 
    -- (without referencing a third table) 
    , parent_category_id INTEGER NOT NULL REFERENCES school.category (id) 
    , instance_name VARCHAR 
);  -- Forbid illegal combinations of {parent_id, parent_category_id} 
ALTER TABLE school.instance ADD CONSTRAINT valid_cat UNIQUE (id,category_id); 
ALTER TABLE school.instance 
    ADD FOREIGN KEY (parent_id, parent_category_id) 
     REFERENCES school.instance(id, category_id); 
    ; 
    -- Forbid illegal combinations of {category_id, parent_category_id} 
ALTER TABLE school.instance 
    ADD FOREIGN KEY (category_id, parent_category_id) 
     REFERENCES school.category_valid_parent(category_id, parent_category_id); 
    ; 

INSERT INTO school.instance(id, category_id 
    , parent_id, parent_category_id 
    , instance_name) VALUES 
    -- Zulo 
    (1,1,1,1, 'University of Utrecht') 
    , (2,2,1,1, 'Uithof') 
    , (3,3,2,2, 'Life sciences') 
    , (4,4,3,3, 'Bacherlor') 
    , (5,5,4,4, 'Biology') 
    , (6,6,5,5, 'Evolutionary Biology') 
    , (7,6,5,5, 'Botany') 
    -- Nulo 
    , (11,1,11,1, 'Hogeschool Utrecht') 
    , (12,4,11,1, 'Journalistiek') 
    , (13,6,12,4, 'Begrijpend Lezen') 
    , (14,6,12,4, 'Typvaardigheid') 
    ; 

    -- try to insert an invalid instance 
INSERT INTO school.instance(id, category_id 
    , parent_id, parent_category_id 
    , instance_name) VALUES 
    (15, 6, 3,3, 'Procreation'); 

WITH RECURSIVE re AS (
    SELECT i0.parent_id AS pa_id 
    , i0.parent_category_id AS pa_cat 
    , i0.id AS my_id 
    , i0.category_id AS my_cat 
    FROM school.instance i0 
    WHERE i0.parent_id = i0.id 
    UNION 
    SELECT i1.parent_id AS pa_id 
    , i1.parent_category_id AS pa_cat 
    , i1.id AS my_id 
    , i1.category_id AS my_cat 
    FROM school.instance i1 
    , re 
    WHERE re.my_id = i1.parent_id 
) 
SELECT re.* 
    , ca.category_name 
    , ins.instance_name 
    FROM re 
    JOIN school.category ca ON (re.my_cat = ca.id) 
    JOIN school.instance ins ON (re.my_id = ins.id) 
    -- WHERE re.my_id = 14 
    ; 

उत्पादन:

INSERT 0 11 
ERROR: insert or update on table "instance" violates foreign key constraint "instance_category_id_fkey1" 
DETAIL: Key (category_id, parent_category_id)=(6, 3) is not present in table "category_valid_parent". 
pa_id | pa_cat | my_id | my_cat | category_name |  instance_name 
-------+--------+-------+--------+---------------+----------------------- 
    1 |  1 |  1 |  1 | School  | University of Utrecht 
    11 |  1 | 11 |  1 | School  | Hogeschool Utrecht 
    1 |  1 |  2 |  2 | Sub_campus | Uithof 
    11 |  1 | 12 |  4 | Term   | Journalistiek 
    2 |  2 |  3 |  3 | Program  | Life sciences 
    12 |  4 | 13 |  6 | Department | Begrijpend Lezen 
    12 |  4 | 14 |  6 | Department | Typvaardigheid 
    3 |  3 |  4 |  4 | Term   | Bacherlor 
    4 |  4 |  5 |  5 | Division  | Biology 
    5 |  5 |  6 |  6 | Department | Evolutionary Biology 
    5 |  5 |  7 |  6 | Department | Botany 
(11 rows) 

Btw: मैं विशेषताओं बाहर छोड़ दिया। मैं प्रस्ताव करता हूं कि उन्हें ईएवी प्रकार के डेटा मॉडल के माध्यम से प्रासंगिक श्रेणियों में लगाया जा सके।

+0

नोट: यह सब "बाधा न्यूनीकरण" के बारे में है। इस मामले में, बाधाएं टोपोलॉजी सुनिश्चित करती हैं, भले ही टोपोलॉजी को एक तालिका में दर्शाया गया हो (और इस प्रकार: लचीला)। अनुमत टोपोलॉजी का एक बदलाव बाधा जोड़ने या बदलने का आह्वान नहीं करता है। – wildplasser

0

मैं एक बहुत ही लचीला तरीके से इस का विकास होता है और क्या लगता है और साथ ही सबसे सरल होने के लिए मतलब करने के लिए:

सिर्फ एक ही मेज होना चाहिए, की सुविधा देता है इसे कहते category_nodes:

-- possible content, of this could be stored in another table and create a 
-- 1:N -> category:content relationship 
drop table if exists category_nodes; 
create table category_nodes (
    category_node_id int(11) default null auto_increment, 
    parent_id int(11) not null default 1, 
    name varchar(256), 
    primary key(category_node_id) 
); 
-- set the first 2 records: 
insert into category_nodes (parent_id, name) values(-1, 'root'); 
insert into category_nodes (parent_id, name) values(-1, 'uncategorized'); 

तो तालिका में प्रत्येक रिकॉर्ड में एक अद्वितीय आईडी, एक मूल आईडी, और एक नाम है।

अब पहले 2 प्रविष्टियों के बाद: श्रेणी_नोड्स में जहां श्रेणी_नोड_आईडी 0 है, रूट नोड (सभी नोड्स के माता-पिता कितने भी degres दूर हैं। दूसरा सिर्फ थोड़ा सहायक के लिए है, एक अनगिनत नोड सेट करें । category_node_id = 1 जो भी PARENT_ID की defalt मूल्य जब तालिका में डालने है

अब कल्पना जड़ श्रेणियों स्कूल, टर्म, और विभाग हैं क्या तुम करोगी:

insert into category_nodes (parent_id, name) values (0, 'School'); 
insert into category_nodes (parent_id, name) values (0, 'Term'); 
insert into category_nodes (parent_id, name) values (0, 'Dept'); 

तब सभी जड़ श्रेणियों पाने के लिए :

select * from category_nodes where parent_id = 0; 

अब एक अधिक जटिल स्कीमा की कल्पना:

-- School -> Division -> Department 
-- CatX -> CatY 
insert into category_nodes (parent_id, name) values (0, 'School'); -- imaging gets pkey = 2 
insert into category_nodes (parent_id, name) values (2, 'Division'); -- imaging gets pkey = 3 
insert into category_nodes (parent_id, name) values (3, 'Dept'); 
-- 
insert into category_nodes (parent_id, name) values (0, 'CatX'); -- 5 
insert into category_nodes (parent_id, name) values (5, 'CatY'); 

अब उदाहरण के लिए स्कूल के सभी उप-श्रेणियों पाने के लिए:

select * from category_nodes where parent_id = 2; 
-- or even 
select * from category_nodes where parent_id in (select category_node_id from category_nodes 
    where name = 'School' 
); 

और इतने पर।एक डिफ़ॉल्ट = 1 PARENT_ID साथ के लिए धन्यवाद, 'अवर्गीकृत' श्रेणी में डालने सरल हो जाते हैं:

<?php 
$name = 'New cat name'; 
mysql_query("insert into category_nodes (name) values ('$name')"); 

चीयर्स