तो आप उच्चतम OrderField
वाली पंक्ति प्राप्त करना चाहते हैं प्रति समूह? मैं इसे इस तरह से करूँगा:
SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId AND t1.OrderField < t2.OrderField
WHERE t2.GroupId IS NULL
ORDER BY t1.OrderField; // not needed! (note by Tomas)
(टॉमस द्वारा संपादित करें: यदि एक ही समूह के भीतर समान ऑर्डरफ़ील्ड के साथ और रिकॉर्ड हैं और आपको उनमें से ठीक एक की आवश्यकता है, तो आप शर्त का विस्तार करना चाह सकते हैं:
SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId
AND (t1.OrderField < t2.OrderField
OR (t1.OrderField = t2.OrderField AND t1.Id < t2.Id))
WHERE t2.GroupId IS NULL
संपादन का अंत।)
दूसरे शब्दों में, पंक्ति t1
return लौटाएं जिसके लिए कोई अन्य पंक्ति t2
. नहीं है समान GroupId
. के साथ मौजूद है और एक बड़ा OrderField
. जब t2.*
NULL है, इसका मतलब है कि बाएँ बाहरी जुड़ाव को ऐसा कोई मेल नहीं मिला, और इसलिए t1
OrderField
. का सबसे बड़ा मान है समूह में।
कोई रैंक नहीं, कोई सबक्वायरी नहीं। यदि आपके पास (GroupId, OrderField)
पर एक कंपाउंड इंडेक्स है, तो यह तेजी से चलना चाहिए और "इंडेक्स का उपयोग करना" के साथ t2 तक पहुंच को अनुकूलित करना चाहिए। ।
प्रदर्शन के संबंध में, मेरा उत्तर देखें प्रत्येक समूह में अंतिम रिकॉर्ड प्राप्त करना . मैंने स्टैक ओवरफ़्लो डेटा डंप का उपयोग करके एक सबक्वायरी विधि और जॉइन विधि की कोशिश की। अंतर उल्लेखनीय है:मेरे परीक्षण में शामिल होने का तरीका 278 गुना तेजी से चला।
सर्वोत्तम परिणाम प्राप्त करने के लिए यह महत्वपूर्ण है कि आपके पास सही अनुक्रमणिका हो!
@Rank वेरिएबल का उपयोग करने वाली आपकी विधि के संबंध में, यह काम नहीं करेगा जैसा आपने इसे लिखा है, क्योंकि क्वेरी द्वारा पहली तालिका को संसाधित करने के बाद @Rank के मान शून्य पर रीसेट नहीं होंगे। मैं आपको एक उदाहरण दिखाता हूँ।
मैंने कुछ डमी डेटा डाला, एक अतिरिक्त फ़ील्ड के साथ जो शून्य है, उस पंक्ति को छोड़कर जिसे हम जानते हैं कि प्रति समूह सबसे बड़ा है:
select * from `Table`;
+---------+------------+------+
| GroupId | OrderField | foo |
+---------+------------+------+
| 10 | 10 | NULL |
| 10 | 20 | NULL |
| 10 | 30 | foo |
| 20 | 40 | NULL |
| 20 | 50 | NULL |
| 20 | 60 | foo |
+---------+------------+------+
हम दिखा सकते हैं कि रैंक पहले समूह के लिए तीन और दूसरे समूह के लिए छह तक बढ़ जाती है, और आंतरिक क्वेरी इन्हें सही ढंग से लौटाती है:
select GroupId, max(Rank) AS MaxRank
from (
select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField) as t
group by GroupId
+---------+---------+
| GroupId | MaxRank |
+---------+---------+
| 10 | 3 |
| 20 | 6 |
+---------+---------+
अब सभी पंक्तियों के कार्टेशियन उत्पाद को बाध्य करने के लिए, बिना किसी सम्मिलित शर्त के क्वेरी चलाएं, और हम सभी कॉलम भी प्राप्त करते हैं:
select s.*, t.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
-- on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField;
+---------+---------+---------+------------+------+------+
| GroupId | MaxRank | GroupId | OrderField | foo | Rank |
+---------+---------+---------+------------+------+------+
| 10 | 3 | 10 | 10 | NULL | 7 |
| 20 | 6 | 10 | 10 | NULL | 7 |
| 10 | 3 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 30 | foo | 9 |
| 10 | 3 | 10 | 30 | foo | 9 |
| 10 | 3 | 20 | 40 | NULL | 10 |
| 20 | 6 | 20 | 40 | NULL | 10 |
| 10 | 3 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 60 | foo | 12 |
| 10 | 3 | 20 | 60 | foo | 12 |
+---------+---------+---------+------------+------+------+
हम ऊपर से देख सकते हैं कि प्रति समूह अधिकतम रैंक सही है, लेकिन फिर @Rank बढ़ती जा रही है क्योंकि यह दूसरी व्युत्पन्न तालिका को 7 और उच्चतर पर संसाधित करता है। तो दूसरी व्युत्पन्न तालिका से रैंक कभी भी पहली व्युत्पन्न तालिका से रैंक के साथ ओवरलैप नहीं होंगे।
आपको दो तालिकाओं को संसाधित करने के बीच @Rank को शून्य पर रीसेट करने के लिए बाध्य करने के लिए एक और व्युत्पन्न तालिका जोड़नी होगी (और आशा है कि अनुकूलक उस क्रम को नहीं बदलेगा जिसमें वह तालिकाओं का मूल्यांकन करता है, या फिर इसे रोकने के लिए STRAIGHT_JOIN का उपयोग करें):पी>
select s.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (select @Rank := 0) r -- RESET @Rank TO ZERO HERE
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField;
+---------+------------+------+------+
| GroupId | OrderField | foo | Rank |
+---------+------------+------+------+
| 10 | 30 | foo | 3 |
| 20 | 60 | foo | 6 |
+---------+------------+------+------+
लेकिन इस क्वेरी का अनुकूलन भयानक है। यह किसी भी इंडेक्स का उपयोग नहीं कर सकता है, यह दो अस्थायी टेबल बनाता है, उन्हें कठिन तरीके से सॉर्ट करता है, और यहां तक कि जॉइन बफर का भी उपयोग करता है क्योंकि यह अस्थायी टेबल में शामिल होने पर इंडेक्स का उपयोग नहीं कर सकता है। यह EXPLAIN
. से उदाहरण आउटपुट है :
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
| 1 | PRIMARY | <derived4> | system | NULL | NULL | NULL | NULL | 1 | Using temporary; Using filesort |
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | |
| 1 | PRIMARY | <derived5> | ALL | NULL | NULL | NULL | NULL | 6 | Using where; Using join buffer |
| 5 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 4 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
| 2 | DERIVED | <derived3> | ALL | NULL | NULL | NULL | NULL | 6 | Using temporary; Using filesort |
| 3 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
जबकि बाएं बाहरी जुड़ाव का उपयोग करके मेरा समाधान बहुत बेहतर अनुकूलन करता है। यह बिना किसी अस्थायी तालिका का उपयोग करता है और यहां तक कि "Using index"
. की रिपोर्ट भी करता है जिसका अर्थ है कि यह डेटा को छुए बिना, केवल अनुक्रमणिका का उपयोग करके शामिल होने का समाधान कर सकता है।
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 1 | SIMPLE | t2 | ref | GroupId | GroupId | 5 | test.t1.GroupId | 1 | Using where; Using index |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
आपने शायद लोगों को अपने ब्लॉग पर यह दावा करते हुए पढ़ा होगा कि "जुड़ने से SQL धीमा हो जाता है," लेकिन यह बकवास है। खराब अनुकूलन SQL को धीमा कर देता है।