MySQL में Recursive SELECT क्वेरी कैसे करें?


79

मुझे निम्नलिखित तालिका मिली:

col1 | col2 | col3
-----+------+-------
1    | a    | 5
5    | d    | 3
3    | k    | 7
6    | o    | 2
2    | 0    | 8

यदि कोई उपयोगकर्ता "1" की खोज करता है, तो प्रोग्राम उस "1" को देखेगा, col1तब उसे col3"5" में मान मिलेगा , फिर प्रोग्राम "5" में खोज जारी रखेगा col1और उसे "3" मिलेगा में col3, और इतने पर। तो यह प्रिंट होगा:

1   | a   | 5
5   | d   | 3
3   | k   | 7

यदि कोई उपयोगकर्ता "6" खोजता है, तो यह प्रिंट आउट होगा:

6   | o   | 2
2   | 0   | 8

ऐसा करने के लिए एक SELECTक्वेरी कैसे बनाएँ ?


इस पोस्ट में आपकी समस्या का हल है stackoverflow.com/questions/14658378/recursive-mysql-select
medina

जवाबों:


70

संपादित करें

@Leftclickben द्वारा उल्लिखित समाधान भी प्रभावी है। हम उसी के लिए एक संग्रहीत प्रक्रिया का उपयोग भी कर सकते हैं।

CREATE PROCEDURE get_tree(IN id int)
 BEGIN
 DECLARE child_id int;
 DECLARE prev_id int;
 SET prev_id = id;
 SET child_id=0;
 SELECT col3 into child_id 
 FROM table1 WHERE col1=id ;
 create TEMPORARY  table IF NOT EXISTS temp_table as (select * from table1 where 1=0);
 truncate table temp_table;
 WHILE child_id <> 0 DO
   insert into temp_table select * from table1 WHERE col1=prev_id;
   SET prev_id = child_id;
   SET child_id=0;
   SELECT col3 into child_id
   FROM TABLE1 WHERE col1=prev_id;
 END WHILE;
 select * from temp_table;
 END //

हम आउटपुट के परिणामों को संग्रहीत करने के लिए अस्थायी तालिका का उपयोग कर रहे हैं और जैसे ही अस्थायी तालिकाएँ सत्र आधारित होती हैं हम अभ्यस्त होंगे कि आउटपुट डेटा के गलत होने के बारे में कोई समस्या नहीं होगी।

SQL FIDDLE Demo

इस प्रश्न को आज़माएं:

SELECT 
    col1, col2, @pv := col3 as 'col3' 
FROM 
    table1
JOIN 
    (SELECT @pv := 1) tmp
WHERE 
    col1 = @pv

SQL FIDDLE Demo:

| COL1 | COL2 | COL3 |
+------+------+------+
|    1 |    a |    5 |
|    5 |    d |    3 |
|    3 |    k |    7 |

इस समाधान के काम करने
parent_idके child_idलिए नोट का मूल्य कम होना चाहिए ।


2
लोग Pls इस जवाब को एक इष्टतम समाधान के रूप में चिह्नित करते हैं क्योंकि इसी तरह के कुछ अन्य समाधानों के बारे में (रिकर्सिव सिलेक्ट मायस्कल में) काफी जटिल हैं क्योंकि इसमें टेबल बनाने और उसमें डेटा डालने की आवश्यकता होती है। यह समाधान बहुत ही सुरुचिपूर्ण है।
तुम

5
बस उसके समाधान के साथ एक ध्यान रखें, कोई चक्र प्रकार निर्भरता नहीं है तो यह अनंत लूप में जाएगा और एक और बात यह केवल उस col3प्रकार का 1 रिकॉर्ड पाएगी इसलिए यदि कई रिकॉर्ड हैं तो यह काम नहीं करेगा।
मेहरज़ाद

