कैसे एक MySQL पदानुक्रम पुनरावर्ती क्वेरी बनाने के लिए


271

मेरे पास एक MySQL टेबल है जो इस प्रकार है:

id | name        | parent_id
19 | category1   | 0
20 | category2   | 19
21 | category3   | 20
22 | category4   | 21
......

अब, मैं एक एकल MySQL क्वेरी चाहता हूं, जिसमें मैं केवल आईडी [उदाहरण के लिए 'आईडी = 19' कहता हूं] फिर मुझे इसकी सभी बच्चे आईडी मिलनी चाहिए [अर्थात परिणाम में '20, 21,22 'होना चाहिए]। ... इसके अलावा, बच्चों का पदानुक्रम ज्ञात नहीं है कि यह अलग-अलग हो सकता है ...।

इसके अलावा, मेरे पास पहले से ही लूप का उपयोग करने का समाधान है ..... मुझे बताएं कि यदि संभव हो तो एक सिंगल MySQL क्वेरी का उपयोग करके समान कैसे प्राप्त करें।


मान लीजिए कि पदानुक्रम 7 स्तर गहरा है। आप आउटपुट तालिका से क्या उम्मीद करते हैं?
जोनाथन लेफ़लर

1
MySQL (अभी भी) पदानुक्रमित प्रश्नों का समर्थन नहीं करता (जैसा कि अन्य आधुनिक DBMS करते हैं)। आपको एक संग्रहीत प्रक्रिया लिखने या एक अलग डेटामॉडल का उपयोग करने की आवश्यकता होगी।
a_horse_with_no_name


1
MYSQL 8.0 CTE (कॉमन टेबल एक्सप्रेशंस) का उपयोग करके पुनरावर्ती क्वेरी का समर्थन करेगा
user3712320

अंतिम टिप्पणी आईडी से शुरू होने वाले पदों की पूरी सूची प्राप्त करने के बारे में क्या? या आखिरी बच्चा?
जो

जवाबों:


392

के लिए MySQL 8: पुनरावर्ती का उपयोग withवाक्य रचना।
के लिए MySQL 5.x: उपयोग इनलाइन चर, पथ आईडी, या स्वयं जुड़ जाता है।

MySQL 8+

with recursive cte (id, name, parent_id) as (
  select     id,
             name,
             parent_id
  from       products
  where      parent_id = 19
  union all
  select     p.id,
             p.name,
             p.parent_id
  from       products p
  inner join cte
          on p.parent_id = cte.id
)
select * from cte;

उस मान को निर्दिष्ट किया parent_id = 19जाना चाहिए जिसे idआप उस मूल माता-पिता के लिए सेट करना चाहते हैं जिसके सभी वंशज का चयन करना चाहते हैं।

MySQL 5.x

MySQL संस्करणों के लिए जो कॉमन टेबल एक्सप्रेशंस (संस्करण 5.7 तक) का समर्थन नहीं करते हैं, आप निम्न क्वेरी के साथ इसे प्राप्त करेंगे:

select  id,
        name,
        parent_id 
from    (select * from products
         order by parent_id, id) products_sorted,
        (select @pv := '19') initialisation
where   find_in_set(parent_id, @pv)
and     length(@pv := concat(@pv, ',', id))

यहाँ एक बेला है

यहां, उस मान को निर्दिष्ट किया @pv := '19'जाना चाहिए idजिसमें उस मूल माता-पिता के लिए सेट होना चाहिए जिसे आप सभी के वंशजों का चयन करना चाहते हैं।

यह तब भी काम करेगा जब एक अभिभावक के कई बच्चे होंगे। हालांकि, यह आवश्यक है कि प्रत्येक रिकॉर्ड शर्त को पूरा करे parent_id < id, अन्यथा परिणाम पूर्ण नहीं होंगे।

एक क्वेरी के अंदर परिवर्तनीय कार्य

