समूहित परिणामों के प्रत्येक समूह के लिए शीर्ष एन रिकॉर्ड प्राप्त करें


140

निम्नलिखित सबसे सरल संभव उदाहरण है, हालांकि किसी भी समाधान को बड़े पैमाने पर करने में सक्षम होना चाहिए, हालांकि कई शीर्ष परिणाम आवश्यक हैं:

नीचे दी गई तालिका की तरह, व्यक्ति, समूह और आयु कॉलम के साथ, आपको प्रत्येक समूह में 2 सबसे पुराने लोग कैसे मिलेंगे ? (समूहों के भीतर संबंधों में अधिक परिणाम नहीं होने चाहिए, लेकिन वर्णमाला क्रम में पहले 2 दें)

+ -------- + ------- + ----- +
| व्यक्ति | समूह | आयु |
+ -------- + ------- + ----- +
| बॉब | 1 | 32 |
| जिल | 1 | 34 |
| शॉन | 1 | 42 |
| जेक | 2 | 29 |
| पॉल | 2 | 36 |
| लौरा | 2 | 39 |
+ -------- + ------- + ----- +

वांछित परिणाम सेट:

+ -------- + ------- + ----- +
| शॉन | 1 | 42 |
| जिल | 1 | 34 |
| लौरा | 2 | 39 |
| पॉल | 2 | 36 |
+ -------- + ------- + ----- +

नोट: यह प्रश्न पिछले एक पर बनाता है- समूहीकृत SQL परिणामों के प्रत्येक समूह के लिए अधिकतम मूल्य के साथ रिकॉर्ड प्राप्त करें - प्रत्येक समूह से एक एकल शीर्ष पंक्ति प्राप्त करने के लिए, और जिसे @Bohemian से एक महान MySQL-विशिष्ट उत्तर मिला:

select * 
from (select * from mytable order by `Group`, Age desc, Person) x
group by `Group`

हालांकि, मैं यह नहीं देख पाऊंगा कि मैं इसे कैसे बना सकता हूं।



2
इस उदाहरण की जाँच करें। यह आपके द्वारा
सविता वेदोवा

समूह के अनुसार N परिणाम प्राप्त करने के लिए GROUP BY द्वारा सीमा का उपयोग करना? stackoverflow.com/questions/2129693/…
Edye Chan

जवाबों:


88

यहाँ इसका उपयोग करने का एक तरीका है, UNION ALL( डेमो के साथ SQL फिडेल देखें )। यह दो समूहों के साथ काम करता है, यदि आपके पास दो से अधिक समूह हैं, तो आपको groupसंख्या निर्दिष्ट करने और प्रत्येक के लिए प्रश्न जोड़ने की आवश्यकता होगी group:

(
  select *
  from mytable 
  where `group` = 1
  order by age desc
  LIMIT 2
)
UNION ALL
(
  select *
  from mytable 
  where `group` = 2
  order by age desc
  LIMIT 2
)

इसे करने के कई तरीके हैं, अपनी स्थिति के लिए सर्वोत्तम मार्ग निर्धारित करने के लिए इस लेख को देखें:

http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/

संपादित करें:

यह आपके लिए भी काम कर सकता है, यह प्रत्येक रिकॉर्ड के लिए एक पंक्ति संख्या उत्पन्न करता है। ऊपर दिए गए लिंक से एक उदाहरण का उपयोग करके केवल 2 या उससे कम की पंक्ति संख्या के साथ उन रिकॉर्डों को वापस किया जाएगा:

select person, `group`, age
from 
(
   select person, `group`, age,
      (@num:=if(@group = `group`, @num +1, if(@group := `group`, 1, 1))) row_number 
  from test t
  CROSS JOIN (select @num:=0, @group:=null) c
  order by `Group`, Age desc, person
) as x 
where x.row_number <= 2;

डेमो देखें


52
अगर उसके पास 1 000+ समूह हैं, तो क्या यह थोड़ा डरावना नहीं होगा?
चार्ल्स फॉरेस्ट

1
@CharlesForest हाँ, यह होगा और इसीलिए मैंने कहा कि आपको इसे दो से अधिक समूहों के लिए निर्दिष्ट करना होगा। यह बदसूरत हो जाएगा।
टैरिन