2
@ HamidSarfraz अब यह sqlfiddle.com/# -2/74f457/14 काम करता है । यह आपके काम आएगा। जैसा कि यह अनुक्रमिक खोज के लिए जाता है और आईडी में हमेशा माता-पिता की तुलना में अधिक मूल्य होगा, क्योंकि माता-पिता को पहले बनाने की आवश्यकता होती है। यदि आपको किसी अतिरिक्त विवरण की आवश्यकता हो तो pl सूचित करें।
मेहरज़ाद

5
यह कोई हल नहीं है। यह एक टेबल स्कैन का सिर्फ एक भाग्यशाली पक्ष प्रभाव है। @Leftclickben के उत्तर को ध्यान से पढ़ें या जैसा कि मैंने किया था बहुत समय बर्बाद करेगा।
जयहेग

2
तुम मुझे पता है कि कैसे पुनरावर्ती एसक्यूएल काम करता है। MySQL ने पुनरावर्ती CTE को लागू नहीं किया है, इसलिए आपके द्वारा दिए गए लिंक में एक व्यवहार्य विकल्प एक है (संग्रहीत प्रक्रियाओं / कार्यों का उपयोग करके)। एक अन्य mysql वैरिएबल का उपयोग कर रहा है। हालाँकि, यहाँ का उत्तर सुरुचिपूर्ण नहीं है, बल्कि विपरीत है, बस भयानक है। यह पुनरावर्ती SQL नहीं दिखा रहा है। यदि यह आपके मामले में काम करता है, तो केवल दुर्घटना से, जैसा कि @jaehung ने सही ढंग से बताया। और मैं भयानक जवाब बुरा नहीं है। मैं सिर्फ उन्हें नीचा दिखाता हूं। लेकिन +50 पर एक भयानक जवाब, मुझे बुरा लगता है।
ypercube y

51

@Meherzad द्वारा स्वीकृत उत्तर केवल तभी काम करता है जब डेटा किसी विशेष क्रम में हो। यह ओपी प्रश्न के डेटा के साथ काम करने के लिए होता है। मेरे मामले में, मुझे अपने डेटा के साथ काम करने के लिए इसे संशोधित करना पड़ा।

नोट यह केवल तभी काम करता है जब प्रत्येक रिकॉर्ड की "आईडी" (कॉल 1 प्रश्न में) का एक महान मूल्य होता है जो रिकॉर्ड की "मूल आईडी" (कॉल 3 प्रश्न में)। यह अक्सर ऐसा होता है, क्योंकि आम तौर पर माता-पिता को पहले बनाने की आवश्यकता होगी। हालाँकि, यदि आपका आवेदन पदानुक्रम में परिवर्तन की अनुमति देता है, जहां एक आइटम को कहीं और फिर से रखा जा सकता है, तो आप इस पर भरोसा नहीं कर सकते।

यह मेरी क्वेरी है अगर यह किसी की मदद करता है; ध्यान दें कि यह दिए गए प्रश्न के साथ काम नहीं करता है क्योंकि डेटा ऊपर वर्णित आवश्यक संरचना का पालन नहीं करता है।

select t.col1, t.col2, @pv := t.col3 col3
from (select * from table1 order by col1 desc) t
join (select @pv := 1) tmp
where t.col1 = @pv

अंतर यह है कि table1आदेश दिया जा रहा है col1ताकि माता-पिता इसके बाद होंगे (चूंकि माता-पिता का col1मूल्य बच्चे की तुलना में कम है)।


यू राइट, अगर किसी बच्चे के पास 2 माता-पिता हैं, तो वह दोनों को नहीं ले सकता है
Tum

धन्यवाद दोस्त। टीमवॉर्क ने इस पोस्ट में अपना काम किया! मुझे यह काम करने के लिए मिला जब मैंने @pv का मान बदल दिया। बस यही मैं ढूंढ रहा था।
मोहम्मद एन्नहदी एल इदरीसी

