तो आप 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
को लौटाएं जिसके लिए कोई अन्य पंक्ति t2
समान GroupId
और अधिक से अधिक मौजूद नहीं है OrderField
। जब t2.*
NULL होता है, तो इसका मतलब है कि बायाँ बाहरी जोड़ ऐसा कोई मिलान नहीं पाया गया है, और इसलिए समूह में इसका t1
सबसे बड़ा मूल्य है OrderField
।
कोई रैंक नहीं, कोई उपश्रेणी नहीं। यदि आपके पास एक कंपाउंड इंडेक्स है, तो इसे तेजी से रन करना चाहिए और "इंडेक्स का उपयोग करते हुए" t2 तक पहुंच को अनुकूलित करना चाहिए (GroupId, OrderField)
।
प्रदर्शन के बारे में, प्रत्येक समूह में पिछले रिकॉर्ड को पुनः प्राप्त करने के लिए मेरा उत्तर देखें । मैंने स्टैक ओवरफ्लो डेटा डंप का उपयोग करके एक सबक्वेरी विधि और ज्वाइन विधि की कोशिश की। अंतर उल्लेखनीय है: मेरे परीक्षण में शामिल होने की विधि 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
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 |
+
हम ऊपर से देख सकते हैं कि प्रति समूह अधिकतम रैंक सही है, लेकिन उसके बाद @ रेंक में वृद्धि जारी है क्योंकि यह दूसरी व्युत्पन्न तालिका को 7 और उच्चतर पर संसाधित करता है। तो दूसरी व्युत्पन्न तालिका से रैंक्स पहले व्युत्पन्न तालिका से रैंकों के साथ ओवरलैप कभी नहीं होगा।
आपको दो तालिकाओं के प्रसंस्करण के बीच शून्य पर रीसेट करने के लिए मजबूर करने के लिए एक और व्युत्पन्न तालिका को जोड़ना होगा (और आशा है कि ऑप्टिमाइज़र उस क्रम को नहीं बदलेगा जिसमें यह तालिकाओं का मूल्यांकन करता है, या फिर इसे रोकने के लिए 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
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 को धीमा बनाता है।