2012-12-28 24 views
10

मैं आपको बंद करने की तालिका के रूप में संग्रहीत पदानुक्रमित डेटा संरचना को सॉर्ट करने में समस्या के साथ मेरी सहायता करने के लिए कहूंगा।एक बंदरगाह तालिका में एक उपखंड को क्रमबद्ध करना पदानुक्रमित-डेटा संरचना

मैं अपनी वेबसाइट मेनू को स्टोर करने के लिए इस संरचना का उपयोग करना चाहता था। सबकुछ ठीक काम करता है, लेकिन समस्या यह है कि मुझे कस्टम ऑर्डर में सटीक उपट्री को सॉर्ट करने का तरीका नहीं पता है। फिलहाल पेड़ को क्रम में क्रमबद्ध किया गया है जिसमें आइटम डेटाबेस में जोड़े गए थे।

मेरी संरचना क्लोजर टेबल्स और कुछ अन्य पोस्ट के बारे में Bill Karwin's article पर आधारित है। साथ __ROOT_ =

SELECT c2.*, cc2.ancestor AS `_parent` 
FROM category AS c1 
JOIN category_closure AS cc1 ON (cc1.ancestor = c1.id) 
JOIN category AS c2 ON (cc1.descendant = c2.id) 
LEFT OUTER JOIN category_closure AS cc2 ON (cc2.descendant = c2.id AND cc2.depth = 1) 
WHERE c1.id = __ROOT__ AND c1.active = 1 
ORDER BY cc1.depth 

डेमो उदाहरण के लिए:

-- 
-- Table `category` 
-- 

CREATE TABLE IF NOT EXISTS `category` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(100) COLLATE utf8_czech_ci NOT NULL, 
    `active` tinyint(1) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB; 


INSERT INTO `category` (`id`, `name`, `active`) VALUES 
(1, 'Cat 1', 1), 
(2, 'Cat 2', 1), 
(3, 'Cat 1.1', 1), 
(4, 'Cat 1.1.1', 1), 
(5, 'Cat 2.1', 1), 
(6, 'Cat 1.2', 1), 
(7, 'Cat 1.1.2', 1); 

-- 
-- Table `category_closure` 
-- 

CREATE TABLE IF NOT EXISTS `category_closure` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT, 
    `ancestor` int(11) DEFAULT NULL, 
    `descendant` int(11) DEFAULT NULL, 
    `depth` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `fk_category_closure_ancestor_category_id` (`ancestor`), 
    KEY `fk_category_closure_descendant_category_id` (`descendant`) 
) ENGINE=InnoDB; 

INSERT INTO `category_closure` (`id`, `ancestor`, `descendant`, `depth`) VALUES 
(1, 1, 1, 0), 
(2, 2, 2, 0), 
(3, 3, 3, 0), 
(4, 1, 3, 1), 
(5, 4, 4, 0), 
(7, 3, 4, 1), 
(8, 1, 4, 2), 
(10, 6, 6, 0), 
(11, 1, 6, 1), 
(12, 7, 7, 0), 
(13, 3, 7, 1), 
(14, 1, 7, 2), 
(16, 5, 5, 0), 
(17, 2, 5, 1); 

यहाँ एक पेड़ के लिए अपने चयन करें क्वेरी है:

यहाँ कुछ डेमो डेटा के साथ अपने MySQL डेटाबेस संरचना है 1 वह क्वेरी प्राप्त होती है:

id name  active  _parent 
1 Cat 1  1   NULL 
3 Cat 1.1  1   1 
6 Cat 1.2  1   1 
4 Cat 1.1.1 1   3 
7 Cat 1.1.2 1   3 

लेकिन अगर मुझे उदाहरण के लिए बिल्ली 1.1 और बिल्ली 1.2 (नाम के अनुसार, या कुछ कस्टम ऑर्डर) के क्रम को बदलने की आवश्यकता है तो क्या होगा?