यह क्वेरी विशिष्ट MySQL सिंटैक्स का उपयोग करती है: चर इसके निष्पादन के दौरान निर्दिष्ट और संशोधित किए जाते हैं। निष्पादन के आदेश के बारे में कुछ धारणाएँ बनाई गई हैं:

  • fromखंड प्रथम मूल्यांकन किया जाता है। तो यह वह जगह है जहाँ @pvसे आरंभ होता है।
  • whereखंड से पुनः प्राप्ति के क्रम में प्रत्येक रिकॉर्ड के लिए मूल्यांकन किया जाता है fromउपनाम। तो यह वह जगह है जहां केवल उन अभिलेखों को शामिल किया जाता है जिनके लिए माता-पिता को पहले से ही वंशज वृक्ष के रूप में पहचाना जाता था (प्राथमिक माता-पिता के सभी वंशज उत्तरोत्तर जोड़े जाते हैं @pv)।
  • इस whereखंड में शर्तों का मूल्यांकन क्रम में किया जाता है, और कुल परिणाम निश्चित होने के बाद मूल्यांकन बाधित होता है। इसलिए दूसरी स्थिति दूसरे स्थान पर होनी चाहिए, क्योंकि यह idमूल सूची में जुड़ जाती है, और यह केवल तभी होना चाहिए idजब पहली शर्त पास हो जाए । lengthसमारोह केवल बनाने के लिए कहा जाता है यकीन है कि इस हालत हमेशा की तरह, सही है भले ही pvस्ट्रिंग किसी कारण के लिए एक falsy मूल्य प्राप्त करेगी।

सभी के सभी, इन मान्यताओं पर भरोसा करने के लिए बहुत जोखिम भरा हो सकता है। प्रलेखन चेतावनी देते हैं:

आपको वे परिणाम मिल सकते हैं जिनकी आप अपेक्षा करते हैं, लेकिन यह गारंटी नहीं है [...] उपयोगकर्ता चर शामिल अभिव्यक्तियों के लिए मूल्यांकन का क्रम अपरिभाषित है।

इसलिए भले ही यह उपरोक्त क्वेरी के साथ लगातार काम करता हो, मूल्यांकन आदेश अभी भी बदल सकता है, उदाहरण के लिए, जब आप शर्तों को जोड़ते हैं या इस क्वेरी को एक बड़े क्वेरी में दृश्य या उप-क्वेरी के रूप में उपयोग करते हैं। यह एक "फीचर" है जिसे भविष्य के MySQL रिलीज में हटा दिया जाएगा :

MySQL के पिछले रिलीज ने इसके अलावा अन्य बयानों में एक उपयोगकर्ता चर के लिए एक मूल्य प्रदान करना संभव बना दिया SET। यह कार्यक्षमता बैकवर्ड संगतता के लिए MySQL 8.0 में समर्थित है, लेकिन MySQL के भविष्य के रिलीज में हटाने के अधीन है।

जैसा कि ऊपर कहा गया है, MySQL 8.0 पर आपको पुनरावर्ती withसिंटैक्स का उपयोग करना चाहिए ।

दक्षता

बहुत बड़े डेटा सेटों के लिए यह समाधान धीमा हो सकता है, क्योंकि find_in_setऑपरेशन किसी सूची में नंबर खोजने के लिए सबसे आदर्श तरीका नहीं है, निश्चित रूप से उस सूची में नहीं जो परिमाण के समान क्रम में एक आकार तक पहुंचता है क्योंकि रिकॉर्ड की संख्या वापस आ गई।

विकल्प 1: with recursive,connect by

अधिक से अधिक डेटाबेस SQL कोWITH [RECURSIVE] लागू करते हैं : पुनरावर्ती प्रश्नों के लिए 1999 आईएसओ मानक वाक्यविन्यास (जैसे Postgres 8.4+ , SQL Server 2005+ , DB2 , Oracle 11gR2 + , SQLite 3.8.4+ , Firebird 2.1+ , H2 , HyperSQL 2.1.0+ , Teradata) , मारियाबीडी 10.2.2+ )। और संस्करण 8.0 के रूप में , MySQL भी इसका समर्थन करता है । सिंटैक्स का उपयोग करने के लिए इस उत्तर के शीर्ष पर देखें।

कुछ डेटाबेस में पदानुक्रमिक लुक-अप के लिए एक वैकल्पिक, गैर-मानक वाक्यविन्यास है, जैसे कि Oracle , DB2 , Informix , CUBRID और अन्य डेटाबेस CONNECT BYपर उपलब्ध खंड ।

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

वैकल्पिक 2: पथ-शैली पहचानकर्ता

चीजें बहुत आसान हो जाती हैं यदि आप उन idमानों को असाइन करेंगे जिनमें पदानुक्रमित जानकारी है: एक पथ। उदाहरण के लिए, आपके मामले में यह इस तरह दिख सकता है:

ID       | NAME
19       | category1   
19/1     | category2  
19/1/1   | category3  
19/1/1/1 | category4  

तब आपका मन selectऐसा लगेगा:

select  id,
        name 
from    products
where   id like '19/%'

वैकल्पिक 3: बार-बार स्व-जुड़ना

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