1
@CharlesForest मैं एक बेहतर समाधान मैंने पाया लगता है, मेरे संपादन देख
टैरिन

1
इसे पढ़ने वाले किसी भी व्यक्ति के लिए एक नोट: संस्करण वह है जो चर सही होने के करीब है। हालाँकि, MySQL अभिव्यक्तियों के मूल्यांकन के क्रम की गारंटी नहीं देता है SELECT(और, वास्तव में, कभी-कभी उन्हें आउट-ऑफ-ऑर्डर का मूल्यांकन करता है)। समाधान की कुंजी सभी चर असाइनमेंट को एक एकल अभिव्यक्ति में रखना है; यहाँ एक उदाहरण है: stackoverflow.com/questions/38535020/…
गॉर्डन लिनोफ

1
@GordonLinoff मेरे उत्तर को अपडेट करें, इसे इंगित करने के लिए धन्यवाद। मुझे इसे अपडेट करने में भी बहुत लंबा समय लगा।
टैरिन

63

अन्य डेटाबेस में आप इसका उपयोग कर सकते हैं ROW_NUMBER। MySQL समर्थन नहीं करता है ROW_NUMBERलेकिन आप इसका अनुकरण करने के लिए चर का उपयोग कर सकते हैं:

SELECT
    person,
    groupname,
    age
FROM
(
    SELECT
        person,
        groupname,
        age,
        @rn := IF(@prev = groupname, @rn + 1, 1) AS rn,
        @prev := groupname
    FROM mytable
    JOIN (SELECT @prev := NULL, @rn := 0) AS vars
    ORDER BY groupname, age DESC, person
) AS T1
WHERE rn <= 2

इसे ऑनलाइन काम करते देखें: sqlfiddle


संपादित करें मैंने अभी देखा कि ब्लूफेट ने एक बहुत ही समान उत्तर पोस्ट किया: +1 उसे। हालाँकि इस उत्तर के दो छोटे फायदे हैं:

  1. यह एक एकल क्वेरी है। चरों का चयन कथन के अंदर आरंभ किया जाता है।
  2. यह प्रश्न में वर्णित संबंधों (नाम से वर्णानुक्रम) को संभालता है।

तो मैं इसे यहाँ छोड़ दूँगा अगर यह किसी की मदद कर सकता है।


1
मार्क- यह हमारे लिए अच्छा काम कर रहा है। एक और अच्छा विकल्प प्रदान करने के लिए धन्यवाद @ ब्लूफेट की बहुत सराहना की गई।
यारिन

+1। इसने मेरे लिए काम किया। वास्तव में साफ और बिंदु जवाब के लिए। क्या आप बता सकते हैं कि यह कैसे काम करता है? क्या इसके पीछे तर्क है?
आदित्य हजारे

3
अच्छा समाधान है, लेकिन ऐसा लगता है कि यह मेरे वातावरण (MySQL 5.6) में काम नहीं कर रहा है, क्योंकि खंड द्वारा आदेश का चयन करने के बाद लागू किया जाता है, इसलिए यह शीर्ष परिणाम वापस नहीं करता है, इस मुद्दे को ठीक करने के लिए मेरा वैकल्पिक समाधान देखें
लॉरेंट PELE

इसे चलाते समय मैं हटाने में सक्षम था JOIN (SELECT @prev := NULL, @rn := 0) AS vars। मुझे लगता है कि विचार खाली चर घोषित करने के लिए है, लेकिन यह MySql के लिए बाहरी है।
जोसेफ चो

1
यह MySQL 5.7 में मेरे लिए बहुत अच्छा काम करता है, लेकिन यह बहुत बढ़िया होगा यदि कोई यह समझा सके कि यह कैसे काम करता है
जॉर्ज बी

41

इसे इस्तेमाल करे:

SELECT a.person, a.group, a.age FROM person AS a WHERE 
(SELECT COUNT(*) FROM person AS b 
WHERE b.group = a.group AND b.age >= a.age) <= 2 
ORDER BY a.group ASC, a.age DESC

डेमो


6
सबसे आसान समाधान के साथ कहीं से भी आ रहा है सूंघ! क्या यह लूडो / बिल करविन की तुलना में अधिक सुंदर है ? क्या मुझे कुछ टिप्पणी मिल सकती है
यारिन