क्या होगा यदि मैं इसे प्रत्येक पंक्ति के लिए पैरेंट आईडी के एक समूह_कॉन्केट कॉलम के रूप में एक बड़े चयन में उपयोग करना चाहता हूं (जिसका अर्थ है कि प्रत्येक पंक्ति के लिए @pv चर का मान गतिशील होना चाहिए)। उपकुंजी में शामिल होने से मास्टर कॉलम (जिस पर मैं कनेक्ट करने की कोशिश करता हूं) को नहीं जानता, दूसरे चर का उपयोग करके यह या तो काम नहीं करता है (हमेशा NULL देता है)
qdev

मैंने एक कस्टम फ़ंक्शन बनाया है जो group_concat का उपयोग करके ट्री पथ को उत्पन्न करता है, और मैं अब प्रत्येक पंक्ति के लिए कॉलम के रूप में भेज सकता हूं;)
qdev

आपके द्वारा पोस्ट किए गए नए उत्तर के बारे में आप क्या सोचते हैं? ऐसा नहीं है कि आपका अच्छा नहीं है, लेकिन मैं केवल एक सेलेक्ट होना चाहता हूं जो पेरेंट आईडी> चाइल्ड आईडी का समर्थन कर सके।
13:13 बजे मास्टर डीजेन

18

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

item | parent
-------------
1    | null
2    | 1
3    | 1
4    | 2
5    | 4
6    | 3

तथा

select t.item_id as item, @pv:=t.parent as parent
from (select * from item_tree order by item_id desc) t
join
(select @pv:=6)tmp
where t.item_id=@pv;

देता है:

item | parent
-------------
6    | 3
3    | 1
1    | null

@ BoB3K, यह काम करेगा यदि आईडी "आदेश" में जरूरी नहीं है। ऐसा लगता है कि माता-पिता के आईडी के साथ काम न करने की स्थिति में चेन अधिक है तो उसका बच्चा? उदाहरण के लिए श्रृंखला 1> 120 > 112 केवल वापस आ जाएगी ((112, 120)), जबकि 2> 22> 221 रिटर्न पूर्ण श्रृंखला ((221,22), (22,2), (2, नल))
टोमेर ऑर्र Cagan

थोड़ी देर हो गई है, लेकिन मुझे लगता है कि मुझे मूल उत्तरों में पढ़ना याद है कि यह काम नहीं करता है यदि आइटम आईडी क्रम में नहीं हैं, जो आमतौर पर एक मुद्दा नहीं है यदि आईडी एक ऑटो वेतन वृद्धि कुंजी है।
BoB3K

यह अच्छी तरह से काम करता है और मैं इसे अपनी साइट के लिए उपयोग करता हूं ... यहां समस्या यह है कि एएससी परिणाम का आदेश देना संभव नहीं है। 1 3 6मैं array_reverse()इसके बजाय php में उपयोग करता हूं ..... उसके लिए कोई sql समाधान?
जो

8

संग्रहीत प्रक्रिया इसे करने का सबसे अच्छा तरीका है। क्योंकि मेहेरज़ाद का समाधान केवल तभी काम करेगा जब डेटा उसी क्रम का पालन करे।

यदि हमारे पास इस तरह की एक टेबल संरचना है

col1 | col2 | col3
-----+------+------
 3   | k    | 7
 5   | d    | 3
 1   | a    | 5
 6   | o    | 2
 2   | 0    | 8

यह काम नहीं करेगा। SQL Fiddle Demo

यहाँ एक नमूना प्रक्रिया कोड प्राप्त करने के लिए है।

delimiter //
CREATE PROCEDURE chainReaction 
(
    in inputNo int
) 
BEGIN 
    declare final_id int default NULL;
    SELECT col3 
    INTO final_id 
    FROM table1
    WHERE col1 = inputNo;
    IF( final_id is not null) THEN
        INSERT INTO results(SELECT col1, col2, col3 FROM table1 WHERE col1 = inputNo);
        CALL chainReaction(final_id);   
    end if;