select      p6.parent_id as parent6_id,
            p5.parent_id as parent5_id,
            p4.parent_id as parent4_id,
            p3.parent_id as parent3_id,
            p2.parent_id as parent2_id,
            p1.parent_id as parent_id,
            p1.id as product_id,
            p1.name
from        products p1
left join   products p2 on p2.id = p1.parent_id 
left join   products p3 on p3.id = p2.parent_id 
left join   products p4 on p4.id = p3.parent_id  
left join   products p5 on p5.id = p4.parent_id  
left join   products p6 on p6.id = p5.parent_id
where       19 in (p1.parent_id, 
                   p2.parent_id, 
                   p3.parent_id, 
                   p4.parent_id, 
                   p5.parent_id, 
                   p6.parent_id) 
order       by 1, 2, 3, 4, 5, 6, 7;

देखिए यह फील

यह whereशर्त निर्दिष्ट करती है कि आप किस माता-पिता के वंशज को पुनः प्राप्त करना चाहते हैं। आप आवश्यकतानुसार इस स्तर को और बढ़ा सकते हैं।


26
मुझे आपका स्पष्टीकरण पसंद है। यह सिर्फ एक जवाब नहीं देता है, यह बताता है कि यह समस्या क्यों हल करता है इसलिए हम वास्तव में इससे सीख सकते हैं। संपादित करें: यह भी बहुत अच्छा है कि यह पहले से स्तरों की संख्या जानने पर भरोसा नहीं करता है।
बाइसन

1
@ बाइसन, मैं आपकी फीड-बैक की बहुत सराहना करता हूं। धन्यवाद!
त्रिनकोट

2
@ एवियॉन, यह कुछ ऐसा नहीं है जिसे आपको कहीं रखना है, यह एक आवश्यकता है कि सभी रिकॉर्ड के लिए यह शर्त सही है। यदि आपके पास एक या एक से अधिक रिकॉर्ड हैं parent_id > idतो आप इस समाधान का उपयोग नहीं कर सकते हैं।
त्रिनोट

2
किसी को भी WITH RECURSIVEविधि का उपयोग करने की तलाश में , मैं निम्नलिखित लेख को वास्तव में विभिन्न परिदृश्यों जैसे कि पुनरावृत्ति की गहराई, भिन्नता, और चक्रों का पता लगाने और बंद करने में मददगार पाया
हॉर्स

2
मैंने अपने कंप्यूटर पर MySQL5.7 पर मुख्य समाधान की कोशिश की, अपने स्वयं के तालिकाओं पर, लेकिन उस खंड के बराबर काम नहीं किया @pv: = concat (@pv, ',', id) गलत का मूल्यांकन करता है। मैंने इसे लंबाई (@pv: = concat (@pv, ',', id))> 0 में बदलकर तय किया है, इसलिए यह हमेशा सत्य है।
के.सी. वोंग

82

MySQL में Hierarchical Data को प्रबंधित करने वाले ब्लॉग से

तालिका संरचना

+-------------+----------------------+--------+
| category_id | name                 | parent |
+-------------+----------------------+--------+
|           1 | ELECTRONICS          |   NULL |
|           2 | TELEVISIONS          |      1 |
|           3 | TUBE                 |      2 |
|           4 | LCD                  |      2 |
|           5 | PLASMA               |      2 |
|           6 | PORTABLE ELECTRONICS |      1 |
|           7 | MP3 PLAYERS          |      6 |
|           8 | FLASH                |      7 |
|           9 | CD PLAYERS           |      6 |
|          10 | 2 WAY RADIOS         |      6 |
+-------------+----------------------+--------+

प्रश्न:

SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS';

उत्पादन

+-------------+----------------------+--------------+-------+
| lev1        | lev2                 | lev3         | lev4  |
+-------------+----------------------+--------------+-------+
| ELECTRONICS | TELEVISIONS          | TUBE         | NULL  |
| ELECTRONICS | TELEVISIONS          | LCD          | NULL  |
| ELECTRONICS | TELEVISIONS          | PLASMA       | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS  | FLASH |
| ELECTRONICS | PORTABLE ELECTRONICS | CD PLAYERS   | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | 2 WAY RADIOS | NULL  |
+-------------+----------------------+--------------+-------+

अधिकांश उपयोगकर्ता एक समय या किसी अन्य ने एक SQL डेटाबेस में पदानुक्रमित डेटा के साथ काम किया है और इसमें कोई संदेह नहीं है कि पदानुक्रमित डेटा का प्रबंधन वह नहीं है जो एक संबंधपरक डेटाबेस के लिए है। एक संबंधपरक डेटाबेस की तालिकाएं पदानुक्रमित (एक्सएमएल की तरह) नहीं हैं, लेकिन बस एक फ्लैट सूची हैं। पदानुक्रमित डेटा में माता-पिता-बच्चे का संबंध होता है जो स्वाभाविक रूप से एक संबंधपरक डेटाबेस तालिका में प्रतिनिधित्व नहीं करता है। अधिक पढ़ें