हम्म, यकीन नहीं है कि यह अधिक सुरुचिपूर्ण है। लेकिन वोटों को देखते हुए, मुझे लगता है कि ब्लूफेट का बेहतर समाधान हो सकता है।
18

2
इसके साथ एक समस्या है। यदि समूह के भीतर दूसरे स्थान के लिए एक टाई है, तो केवल एक शीर्ष परिणाम लौटाया जाता है। संशोधित डेमो
यारिन

2
यह एक समस्या नहीं है अगर यह वांछित है। का क्रम निर्धारित कर सकते हैं a.person
अल्बर्टो Leal

नहीं, यह मेरे मामले में काम नहीं कर रहा है, न ही डेमो काम करता है
चोइस

31

स्व-जुड़ने का उपयोग कैसे करें:

CREATE TABLE mytable (person, groupname, age);
INSERT INTO mytable VALUES('Bob',1,32);
INSERT INTO mytable VALUES('Jill',1,34);
INSERT INTO mytable VALUES('Shawn',1,42);
INSERT INTO mytable VALUES('Jake',2,29);
INSERT INTO mytable VALUES('Paul',2,36);
INSERT INTO mytable VALUES('Laura',2,39);

SELECT a.* FROM mytable AS a
  LEFT JOIN mytable AS a2 
    ON a.groupname = a2.groupname AND a.age <= a2.age
GROUP BY a.person
HAVING COUNT(*) <= 2
ORDER BY a.groupname, a.age DESC;

मुझे देता है:

a.person    a.groupname  a.age     
----------  -----------  ----------
Shawn       1            42        
Jill        1            34        
Laura       2            39        
Paul        2            36      

मैं बिल करविन के उत्तर से प्रत्येक श्रेणी के लिए शीर्ष 10 रिकॉर्ड का चयन करने के लिए दृढ़ता से प्रेरित था

इसके अलावा, मैं SQLite का उपयोग कर रहा हूं, लेकिन यह MySQL पर काम करना चाहिए।

एक और बात: उपरोक्त में, मैंने सुविधा के लिए groupएक groupnameकॉलम के साथ कॉलम को बदल दिया ।

संपादित करें :

गायब टाई परिणामों के बारे में ओपी की टिप्पणी के बाद, मैंने सभी संबंधों को दिखाने के लिए स्नफ़िन के उत्तर पर वृद्धि की। इसका मतलब है कि यदि अंतिम वाले संबंध हैं, तो 2 से अधिक पंक्तियों को वापस किया जा सकता है, जैसा कि नीचे दिखाया गया है:

.headers on
.mode column

CREATE TABLE foo (person, groupname, age);
INSERT INTO foo VALUES('Paul',2,36);
INSERT INTO foo VALUES('Laura',2,39);
INSERT INTO foo VALUES('Joe',2,36);
INSERT INTO foo VALUES('Bob',1,32);
INSERT INTO foo VALUES('Jill',1,34);
INSERT INTO foo VALUES('Shawn',1,42);
INSERT INTO foo VALUES('Jake',2,29);
INSERT INTO foo VALUES('James',2,15);
INSERT INTO foo VALUES('Fred',1,12);
INSERT INTO foo VALUES('Chuck',3,112);


SELECT a.person, a.groupname, a.age 
FROM foo AS a 
WHERE a.age >= (SELECT MIN(b.age)
                FROM foo AS b 
                WHERE (SELECT COUNT(*)
                       FROM foo AS c
                       WHERE c.groupname = b.groupname AND c.age >= b.age) <= 2
                GROUP BY b.groupname)
ORDER BY a.groupname ASC, a.age DESC;

मुझे देता है:

person      groupname   age       
----------  ----------  ----------
Shawn       1           42        
Jill        1           34        
Laura       2           39        
Paul        2           36        
Joe         2           36        
Chuck       3           112      

@ Ludo- बस ऐसे ही देखा विधेयक Karwin से जवाब - यह यहां आवेदन करने के लिए धन्यवाद
Yarin

स्नफ़िन के उत्तर से आप क्या समझते हैं? मैं दोनों की तुलना करने की कोशिश कर रहा हूं
यारिन

