निम्नलिखित दो प्रश्नों के बीच अंतर पर विचार करें:
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 द्वारा गारंटी नहीं है, और सर्वर के किसी भी संस्करण के साथ बदल सकता है।
मुझे एक त्रुटि मिलती है: '# 1064 - आपको अपने SQL वाक्यविन्यास में कोई त्रुटि है; '@id int) रिटर्न वर्चर (400) के उपयोग के लिए सही वाक्यविन्यास के लिए आपके MySQL सर्वर संस्करण से मेल खाने वाले मैन्युअल की जांच करें, जैसा कि @path varchar (400) घोषित करना शुरू होता है @ linem' लाइन 1' पर घोषित करता है और बिना लूप के समाधान के बारे में क्या है ?! –
उत्तर SQLServer वाक्यविन्यास में है; मैं इसे MySQL वाक्यविन्यास में बदलने में आपकी सहायता कर सकता हूं। चूंकि इसमें पदानुक्रमित डेटा है, और आप कोई कॉलम नहीं जोड़ना चाहते हैं यानी: गहराई/पथ जिसे आपको अद्यतन/आवेषण/हटाना में अपडेट करने की आवश्यकता है; मैं नहीं देख सकता कि एक सोलन है। बिना लूप के। यदि MySQL में सीटीई होगा, तो आप उपयोगकर्ता परिभाषित फ़ंक्शन के बिना, और बिना लूप के एक ही समस्या को हल कर सकते हैं। –