अधिक जानकारी के लिए ब्लॉग देखें।

संपादित करें:

select @pv:=category_id as category_id, name, parent from category
join
(select @pv:=19)tmp
where parent=@pv

आउटपुट:

category_id name    parent
19  category1   0
20  category2   19
21  category3   20
22  category4   21

संदर्भ: मैसकरल में पुनरावर्ती चयन क्वेरी कैसे करें?


23
यह ठीक है जब तक कि पदानुक्रम में 4 से अधिक स्तर नहीं हैं। यदि एन स्तर हैं, तो आपको यह जानना होगा कि क्वेरी को ठीक से बनाना है।
जोनाथन लेफ्लर

2
@ दामोदरन, आपके उत्तर के लिए धन्यवाद ... मुझे जिस चीज की आवश्यकता है वह एक ऐसी स्थिति है जहाँ चिल्ड की संख्या ज्ञात नहीं है ... और ब्लॉग में जो आंतरिक जुड़ाव अवधारणा का उपयोग कर रहा है उसमें पदानुक्रम को जानना आवश्यक है जो कि नहीं है मेरे मामले में ... तो मेरे hirerachy स्तरों पर एक ही ... तो आपके विचार पता है, सरल शब्दों में मैं संभाल करने के लिए एक प्रश्न की जरूरत है 'एन' चलो जहां 'एन' ज्ञात नहीं है .....
तरुण Parswani

1
@ user3036105: एकल SQL क्वेरी के साथ MySQL में ऐसा करना संभव नहीं है । MySQL बस उस के लिए पर्याप्त उन्नत नहीं है। यदि आपको वास्तव में इसकी आवश्यकता है, तो एक DBMS के उन्नयन पर विचार करें जो पुनरावर्ती प्रश्नों का समर्थन करता है।
a_horse_with_no_name

5
> एक समय या किसी अन्य में अधिकांश उपयोगकर्ताओं ने SQL डेटाबेस में पदानुक्रम डेटा से निपटा है और इसमें कोई संदेह नहीं है कि पदानुक्रमित डेटा का प्रबंधन वह नहीं है जो एक संबंधपरक डेटाबेस के लिए है। शायद आपका मतलब एक MySQL डेटाबेस था। एक Oracle डेटाबेस पदानुक्रमित डेटा और प्रश्नों को काफी अच्छी तरह से संभालता है।
पीटर नोसको

1
"... पदानुक्रमित डेटा का प्रबंधन वह नहीं है जो एक संबंधपरक डेटाबेस के लिए है ..." जबकि यह एक संबंधपरक डेटाबेस का मूल उद्देश्य नहीं हो सकता है, वास्तविक दुनिया में पदानुक्रमित डेटा अविश्वसनीय रूप से सामान्य है और MySQL को प्रतिबिंबित करना चाहिए लोगों को वास्तव में वास्तविक दुनिया के परिदृश्यों में अपने डेटा का उपयोग करने की आवश्यकता है।
डेव एल

9

इन्हें कोशिश करें:

तालिका परिभाषा:

DROP TABLE IF EXISTS category;
CREATE TABLE category (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(20),
    parent_id INT,
    CONSTRAINT fk_category_parent FOREIGN KEY (parent_id)
    REFERENCES category (id)
) engine=innodb;

प्रायोगिक पंक्तियाँ:

INSERT INTO category VALUES
(19, 'category1', NULL),
(20, 'category2', 19),
(21, 'category3', 20),
(22, 'category4', 21),
(23, 'categoryA', 19),
(24, 'categoryB', 23),
(25, 'categoryC', 23),
(26, 'categoryD', 24);

पुनरावर्ती संग्रहित प्रक्रिया:

DROP PROCEDURE IF EXISTS getpath;
DELIMITER $$
CREATE PROCEDURE getpath(IN cat_id INT, OUT path TEXT)
BEGIN
    DECLARE catname VARCHAR(20);
    DECLARE temppath TEXT;
    DECLARE tempparent INT;
    SET max_sp_recursion_depth = 255;
    SELECT name, parent_id FROM category WHERE id=cat_id INTO catname, tempparent;
    IF tempparent IS NULL
    THEN
        SET path = catname;
    ELSE
        CALL getpath(tempparent, temppath);
        SET path = CONCAT(temppath, '/', catname);
    END IF;