2
इसके साथ एक समस्या है। यदि समूह के भीतर दूसरे स्थान के लिए एक टाई है, तो केवल एक शीर्ष परिणाम लौटाया जाता
यारिन

1
@ लुडो- मूल आवश्यकता यह थी कि प्रत्येक समूह सटीक n परिणाम लौटाए, किसी भी संबंध को वर्णानुक्रम से हल किया जाए
यारिन

संबंधों को शामिल करने का संपादन मेरे लिए काम नहीं करता है। मुझे लगता है ERROR 1242 (21000): Subquery returns more than 1 row, शायद की वजह से GROUP BY। जब मैं SELECT MINअकेले उपश्रम को अंजाम देता हूं , तो यह तीन पंक्तियों को उत्पन्न करता है: 34, 39, 112और यह प्रतीत होता है कि दूसरा मूल्य 36 होना चाहिए, 39 नहीं।
क्रिया

12

जब आप बहुत पंक्तियों और मार्क बायर्स / रिक जेम्स और ब्लूफेट समाधानों को प्राप्त कर लेते हैं तो Snuffin सॉल्यूशन निष्पादित करने में काफी धीमा लगता है, यह मेरे environnement (MySQL 5.6) पर काम नहीं करता है क्योंकि ऑर्डर सिलेक्ट होने के बाद लागू होता है, इसलिए यहाँ एक वेरिएंट है मार्क बायर्स / रिक जेम्स समाधान इस समस्या को हल करने के लिए (अतिरिक्त imbricated चयन के साथ):

select person, groupname, age
from
(
    select person, groupname, age,
    (@rn:=if(@prev = groupname, @rn +1, 1)) as rownumb,
    @prev:= groupname 
    from 
    (
        select person, groupname, age
        from persons 
        order by groupname ,  age desc, person
    )   as sortedlist
    JOIN (select @prev:=NULL, @rn :=0) as vars
) as groupedlist 
where rownumb<=2
order by groupname ,  age desc, person;

मैंने 5 लाख पंक्तियों वाली तालिका पर समान क्वेरी की कोशिश की और यह 3 सेकंड से भी कम समय में परिणाम देता है


3
यह एकमात्र क्वेरी है जो मेरे वातावरण में काम कर रही है। धन्यवाद!
हेररहर

3
LIMIT 9999999एक के साथ किसी भी व्युत्पन्न तालिका में जोड़ें ORDER BY। यह हो सकता है को रोकने ORDER BYअनदेखा किया जा रहा से।
रिक जेम्स

मैंने कुछ हजार पंक्तियों वाली एक मेज पर एक समान क्वेरी चलाई, और एक परिणाम को वापस करने के लिए 60 सेकंड लगे, इसलिए ... पोस्ट के लिए धन्यवाद, यह मेरे लिए एक शुरुआत है। (ईटीए: नीचे 5 सेकंड तक। अच्छा!)
इवान

10

इसकी जांच करें:

SELECT
  p.Person,
  p.`Group`,
  p.Age
FROM
  people p
  INNER JOIN
  (
    SELECT MAX(Age) AS Age, `Group` FROM people GROUP BY `Group`
    UNION
    SELECT MAX(p3.Age) AS Age, p3.`Group` FROM people p3 INNER JOIN (SELECT MAX(Age) AS Age, `Group` FROM people GROUP BY `Group`) p4 ON p3.Age < p4.Age AND p3.`Group` = p4.`Group` GROUP BY `Group`
  ) p2 ON p.Age = p2.Age AND p.`Group` = p2.`Group`
ORDER BY
  `Group`,
  Age DESC,
  Person;

एसक्यूएल फिडल: http://sqlfiddle.com/#/2/cdbb6/15


5
यार, दूसरों को बहुत सरल समाधान मिला ... मैंने इस पर सिर्फ 15 मिनट बिताए और इस तरह के जटिल समाधान के साथ आने के लिए मुझे खुद पर गर्व था। वह चूसता है।
Travesty3

मुझे एक आंतरिक संस्करण संख्या ढूंढनी थी जो वर्तमान से 1 कम थी - इससे मुझे ऐसा करने का उत्तर मिला: max(internal_version - 1)- इतना तनाव कम :)
जेमी स्ट्रॉस

8