END//
delimiter ;

call chainReaction(1);
SELECT * FROM results;
DROP TABLE if exists results;

यह एक मजबूत समाधान है और मैं बिना परेशानी के इसका उपयोग कर रहा हूं। क्या आप कृपया दूसरी दिशा में जाने पर, यानी पेड़ के नीचे जाने में मेरी मदद कर सकते हैं - मुझे उन सभी पंक्तियों का पता है जहाँ मूल आईडी == inputNo है, लेकिन कई आईडी में एक मूल आईडी हो सकती है।
mils

8

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

मैं तालिका / स्तंभ नामों को पारित करने में सक्षम होने के लिए गतिशील SQL का उपयोग करना चाहता था, लेकिन MySQL में फ़ंक्शन इसका समर्थन नहीं करते हैं।

DELIMITER $$

CREATE FUNCTION `isSubElement`(pParentId INT, pId INT) RETURNS int(11)
DETERMINISTIC    
READS SQL DATA
BEGIN
DECLARE isChild,curId,curParent,lastParent int;
SET isChild = 0;
SET curId = pId;
SET curParent = -1;
SET lastParent = -2;

WHILE lastParent <> curParent AND curParent <> 0 AND curId <> -1 AND curParent <> pId AND isChild = 0 DO
    SET lastParent = curParent;
    SELECT ParentId from `test` where id=curId limit 1 into curParent;

    IF curParent = pParentId THEN
        SET isChild = 1;
    END IF;
    SET curId = curParent;
END WHILE;

RETURN isChild;
END$$

यहां, तालिका testको वास्तविक तालिका नाम में संशोधित किया जाना है और आपके वास्तविक नामों के लिए कॉलम (ParentId, Id) को समायोजित करना पड़ सकता है।

उपयोग:

SET @wantedSubTreeId = 3;
SELECT * FROM test WHERE isSubElement(@wantedSubTreeId,id) = 1 OR ID = @wantedSubTreeId;

परिणाम :

3   7   k
5   3   d
9   3   f
1   5   a

परीक्षण के निर्माण के लिए एसक्यूएल:

CREATE TABLE IF NOT EXISTS `test` (
  `Id` int(11) NOT NULL,
  `ParentId` int(11) DEFAULT NULL,
  `Name` varchar(300) NOT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

insert into test (id, parentid, name) values(3,7,'k');
insert into test (id, parentid, name) values(5,3,'d');
insert into test (id, parentid, name) values(9,3,'f');
insert into test (id, parentid, name) values(1,5,'a');
insert into test (id, parentid, name) values(6,2,'o');
insert into test (id, parentid, name) values(2,8,'c');

संपादित करें: यहाँ अपने आप को परीक्षण करने के लिए एक बेला है। इसने मुझे पूर्वनिर्धारित एक का उपयोग करके सीमांकक को बदलने के लिए मजबूर किया, लेकिन यह काम करता है।


0

मास्टर डीजेन का निर्माण

यहां सरलीकृत कार्य है जो वापसी की गहराई की अतिरिक्त उपयोगिता प्रदान करता है (यदि आप पैरेंट कार्य को शामिल करने के लिए तर्क का उपयोग करना चाहते हैं या किसी विशिष्ट गहराई पर खोज करना चाहते हैं)

DELIMITER $$
FUNCTION `childDepth`(pParentId INT, pId INT) RETURNS int(11)
    READS SQL DATA
    DETERMINISTIC
BEGIN
DECLARE depth,curId int;
SET depth = 0;
SET curId = pId;

WHILE curId IS not null AND curId <> pParentId DO
    SELECT ParentId from test where id=curId limit 1 into curId;
    SET depth = depth + 1;
END WHILE;

IF curId IS NULL THEN
    set depth = -1;
END IF;

RETURN depth;
END$$

उपयोग:

select * from test where childDepth(1, id) <> -1;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.