END$$
DELIMITER ;

संग्रहित प्रक्रिया के लिए आवरण समारोह:

DROP FUNCTION IF EXISTS getpath;
DELIMITER $$
CREATE FUNCTION getpath(cat_id INT) RETURNS TEXT DETERMINISTIC
BEGIN
    DECLARE res TEXT;
    CALL getpath(cat_id, res);
    RETURN res;
END$$
DELIMITER ;

उदाहरण का चयन करें:

SELECT id, name, getpath(id) AS path FROM category;

आउटपुट:

+----+-----------+-----------------------------------------+
| id | name      | path                                    |
+----+-----------+-----------------------------------------+
| 19 | category1 | category1                               |
| 20 | category2 | category1/category2                     |
| 21 | category3 | category1/category2/category3           |
| 22 | category4 | category1/category2/category3/category4 |
| 23 | categoryA | category1/categoryA                     |
| 24 | categoryB | category1/categoryA/categoryB           |
| 25 | categoryC | category1/categoryA/categoryC           |
| 26 | categoryD | category1/categoryA/categoryB/categoryD |
+----+-----------+-----------------------------------------+

निश्चित पथ के साथ पंक्तियों को फ़िल्टर करना:

SELECT id, name, getpath(id) AS path FROM category HAVING path LIKE 'category1/category2%';

आउटपुट:

+----+-----------+-----------------------------------------+
| id | name      | path                                    |
+----+-----------+-----------------------------------------+
| 20 | category2 | category1/category2                     |
| 21 | category3 | category1/category2/category3           |
| 22 | category4 | category1/category2/category3/category4 |
+----+-----------+-----------------------------------------+

1
यह एक से अधिक बच्चों के लिए काम नहीं करेगा। जैसे(20, 'category2', 19), (21, 'category3', 20), (22, 'category4', 20),
क्लीन कोड

3
मुझे पूरा यकीन है कि यह एक से अधिक बच्चों के लिए काम करता है। मैं भी इसे फिर से परीक्षण किया गया।
फांडी सुसंतो

@ फांडी सुसांतो, धन्यवाद यह मुझे मदद करता है।
डोगन ओज़र

9

मेरे पास सबसे अच्छा तरीका है

  1. पेड़ों को सॉर्ट करने के लिए वंश का उपयोग करें। यह पर्याप्त से अधिक है, और किसी भी अन्य दृष्टिकोण की तुलना में पढ़ने के लिए हजारों गुना तेजी से काम करता है। यह उस पैटर्न पर रहने की अनुमति देता है, भले ही डीबी बदल जाएगा (जैसा कि कोई भी डीबी उस पैटर्न का उपयोग करने की अनुमति देगा)
  2. फ़ंक्शन का उपयोग करें जो विशिष्ट आईडी के लिए वंश को निर्धारित करता है।
  3. अपनी इच्छानुसार (चयनों में, या CUD परिचालनों पर, या यहां तक ​​कि नौकरियों द्वारा) इसका उपयोग करें।

वंश दृष्टिकोण descr। उदाहरण के लिए, यहाँ या जहाँ भी पाया जा सकता है । समारोह के रूप में - कि मुझे क्या कहा जाता है।

अंत में - अधिक-या-कम सरल, अपेक्षाकृत तेज़, और सरल समाधान मिला।

शरीर का कार्य

-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$

CREATE DEFINER=`root`@`localhost` FUNCTION `get_lineage`(the_id INT) RETURNS text CHARSET utf8
    READS SQL DATA
BEGIN

 DECLARE v_rec INT DEFAULT 0;

 DECLARE done INT DEFAULT FALSE;
 DECLARE v_res text DEFAULT '';
 DECLARE v_papa int;
 DECLARE v_papa_papa int DEFAULT -1;
 DECLARE csr CURSOR FOR 
  select _id,parent_id -- @n:=@n+1 as rownum,T1.* 
  from 
    (SELECT @r AS _id,
        (SELECT @r := table_parent_id FROM table WHERE table_id = _id) AS parent_id,
        @l := @l + 1 AS lvl
    FROM
        (SELECT @r := the_id, @l := 0,@n:=0) vars,
        table m
    WHERE @r <> 0
    ) T1
    where T1.parent_id is not null
 ORDER BY T1.lvl DESC;
 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    open csr;
    read_loop: LOOP
    fetch csr into v_papa,v_papa_papa;
        SET v_rec = v_rec+1;
        IF done THEN
            LEAVE read_loop;
        END IF;
        -- add first
        IF v_rec = 1 THEN
            SET v_res = v_papa_papa;
        END IF;
        SET v_res = CONCAT(v_res,'-',v_papa);
    END LOOP;
    close csr;
    return v_res;