यदि अन्य उत्तर पर्याप्त नहीं हैं, तो इस कोड को आज़माएं:

SELECT
        province, n, city, population
    FROM
      ( SELECT  @prev := '', @n := 0 ) init
    JOIN
      ( SELECT  @n := if(province != @prev, 1, @n + 1) AS n,
                @prev := province,
                province, city, population
            FROM  Canada
            ORDER BY
                province   ASC,
                population DESC
      ) x
    WHERE  n <= 3
    ORDER BY  province, n;

आउटपुट:

+---------------------------+------+------------------+------------+
| province                  | n    | city             | population |
+---------------------------+------+------------------+------------+
| Alberta                   |    1 | Calgary          |     968475 |
| Alberta                   |    2 | Edmonton         |     822319 |
| Alberta                   |    3 | Red Deer         |      73595 |
| British Columbia          |    1 | Vancouver        |    1837970 |
| British Columbia          |    2 | Victoria         |     289625 |
| British Columbia          |    3 | Abbotsford       |     151685 |
| Manitoba                  |    1 | ...

आपकी साइट पर देखा - मुझे शहरों की आबादी के लिए डेटा स्रोत कहां मिलेगा? टीआईए और आरजीएस।
वेयर्स

maxmind.com/en/worldcities - मुझे यह अव्यक्त / lng खोजों , प्रश्नों, विभाजन आदि के साथ प्रयोग करने में आसान लगता है , यह दिलचस्प होने के लिए काफी बड़ा है, फिर भी जवाबों को पहचानने के लिए पर्याप्त पठनीय है। इस तरह के प्रश्न के लिए कनाडाई सबसेट सबसे आसान है। (अमेरिका के शहरों की तुलना में कम प्रांत।)
रिक जेम्स

2

मैं इसे साझा करना चाहता था क्योंकि मैंने एक जावा प्रोग्राम में इसे लागू करने के लिए एक आसान तरीका खोजने के लिए एक लंबा समय बिताया। यह वह आउटपुट नहीं देता है जिसकी आप तलाश कर रहे हैं लेकिन इसके करीब है। Mysql में फ़ंक्शन GROUP_CONCAT()ने यह निर्दिष्ट करने के लिए वास्तव में अच्छी तरह से काम किया कि प्रत्येक समूह में कितने परिणाम वापस आए। मेरे LIMITसाथ COUNTकाम करने की कोशिश कर रहे किसी भी अन्य फैंसी तरीके का उपयोग करना या करना मेरे लिए नहीं था। इसलिए यदि आप एक संशोधित आउटपुट को स्वीकार करने के लिए तैयार हैं, तो यह एक महान समाधान है। बता दें कि मेरे पास छात्र आईडी, उनके लिंग और gpa के साथ 'छात्र' नामक एक तालिका है। आइए बताते हैं कि मैं प्रत्येक लिंग के लिए 5 gpas को शीर्ष पर रखना चाहता हूं। फिर मैं क्वेरी को इस तरह लिख सकता हूं

SELECT sex, SUBSTRING_INDEX(GROUP_CONCAT(cast(gpa AS char ) ORDER BY gpa desc), ',',5) 
AS subcategories FROM student GROUP BY sex;

ध्यान दें कि पैरामीटर '5' यह बताता है कि प्रत्येक पंक्ति में कितनी प्रविष्टियाँ सम्मिलित हैं

और आउटपुट कुछ ऐसा दिखेगा

+--------+----------------+
| Male   | 4,4,4,4,3.9    |
| Female | 4,4,3.9,3.9,3.8|
+--------+----------------+

आप ORDER BYचर को भी बदल सकते हैं और उन्हें अलग तरीके से ऑर्डर कर सकते हैं । इसलिए अगर मेरे पास छात्र की उम्र होती तो मैं 'gpa desc' को 'desc' से बदल सकता था और यह काम करेगा! आउटपुट में अधिक कॉलम प्राप्त करने के लिए आप स्टेटमेंट द्वारा ग्रुप में वैरिएबल भी जोड़ सकते हैं। तो यह सिर्फ एक तरीका है जो मैंने पाया कि यह काफी लचीला है और यदि आप केवल लिस्टिंग के परिणाम के साथ ठीक हैं तो यह अच्छा काम करता है।


