2012-08-20 7 views
8

मेरे पास MySQL में एक पदानुक्रमित तालिका है: parent प्रत्येक आइटम बिंदु के क्षेत्र id अपने मूल आइटम के क्षेत्र में। प्रत्येक आइटम के लिए मैं query described here का उपयोग कर अपने सभी माता-पिता की सूची [गहराई पर ध्यान दें] प्राप्त कर सकता हूं।पदानुक्रमित तालिका - आइटमों के पथ कैसे प्राप्त करें [MySQL में लिंक्ड सूचियां]

SELECT GROUP_CONCAT(_id SEPARATOR ' > ') FROM (
SELECT @r AS _id, 
     (
     SELECT @r := parent 
     FROM t_hierarchy 
     WHERE id = _id 
     ) AS parent, 
     @l := @l + 1 AS lvl 
FROM (
     SELECT @r := 200, 
       @l := 0 
     ) vars, 
     t_hierarchy h 
WHERE @r <> 0 
ORDER BY lvl DESC 
) x 

मैं इस काम को केवल तभी आइटम के id तय हो गई है [यह इस मामले में 200 है] कर सकते हैं: GROUP_CONCAT के साथ मैं एक एकल स्ट्रिंग के रूप में पूरा पथ मिलता है।

मैं सभी पंक्तियों के लिए ऐसा करना चाहता हूं: पूरे तालिका को एक अतिरिक्त फ़ील्ड (path) से पुनर्प्राप्त करें जो पूर्ण पथ प्रदर्शित करेगा। मेरे दिमाग में आने वाला एकमात्र समाधान यह प्रश्न किसी अन्य चयन में लपेटना है, अस्थायी चर @id सेट करें और इसे सबक्वायरी के अंदर उपयोग करें। लेकिन यह काम नहीं करता है। मुझे path फ़ील्ड में NULL एस मिलता है।

SELECT @id := id, parent, (
    SELECT GROUP_CONCAT(_id SEPARATOR ' > ') FROM (
    SELECT @r AS _id, 
      (
      SELECT @r := parent 
      FROM t_hierarchy 
      WHERE id = _id 
      ) AS parent, 
      @l := @l + 1 AS lvl 
    FROM (
      SELECT @r := @id, 
        @l := 0 
      ) vars, 
      t_hierarchy h 
    WHERE @r <> 0 
    ORDER BY lvl DESC 
    ) x 
) as path 
FROM t_hierarchy 

पीएस मुझे पता है कि मैं पथ को एक अलग फ़ील्ड में स्टोर कर सकता हूं और उन्हें डालने/अपडेट करने पर अपडेट कर सकता हूं, लेकिन मुझे लिंक की गई सूची तकनीक पर आधारित समाधान की आवश्यकता है।

अद्यतन: मैं एक समाधान है कि for और while तरह प्रत्यावर्तन या निर्माणों का उपयोग नहीं होगा देखना चाहेंगे। पथ खोजने के लिए उपर्युक्त विधि किसी भी लूप या फ़ंक्शंस का उपयोग नहीं करती है। मैं एक ही तर्क में समाधान ढूंढना चाहता हूं। या, यदि यह असंभव है, तो कृपया समझाने की कोशिश क्यों करें!

उत्तर

1

getPath समारोह परिभाषित करें और निम्न क्वेरी चलाएँ:

SELECT @id := id as id, parent, (
    SELECT concat(id, ': ', @id) 
) as path 
FROM t_hierarchy; 

SELECT @id := id as id, parent, (
    SELECT concat(id, ': ', _id) 
    FROM (SELECT @id as _id) as x 
) as path 
FROM t_hierarchy; 

वे:

create function dbo.getPath(@id int) 
returns varchar(400) 
as 
begin 
declare @path varchar(400) 
declare @term int 
declare @parent varchar(100) 
set @path = '' 
set @term = 0 
while (@term <> 1) 
begin 
    select @parent = parent from t_hierarchy where id = @id 
    if (@parent is null or @parent = '' or @parent = @id) 
     set @term = 1 
    else 
     set @path = @path + @parent 
    set @id = @parent  