END

और फिर तुम बस

select get_lineage(the_id)

आशा है कि यह किसी की मदद करता है :)


9

यहाँ एक और quetion के लिए एक ही बात की थी

Mysql चुनिंदा पुनरावर्ती को कई स्तर के साथ सभी बच्चे मिलते हैं

क्वेरी होगी:

SELECT GROUP_CONCAT(lv SEPARATOR ',') FROM (
  SELECT @pv:=(
    SELECT GROUP_CONCAT(id SEPARATOR ',')
    FROM table WHERE parent_id IN (@pv)
  ) AS lv FROM table 
  JOIN
  (SELECT @pv:=1)tmp
  WHERE parent_id IN (@pv)
) a;

हम ऐसा कैसे कर सकते हैं? SELECT idFolder, (SELECT GROUP_CONCAT(lv SEPARATOR ',') FROM ( SELECT @pv:=(SELECT GROUP_CONCAT(idFolder SEPARATOR ',') FROM Folder WHERE idFolderParent IN (@pv)) AS lv FROM Folder JOIN (SELECT @pv:= F1.idFolder )tmp WHERE idFolderParent IN (@pv)) a) from folder F1 where id > 10; मैं @34 के लिए F1.idFolder का संदर्भ नहीं ले सकता
राहुल

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

7

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

ancestor | descendant | depth
0        | 0          | 0
0        | 19         | 1
0        | 20         | 2
0        | 21         | 3
0        | 22         | 4
19       | 19         | 0
19       | 20         | 1
19       | 21         | 3
19       | 22         | 4
20       | 20         | 0
20       | 21         | 1
20       | 22         | 2
21       | 21         | 0
21       | 22         | 1
22       | 22         | 0

एक बार जब आपके पास यह तालिका होती है, तो पदानुक्रमित प्रश्न बहुत आसान और तेज़ हो जाते हैं। श्रेणी 20 के सभी वंशज पाने के लिए:

SELECT cat.* FROM categories_closure AS cl
INNER JOIN categories AS cat ON cat.id = cl.descendant
WHERE cl.ancestor = 20 AND cl.depth > 0

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

संपादित करें : प्रश्न देखें कि संबंधपरक डेटाबेस में पदानुक्रमित डेटा संग्रहीत करने के लिए क्या विकल्प हैं? अधिक विकल्पों के लिए। विभिन्न स्थितियों के लिए अलग-अलग इष्टतम समाधान हैं।


4

पहली पुनरावृत्ति के बच्चे की सूची के लिए सरल प्रश्न:

select @pv:=id as id, name, parent_id
from products
join (select @pv:=19)tmp
where parent_id=@pv

परिणाम:

id  name        parent_id
20  category2   19
21  category3   20
22  category4   21
26  category24  22

... बाईं ओर के साथ:

select
    @pv:=p1.id as id
  , p2.name as parent_name
  , p1.name name
  , p1.parent_id
from products p1
join (select @pv:=19)tmp
left join products p2 on p2.id=p1.parent_id -- optional join to get parent name
where p1.parent_id=@pv

सभी बच्चों को सूचीबद्ध करने के लिए @tincot का समाधान:

select  id,
        name,
        parent_id 
from    (select * from products
         order by parent_id, id) products_sorted,
        (select @pv := '19') initialisation
where   find_in_set(parent_id, @pv) > 0
and     @pv := concat(@pv, ',', id)

Sql Fiddle के साथ इसे ऑनलाइन टेस्ट करें और सभी परिणाम देखें।

http://sqlfiddle.com/#!9/a318e3/4/0


3

आप इसे अन्य डेटाबेस में एक पुनरावर्ती क्वेरी (प्रदर्शन पर वाईएमएमवी) के साथ आसानी से कर सकते हैं।

ऐसा करने का दूसरा तरीका दो अतिरिक्त बिट डेटा, एक बाएँ और दाएँ मान को संग्रहीत करना है। बाएँ और दाएँ मान ट्री संरचना के एक पूर्व-क्रम ट्रैवर्सल से प्राप्त किए जाते हैं, जिसका आप प्रतिनिधित्व कर रहे हैं।

यह संशोधित प्रीऑर्डर ट्री ट्रैवर्सल के रूप में जाना जाता है और आपको एक बार में सभी मूल मान प्राप्त करने के लिए एक सरल क्वेरी चलाने की सुविधा देता है। इसे "नेस्टेड सेट" नाम से भी जाना जाता है।