मैंने कुछ ब्रेडक्रंब समाधान (ब्रेडक्रंब द्वारा क्रमबद्ध करने के लिए) देखा है, लेकिन मुझे नहीं पता कि उन्हें कैसे उत्पन्न और परिवर्तित करना है।

+0

+1 नमूना डीडीएल और डेटा पोस्ट करने के लिए धन्यवाद। –

उत्तर

13

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

क्लोजर टेबल के लिए जिस समाधान के साथ आया है, इसमें एक अतिरिक्त शामिल होना शामिल है। पेड़ में प्रत्येक नोड अपने पूर्वजों की श्रृंखला में शामिल होता है, जैसे "ब्रेडक्रंब" प्रकार की क्वेरी। फिर एक अल्पविराम से अलग स्ट्रिंग में ब्रेडक्रंब को पतन करने के लिए GROUP_CONCAT() का उपयोग करें, आईडी संख्याओं को पेड़ में गहराई से सॉर्ट करें। अब आपके पास एक स्ट्रिंग है जिसके द्वारा आप सॉर्ट कर सकते हैं।

SELECT c2.*, cc2.ancestor AS `_parent`, 
    GROUP_CONCAT(breadcrumb.ancestor ORDER BY breadcrumb.depth DESC) AS breadcrumbs 
FROM category AS c1 
JOIN category_closure AS cc1 ON (cc1.ancestor = c1.id) 
JOIN category AS c2 ON (cc1.descendant = c2.id) 
LEFT OUTER JOIN category_closure AS cc2 ON (cc2.descendant = c2.id AND cc2.depth = 1) 
JOIN category_closure AS breadcrumb ON (cc1.descendant = breadcrumb.descendant) 
WHERE c1.id = 1/*__ROOT__*/ AND c1.active = 1 
GROUP BY cc1.descendant 
ORDER BY breadcrumbs; 

+----+------------+--------+---------+-------------+ 
| id | name  | active | _parent | breadcrumbs | 
+----+------------+--------+---------+-------------+ 
| 1 | Cat 1  |  1 | NULL | 1   | 
| 3 | Cat 1.1 |  1 |  1 | 1,3   | 
| 4 | Cat 1.1.1 |  1 |  3 | 1,3,4  | 
| 7 | Cat 1.1.2 |  1 |  3 | 1,3,7  | 
| 6 | Cat 1.2 |  1 |  1 | 1,6   | 
+----+------------+--------+---------+-------------+ 

चेतावनियां:

  • आईडी मूल्यों, वर्दी लंबाई होना चाहिए क्योंकि "1,3" छंटाई और "1,6" और "1,327" आपको इरादा नहीं दे सकता है। लेकिन "001,003" और "001,006" और "001,327" सॉर्ट करना होगा। तो आपको या तो 1000000+ पर अपने आईडी मानों को शुरू करने की आवश्यकता है, या फिर श्रेणी_closure तालिका में पूर्वजों और वंश के लिए ZEROFILL का उपयोग करें।
  • इस समाधान में डिस्प्ले ऑर्डर श्रेणी आईडी के संख्यात्मक क्रम पर निर्भर करता है। आईडी मानों का वह संख्यात्मक क्रम उस क्रम का प्रतिनिधित्व नहीं कर सकता है जिसे आप पेड़ प्रदर्शित करना चाहते हैं। या आप संख्यात्मक आईडी मानों के बावजूद प्रदर्शन आदेश को बदलने की स्वतंत्रता चाहते हैं। या आप एक ही श्रेणी के डेटा को एक से अधिक पेड़ में दिखाना चाहते हैं, प्रत्येक अलग प्रदर्शन आदेश के साथ।
    यदि आपको अधिक स्वतंत्रता की आवश्यकता है, तो आपको आईडी से अलग क्रमबद्ध क्रम मूल्यों को स्टोर करने की आवश्यकता है, और समाधान और भी जटिल हो जाता है। लेकिन ज्यादातर परियोजनाओं में, शॉर्ट-कट का उपयोग करना स्वीकार्य है, जिससे श्रेणी आईडी के डबल ड्यूटी को वृक्ष प्रदर्शन आदेश के रूप में दिया जाता है।