0

SQL सर्वर row_numer()में एक शक्तिशाली फ़ंक्शन है जो नीचे के रूप में आसानी से परिणाम प्राप्त कर सकता है

select Person,[group],age
from
(
select * ,row_number() over(partition by [group] order by age desc) rn
from mytable
) t
where rn <= 2

8.0 और 10.2 जीए होने के साथ, यह उत्तर उचित हो रहा है।
रिक जेम्स

@ रिकजम्स 'जीए' होने का क्या मतलब है? विंडो फ़ंक्शंस ( dev.mysql.com/doc/refman/8.0/en/window-functions.html ) ने मेरी समस्या को बहुत अच्छे से हल किया।
iedmrc

1
@iedmrc - "GA" का अर्थ है "आम तौर पर उपलब्ध"। यह "प्राइम टाइम के लिए तैयार", या "रिलीज़" के लिए टेक-स्पीक है। वे संस्करण विकसित करने के माध्यम से हैं और बग पर ध्यान केंद्रित करेंगे कि वे चूक गए। यह लिंक MySQL 8.0 के कार्यान्वयन पर चर्चा करता है, जो कि MariaDB 10.2 के कार्यान्वयन से भिन्न हो सकता है।
रिक जेम्स

-1

MySQL पर इस समस्या का बहुत अच्छा जवाब है - प्रत्येक समूह के लिए शीर्ष एन पंक्तियां कैसे प्राप्त करें

संदर्भित लिंक में समाधान के आधार पर, आपकी क्वेरी निम्न होगी:

SELECT Person, Group, Age
   FROM
     (SELECT Person, Group, Age, 
                  @group_rank := IF(@group = Group, @group_rank + 1, 1) AS group_rank,
                  @current_group := Group 
       FROM `your_table`
       ORDER BY Group, Age DESC
     ) ranked
   WHERE group_rank <= `n`
   ORDER BY Group, Age DESC;

कहाँ nहै top nऔर your_tableआपकी तालिका का नाम है।

मुझे लगता है कि संदर्भ में स्पष्टीकरण वास्तव में स्पष्ट है। त्वरित संदर्भ के लिए मैं इसे यहां कॉपी और पेस्ट करूंगा:

वर्तमान में MySQL ROW_NUMBER () फ़ंक्शन का समर्थन नहीं करता है जो किसी समूह के भीतर अनुक्रम संख्या निर्दिष्ट कर सकता है, लेकिन वर्कअराउंड के रूप में हम MySQL सत्र चर का उपयोग कर सकते हैं।

इन चरों को घोषणा की आवश्यकता नहीं होती है, और इसका उपयोग गणना करने और मध्यवर्ती परिणामों को संग्रहीत करने के लिए क्वेरी में किया जा सकता है।

@current_country: = देश इस कोड को प्रत्येक पंक्ति के लिए निष्पादित किया जाता है और देश स्तंभ के मान को @current_country चर में संग्रहीत करता है।

@country_rank: = IF (@current_country = country, @country_rank + 1, 1) इस कोड में, अगर @current_country समान है तो हम वेतन वृद्धि रैंक देते हैं, अन्यथा इसे 1 पर सेट करें। पहली पंक्ति के लिए @current_country NULL है, इसलिए रैंक रैंक है। 1 पर भी सेट।

सही रैंकिंग के लिए, हमें ORDER BY देश, जनसंख्या DESC की आवश्यकता होगी


खैर, यह मार्क बायर्स, रिक जेम्स और मेरा के समाधान द्वारा उपयोग किया जाने वाला सिद्धांत है।
लॉरेंट PELE

यह कहना मुश्किल है कि कौन सा पद (स्टैक ओवरफ्लो या एसक्यूलाइन्स) पहला था
लॉरेंट पीएलई

@LaurentPELE - मेरा फरवरी, 2015 को पोस्ट किया गया था। मुझे SQLlines पर कोई टाइमस्टैम्प या नाम दिखाई नहीं देता है। MySQL ब्लॉग काफी लंबे समय से रहे हैं कि उनमें से कुछ पुराने हैं, और उन्हें हटा दिया जाना चाहिए - लोग गलत जानकारी उद्धृत कर रहे हैं।
रिक जेम्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.