मैं आपके साथ एक समान टिप्पणी जोड़ना चाहता था, लेकिन जब से आपने इसे किया, मैं "नेस्टेड सेट" के एक अच्छे उदाहरण के लिए सिर्फ एक लिंक जोड़ूंगा
Miroslaw ओपका

2

Mysql में सेल्फ-रिलेशन टेबल का पेड़ बनाने के लिए बस ब्लूएम / ट्री php क्लास का उपयोग करें ।

ट्री और ट्री \ Node डेटा को संभालने के लिए PHP कक्षाएं हैं जो मूल आईडी संदर्भों का उपयोग करते हुए पदानुक्रम से संरचित होती हैं। एक विशिष्ट उदाहरण एक संबंधपरक डेटाबेस में एक तालिका है जहां प्रत्येक रिकॉर्ड का "मूल" फ़ील्ड दूसरे रिकॉर्ड की प्राथमिक कुंजी को संदर्भित करता है। बेशक, ट्री केवल डेटाबेस से उत्पन्न डेटा का उपयोग नहीं कर सकता है, लेकिन कुछ भी: आप डेटा की आपूर्ति करते हैं, और ट्री इसका उपयोग करता है, भले ही डेटा कहाँ से आया हो और यह कैसे संसाधित किया गया हो। अधिक पढ़ें

यहाँ ब्लूएम / ट्री का उपयोग करने का एक उदाहरण दिया गया है:

<?php 
require '/path/to/vendor/autoload.php'; $db = new PDO(...); // Set up your database connection 
$stm = $db->query('SELECT id, parent, title FROM tablename ORDER BY title'); 
$records = $stm->fetchAll(PDO::FETCH_ASSOC); 
$tree = new BlueM\Tree($records); 
...

2

यहां छवि विवरण दर्ज करें

यह एक श्रेणी तालिका है।

SELECT  id,
        NAME,
        parent_category 
FROM    (SELECT * FROM category
         ORDER BY parent_category, id) products_sorted,
        (SELECT @pv := '2') initialisation
WHERE   FIND_IN_SET(parent_category, @pv) > 0
AND     @pv := CONCAT(@pv, ',', id)

आउटपुट :: यहां छवि विवरण दर्ज करें


1
क्या आप इसे समझा सकते हैं? लेकिन मैं गारंटी देता हूं कि यह काम कर रहा है। धन्यवाद।
जॉब्स

1
plz क्वेरी की व्याख्या करें और @pv का अर्थ क्या है ?? इस क्वेरी में लूप कैसे काम कर रहा है?
अमनजोत कौर