अपनी टिप्पणी पुन:

हाँ, आप बंद तालिका में एक और स्तंभ के रूप में "भाई सॉर्ट क्रम" संग्रहीत कर सकती है, तो ancestor के बजाय उस मान का उपयोग ब्रेडक्रंब स्ट्रिंग बनाने के लिए। लेकिन यदि आप ऐसा करते हैं, तो आप बहुत सारे डेटा रिडंडेंसी के साथ समाप्त होते हैं। यही है, एक दिया गया पूर्वज कई पंक्तियों पर संग्रहीत होता है, प्रत्येक से निकलने वाले प्रत्येक पथ के लिए। तो आपको उन सभी पंक्तियों पर भाई क्रमबद्ध क्रम के लिए एक ही मूल्य स्टोर करना होगा, जो एक विसंगति का खतरा पैदा करता है।

विकल्प पेड़ में प्रति विशिष्ट पूर्वजों के लिए केवल एक पंक्ति के साथ एक और तालिका बनाना होगा, और भाई के आदेश को प्राप्त करने के लिए उस तालिका में शामिल होना होगा।

CREATE TABLE category_closure_order (
    ancestor INT PRIMARY KEY, 
    sibling_order SMALLINT UNSIGNED NOT NULL DEFAULT 1 
); 

SELECT c2.*, cc2.ancestor AS `_parent`, 
    GROUP_CONCAT(o.sibling_order ORDER BY breadcrumb.depth DESC) AS breadcrumbs 
FROM category AS c1 
JOIN category_closure AS cc1 ON (cc1.ancestor = c1.id) 
JOIN category AS c2 ON (cc1.descendant = c2.id) 
LEFT OUTER JOIN category_closure AS cc2 ON (cc2.descendant = c2.id AND cc2.depth = 1) 
JOIN category_closure AS breadcrumb ON (cc1.descendant = breadcrumb.descendant) 
JOIN category_closure_order AS o ON breadcrumb.ancestor = o.ancestor 
WHERE c1.id = 1/*__ROOT__*/ AND c1.active = 1 
GROUP BY cc1.descendant 
ORDER BY breadcrumbs; 

+----+------------+--------+---------+-------------+ 
| id | name  | active | _parent | breadcrumbs | 
+----+------------+--------+---------+-------------+ 
| 1 | Cat 1  |  1 | NULL | 1   | 
| 3 | Cat 1.1 |  1 |  1 | 1,1   | 
| 4 | Cat 1.1.1 |  1 |  3 | 1,1,1  | 
| 7 | Cat 1.1.2 |  1 |  3 | 1,1,2  | 
| 6 | Cat 1.2 |  1 |  1 | 1,2   | 
+----+------------+--------+---------+-------------+ 
+0

बहुत धन्यवाद श्री करविन, इसलिए जब मुझे पेड़ के कुछ स्तर पर अधिक स्वतंत्रता की आवश्यकता होती है और वहां एक कस्टम ऑर्डर बनाते हैं (मेनू आइटमों का ऑर्डर सेट करने के लिए जो समान माता-पिता होते हैं), तो मैं " स्तर प्राथमिकता "कॉलम या यह एक बुरा विचार है? – JCZ

+0

बहुत धन्यवाद श्रीमान कार्विन, मेरे पास (उम्मीद है कि सफलतापूर्वक) एक तीसरी तालिका के साथ विकल्प लागू किया है। तुम महान हो। – JCZ

+0

उत्सुक, श्रेणी तालिका में सॉर्ट ऑर्डर क्यों स्टोर न करें, इस तरह कोई तीसरी तालिका आवश्यक नहीं है? जब मैंने पैतृक पेड़ में छंटनी की है, तो मैंने हमेशा एक डबल का उपयोग किया है, ताकि जब नोड्स 1 और 2 के बीच एक नया नोड डाला जाना चाहिए, तो मैं इसे क्रमबद्ध कर सकता हूं वैल्यू 1.5 – TheSmileyCoder