end 
return @path 
end 
+0

मुझे एक त्रुटि मिलती है: '# 1064 - आपको अपने SQL वाक्यविन्यास में कोई त्रुटि है; '@id int) रिटर्न वर्चर (400) के उपयोग के लिए सही वाक्यविन्यास के लिए आपके MySQL सर्वर संस्करण से मेल खाने वाले मैन्युअल की जांच करें, जैसा कि @path varchar (400) घोषित करना शुरू होता है @ linem' लाइन 1' पर घोषित करता है और बिना लूप के समाधान के बारे में क्या है ?! –

+0

उत्तर SQLServer वाक्यविन्यास में है; मैं इसे MySQL वाक्यविन्यास में बदलने में आपकी सहायता कर सकता हूं। चूंकि इसमें पदानुक्रमित डेटा है, और आप कोई कॉलम नहीं जोड़ना चाहते हैं यानी: गहराई/पथ जिसे आपको अद्यतन/आवेषण/हटाना में अपडेट करने की आवश्यकता है; मैं नहीं देख सकता कि एक सोलन है। बिना लूप के। यदि MySQL में सीटीई होगा, तो आप उपयोगकर्ता परिभाषित फ़ंक्शन के बिना, और बिना लूप के एक ही समस्या को हल कर सकते हैं। –

2

निम्नलिखित दो प्रश्नों के बीच अंतर पर विचार करें:

select id, parent, dbo.getPath(id) as path from t_hierarchy 

getPath समारोह को परिभाषित करना लगभग समान दिखें, लेकिन नाटकीय रूप से अलग-अलग परिणाम दें। MySQL के मेरे संस्करण पर, दूसरी क्वेरी में _id अपनी पंक्ति सेट में प्रत्येक पंक्ति के लिए समान है, और अंतिम पंक्ति के id के बराबर है। हालांकि, वह अंतिम बिट केवल सच है क्योंकि मैंने दिए गए क्रम में दो प्रश्नों को निष्पादित किया है; SET @id := 1 के बाद, उदाहरण के लिए, मैं देख सकता हूं कि _id हमेशा SET कथन में मान के बराबर है।

तो यहां क्या हो रहा है? एक EXPLAIN एक सुराग पैदावार:

mysql>  explain SELECT @id := id as id, parent, (
    ->   SELECT concat(id, ': ', _id) 
    ->   FROM (SELECT @id as _id) as x 
    -> ) as path 
    ->  FROM t_hierarchy; 
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+ 
| id | select_type  | table  | type | possible_keys | key    | key_len | ref | rows | Extra   | 
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+ 
| 1 | PRIMARY   | t_hierarchy | index | NULL   | hierarchy_parent | 9  | NULL | 1398 | Using index | 
| 2 | DEPENDENT SUBQUERY | <derived3> | system | NULL   | NULL    | NULL | NULL | 1 |    | 
| 3 | DERIVED   | NULL  | NULL | NULL   | NULL    | NULL | NULL | NULL | No tables used | 
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+ 
3 rows in set (0.00 sec) 

तीसरी पंक्ति का उपयोग किया नहीं तालिकाओं के साथ DERIVED मेज, MySQL करने के लिए इंगित करता है कि यह किसी भी समय ठीक एक बार गणना की जा सकती। सर्वर यह नहीं देखता है कि व्युत्पन्न तालिका क्वेरी में कहीं और परिभाषित चर का उपयोग करती है, और इसमें कोई सुराग नहीं है जिसे आप प्रति पंक्ति एक बार चलाने के लिए चाहते हैं। आप एक व्यवहार user-defined variables पर MySQL दस्तावेज में उल्लेख किया है ने काट लिया हो जा रहा है:

As a general rule, you should never assign a value to a user variable and read the value within the same statement. You might get the results you expect, but this is not guaranteed. The order of evaluation for expressions involving user variables is undefined and may change based on the elements contained within a given statement; in addition, this order is not guaranteed to be the same between releases of the MySQL Server.