2
सभी स्तरों पर काम करना प्रतीत नहीं होता है यदि ऐसे बच्चे हैं जिनके माता-पिता की तुलना में कम आईडी हैं। :(
जोनास

1
@ जोनास ने अलग-अलग संयोजन के साथ वास्तविक समस्या की पहचान करने में मुझे 20 मिनट का समय दिया। हाँ तुम सही हो। यह अपनी मूल आईडी से कम आईडी के साथ काम नहीं करेगा। क्या आपके पास कोई उपाय है?
मुअज़्ज़

@muaaz I ने अंत में इसे "पथ" फ़ील्ड का उपयोग करके हल किया जिसमें संबंधित पंक्ति के लिए पथ है, जैसे ID 577 के साथ पंक्ति में पथ "/ 1/2/45/577 /" है। यदि आप ID 2 के सभी बच्चों की तलाश कर रहे हैं, तो आप पथ LIKE "/ 1/2 /%" के साथ सभी पंक्तियों का चयन कर सकते हैं। केवल नकारात्मक पक्ष यह है कि आपको अपने अपडेट के तरीकों में पथ को अपडेट करना होगा। लेकिन MySQL 5.6 (संगत) के लिए, यह एकमात्र समाधान था जो मेरे लिए काम करता था।
जोनास

1

इसकी एक छोटी सी मुश्किल है, यह जांचें कि क्या यह आपके लिए काम कर रहा है

select a.id,if(a.parent = 0,@varw:=concat(a.id,','),@varw:=concat(a.id,',',@varw)) as list from (select * from recursivejoin order by if(parent=0,id,parent) asc) a left join recursivejoin b on (a.id = b.parent),(select @varw:='') as c  having list like '%19,%';

SQL फिडेल लिंक http://www.sqlfiddle.com/#/2/e3cdf/2

अपने क्षेत्र और तालिका के नाम को उचित रूप से बदलें।


यह इस मामले में काम नहीं करेगा sqlfiddle.com/#/2/19360/2 , इस ट्रिक के साथ, कम से कम आपको पहले पदानुक्रमित स्तर से ऑर्डर करना चाहिए।
जुगार चांग

1

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

कुछ ऐसा हैं:

id | name        | path
19 | category1   | /19
20 | category2   | /19/20
21 | category3   | /19/20/21
22 | category4   | /19/20/21/22

उदाहरण:

-- get children of category3:
SELECT * FROM my_table WHERE path LIKE '/19/20/21%'
-- Reparent an item:
UPDATE my_table SET path = REPLACE(path, '/19/20', '/15/16') WHERE path LIKE '/19/20/%'

पथ संख्या को ऑप्टिमाइज़ करें और ORDER BY pathवास्तविक सांख्यिक पथ आईडी के बजाय बेस 36 एन्कोडिंग का उपयोग करें

 // base10 => base36
 '1' => '1',
 '10' => 'A',
 '100' => '2S',
 '1000' => 'RS',
 '10000' => '7PS',
 '100000' => '255S',
 '1000000' => 'LFLS',
 '1000000000' => 'GJDGXS',
 '1000000000000' => 'CRE66I9S'

https://en.wikipedia.org/wiki/Base36

निश्चित लंबाई का उपयोग करके और एन्कोडेड आईडी पर पैडिंग करके स्लैश '/' विभाजक को दबाएं

यहाँ विस्तृत अनुकूलन स्पष्टीकरण: https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/

करने के लिए

एक आइटम के पूर्ववर्ती पूर्वजों के लिए पथ को विभाजित करने के लिए एक फ़ंक्शन या प्रक्रिया का निर्माण


धन्यवाद! साथ दिलचस्पbase36
व्लाद

0

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

SET @id:= '22';

SELECT Menu_Name, (@id:=Sub_Menu_ID ) as Sub_Menu_ID, Menu_ID 
FROM 
    ( SELECT Menu_ID, Menu_Name, Sub_Menu_ID 
      FROM menu 
      ORDER BY Sub_Menu_ID DESC
    ) AS aux_table 
    WHERE Menu_ID = @id
     ORDER BY Sub_Menu_ID;

सभी स्तरों पर काम करना प्रतीत नहीं होता है अगर ऐसे बच्चे हैं जिनके माता
muaaz

-1

मैंने इसे और अधिक आसानी से पाया:

1) एक फ़ंक्शन बनाएं जो यह जांच करेगा कि क्या एक आइटम किसी दूसरे के माता-पिता के पदानुक्रम में कहीं भी है। कुछ इस तरह (मैं फ़ंक्शन नहीं लिखूंगा, इसे WHILE DO के साथ बनाएं):

is_related(id, parent_id);

आपके उदाहरण में

is_related(21, 19) == 1;
is_related(20, 19) == 1;
is_related(21, 18) == 0;

2) एक उप-चयन का उपयोग करें, कुछ इस तरह से:

select ...
from table t
join table pt on pt.id in (select i.id from table i where is_related(t.id,i.id));

-1

मैंने आपके लिए एक प्रश्न किया है। यह आपको एकल क्वेरी के साथ पुनरावर्ती श्रेणी देगा:

SELECT id,NAME,'' AS subName,'' AS subsubName,'' AS subsubsubName FROM Table1 WHERE prent is NULL
UNION 
SELECT b.id,a.name,b.name AS subName,'' AS subsubName,'' AS subsubsubName FROM Table1 AS a LEFT JOIN Table1 AS b ON b.prent=a.id WHERE a.prent is NULL AND b.name IS NOT NULL 
UNION 
SELECT c.id,a.name,b.name AS subName,c.name AS subsubName,'' AS subsubsubName FROM Table1 AS a LEFT JOIN Table1 AS b ON b.prent=a.id LEFT JOIN Table1 AS c ON c.prent=b.id WHERE a.prent is NULL AND c.name IS NOT NULL 
UNION 
SELECT d.id,a.name,b.name AS subName,c.name AS subsubName,d.name AS subsubsubName FROM Table1 AS a LEFT JOIN Table1 AS b ON b.prent=a.id LEFT JOIN Table1 AS c ON c.prent=b.id LEFT JOIN Table1 AS d ON d.prent=c.id WHERE a.prent is NULL AND d.name IS NOT NULL 
ORDER BY NAME,subName,subsubName,subsubsubName

यहाँ एक बेला है


कृपया अपनी सकारात्मक प्रतिष्ठा वापस पाने के लिए अपना उत्तर हटाएं / संपादित करें।
fWd82
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.