मेरे मामले में, यह है कि टेबल पहले की गणना करना चुनता है, इससे पहले कि @id (पुनः) बाहरी SELECT द्वारा परिभाषित किया गया है।असल में, यही कारण है कि मूल पदानुक्रमित डेटा क्वेरी काम करता है; @r परिभाषा को क्वेरी में किसी और चीज से पहले MySQL द्वारा गणना की जाती है, ठीक है क्योंकि यह उस तरह की व्युत्पन्न तालिका है। हालांकि, हमें प्रति पंक्ति पंक्ति में एक बार @r रीसेट करने का एक तरीका चाहिए, न केवल संपूर्ण क्वेरी के लिए। ऐसा करने के लिए, हमें एक क्वेरी की आवश्यकता है जो मूल की तरह दिखता है, @r को हाथ से रीसेट कर रहा है।

SELECT @r := if(
      @c = th1.id, 
      if(
      @r is null, 
      null, 
      (
       SELECT parent 
       FROM t_hierarchy 
       WHERE id = @r 
      ) 
     ), 
      th1.id 
     ) AS parent, 
     @l := if(@c = th1.id, @l + 1, 0) AS lvl, 
     @c := th1.id as _id 
FROM (
     SELECT @c := 0, 
       @r := 0, 
       @l := 0 
     ) vars 
     left join t_hierarchy as th1 on 1 
     left join t_hierarchy as th2 on 1 
HAVING parent is not null 

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

विचित्र रूप से, GROUP_CONCAT के माध्यम से परिणाम चलाने से ऑर्डरिंग में बाधा आती है।

SELECT _id, 
     GROUP_CONCAT(parent ORDER BY lvl desc SEPARATOR ' > ') as path, 
     max(lvl) as depth 
FROM (
    SELECT @r := if(
      @c = th1.id, 
      if(
       @r is null, 
       null, 
       (
       SELECT parent 
       FROM t_hierarchy 
       WHERE id = @r 
      ) 
      ), 
      th1.id 
     ) AS parent, 
      @l := if(@c = th1.id, @l + 1, 0) AS lvl, 
      @c := th1.id as _id 
    FROM (
      SELECT @c := 0, 
        @r := 0, 
        @l := 0 
     ) vars 
      left join t_hierarchy as th1 on 1 
      left join t_hierarchy as th2 on 1 
    HAVING parent is not null 
    ORDER BY th1.id 
) as x 
GROUP BY _id; 

मेले चेतावनी:: इन प्रश्नों परोक्ष @c अद्यतन से पहले हो रहा @r और @l अद्यतन पर भरोसा करते हैं सौभाग्य से, कि समारोह का अपना ORDER BY खंड है। उस आदेश को MySQL द्वारा गारंटी नहीं है, और सर्वर के किसी भी संस्करण के साथ बदल सकता है।

+0

इस प्रश्न का जवाब देने के लिए धन्यवाद! यह जवाब मेरे लिए बहुत सी चीजों को स्पष्ट करता है। हालांकि अंतिम क्वेरी मेरे लिए खाली परिणाम लौटाती है [MySQL 5.1.40] लेकिन इसमें बहुत सारे महत्वपूर्ण विचार हैं, इसलिए मैं इसे प्रतिभा दे रहा हूं। यह कई बार पढ़ने की कोशिश करेगा और यह समझने की कोशिश करेगा कि आखिरी क्वेरी मेरे डीबी पर क्यों काम नहीं करती है और शायद कुछ चीजों को स्पष्ट करने के लिए कहेंगे। एक बार फिर धन्यवाद! –

+0

उत्सुक है कि यह 5.1.40 के लिए काम नहीं करेगा; यह उबंटू 11.10 वनिरिक पर 5.1.63 के खिलाफ परीक्षण किया गया था। आप @ सी लाइन को चारों ओर ले जाने का प्रयास कर सकते हैं; यह 'हैविंग' लाइन को हटाने या टिप्पणी करने के लिए डिबगिंग की सहायता भी कर सकता है। – eswald

संबंधित मुद्दे