समूहीकृत SQL परिणामों के प्रत्येक समूह के लिए अधिकतम मूल्य के साथ रिकॉर्ड प्राप्त करें


229

आप उन पंक्तियों को कैसे प्राप्त करते हैं जिनमें प्रत्येक समूहीकृत सेट के लिए अधिकतम मूल्य होता है?

मैंने इस प्रश्न पर कुछ अधिक जटिल बदलाव देखे हैं, और एक अच्छे उत्तर के साथ नहीं। मैंने सबसे सरल संभव उदाहरण को एक साथ रखने की कोशिश की है:

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

Person | Group | Age
---
Bob  | 1     | 32  
Jill | 1     | 34  
Shawn| 1     | 42  
Jake | 2     | 29  
Paul | 2     | 36  
Laura| 2     | 39  

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

Shawn | 1     | 42    
Laura | 2     | 39  

3
सावधानी: स्वीकृत उत्तर 2012 में काम किया गया था जब यह लिखा गया था। हालाँकि, यह अब कई कारणों से काम नहीं करता है, जैसा कि टिप्पणियों में दिया गया है।
रिक जेम्स

जवाबों:


132

Mysql में ऐसा करने का एक सुपर-सरल तरीका है:

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

यह काम करता है क्योंकि mysql में आपको गैर-समूह-स्तंभों को एकत्रित नहीं करने दिया जाता है, इस स्थिति में mysql सिर्फ पहली पंक्ति देता है। समाधान पहले डेटा को क्रमबद्ध करना है जैसे कि प्रत्येक समूह के लिए जो पंक्ति आप चाहते हैं वह पहले है, फिर उन कॉलमों के आधार पर समूह जिनके लिए आप मूल्य चाहते हैं।

आप जटिल उपश्रेणियों से बचते हैं जो max()आदि को खोजने की कोशिश करते हैं , और कई पंक्तियों को वापस करने की समस्या भी होती है जब एक ही अधिकतम मूल्य के साथ एक से अधिक हो (जैसा कि अन्य उत्तर करेंगे)

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

संस्करण 5.7 अद्यतन:

5.7 संस्करण के बाद से, sql-modeसेटिंग शामिल हैONLY_FULL_GROUP_BY डिफ़ॉल्ट रूप से है, इसलिए यह काम करने के लिए आपके पास यह विकल्प नहीं होना चाहिए (इस सेटिंग को हटाने के लिए सर्वर के लिए विकल्प फ़ाइल को संपादित करें)।


66
"mysql सिर्फ पहली पंक्ति लौटाता है।" - शायद यह है कि यह कैसे काम करता है लेकिन इसकी गारंटी नहीं है। प्रलेखन कहते हैं: "सर्वर प्रत्येक समूह से किसी भी मूल्य चुनने के लिए स्वतंत्र है, इसलिए जब तक वे एक ही हैं, मूल्यों चुना अनिश्चित हैं।" । सर्वर प्रत्येक कॉलम या एक्सप्रेशन के लिए पंक्तियों लेकिन मानों (समान पंक्ति से जरूरी नहीं) का चयन करता है जो कि SELECTक्लॉज में दिखाई देता है और एक समग्र फ़ंक्शन का उपयोग करके गणना नहीं की जाती है।
axiac

16
यह व्यवहार MySQL 5.7.5 में बदल गया और डिफ़ॉल्ट रूप से, यह इस क्वेरी को अस्वीकार कर देता है क्योंकि SELECTक्लॉज में कॉलम कार्यात्मक रूप से इस पर निर्भर नहीं हैंGROUP BY कॉलम हैं। यदि इसे स्वीकार करने के लिए इसे कॉन्फ़िगर किया गया है (`ONLY_FULL_GROUP_BY` अक्षम है), तो यह पिछले संस्करणों की तरह काम करता है (यानी उन कॉलम के मान अनिश्चित हैं)।
अक्षीय

17
मुझे आश्चर्य है कि इस उत्तर को इतने सारे उभार मिले। यह गलत है और यह बुरा है। यह क्वेरी काम करने की गारंटी नहीं है। उपखंड में डेटा खंड द्वारा आदेश के बावजूद एक अनियंत्रित सेट है। MySQL वास्तव में अब रिकॉर्ड का आदेश दे सकता है और उस आदेश को रख सकता है, लेकिन यह किसी भी नियम को तोड़ नहीं सकता है अगर यह भविष्य के संस्करण में ऐसा करना बंद कर देता है। फिर GROUP BYएक रिकॉर्ड के लिए संघनन, लेकिन सभी क्षेत्रों को रिकॉर्ड से मनमाने ढंग से चुना जाएगा। यह हो सकता है कि MySQL वर्तमान में हमेशा पहली पंक्ति चुनता है, लेकिन यह भविष्य के संस्करण में अलग-अलग पंक्तियों से किसी भी अन्य पंक्ति या यहां तक ​​कि मान भी चुन सकता है ।
थोरस्टन केटनर

9
ठीक है, हम यहाँ असहमत हैं। मैं अभी हाल ही में काम करने के लिए होने वाली अनिर्दिष्ट सुविधाओं का उपयोग नहीं करता हूं और कुछ परीक्षणों पर भरोसा करता हूं जो उम्मीद करते हैं कि इसे कवर किया जाएगा। आप जानते हैं कि आप सिर्फ भाग्यशाली हैं कि वर्तमान कार्यान्वयन आपको पूरा पहला रिकॉर्ड देता है जहां डॉक्स स्पष्ट रूप से बताता है कि आपको इसके बजाय कोई अनिश्चित मान मिल सकता है, लेकिन आप अभी भी इसका उपयोग करते हैं। कुछ साधारण सत्र या डेटाबेस सेटिंग इसे कभी भी बदल सकती है। मैं इसे बहुत जोखिम भरा मानता हूँ।
थोरस्टन केटनर

3
यह उत्तर गलत लगता है। प्रति डॉक , सर्वर प्रत्येक समूह ... इसके अलावा, प्रत्येक समूह से मूल्यों का चयन खंड एक ORDER BY जोड़ने से प्रभावित नहीं किया जा सकता से किसी भी मूल्य का चयन करने के लिए स्वतंत्र है। मान सेट किए जाने के बाद परिणाम सेट छँटाई होती है, और ORDER BY सर्वर को चुनने वाले प्रत्येक समूह के भीतर किस मूल्य को प्रभावित नहीं करता है।
TGR

296

सही समाधान है:

SELECT o.*
FROM `Persons` o                    # 'o' from 'oldest person in group'
  LEFT JOIN `Persons` b             # 'b' from 'bigger age'
      ON o.Group = b.Group AND o.Age < b.Age
WHERE b.Age is NULL                 # bigger age not found

यह काम किस प्रकार करता है:

यह स्तंभ में समान मान और स्तंभ में बड़ा मान होने oसे सभी पंक्तियों से प्रत्येक पंक्ति से मेल खाता है । कॉलम में अपने समूह का अधिकतम मूल्य नहीं होने से कोई भी पंक्ति एक या अधिक पंक्तियों से मेल खाएगीbGroupAgeoAgeb

LEFT JOINउस पर पूरा एक पंक्ति के साथ (व्यक्तियों है कि उनके समूह में अकेले हैं सहित) समूह के सबसे पुराने व्यक्ति से मेल करता है NULLसे रों b( 'समूह में कोई सबसे बड़ी उम्र')।
उपयोग करने INNER JOINसे ये पंक्तियाँ मेल नहीं खाती हैं और उन्हें अनदेखा किया जाता है।

WHEREखंड केवल होने पंक्तियों रहता NULLसे निकाला क्षेत्रों में रोंb । वे प्रत्येक समूह से सबसे पुराने व्यक्ति हैं।

आगे की पढाई

यह समाधान और कई अन्य लोगों को एसक्यूएल एंटीपैटर्नस पुस्तक में समझाया गया है : डेटाबेस प्रोग्रामिंग के नुकसान से बचना


43
BTW यह एक ही समूह के लिए दो या अधिक पंक्तियों को वापस कर सकता है o.Age = b.Age, उदाहरण के लिए यदि समूह 2 से पॉल 39 पर लौरा की तरह है। हालाँकि अगर हम ऐसा व्यवहार नहीं चाहते हैं तो हम कर सकते हैं:ON o.Group = b.Group AND (o.Age < b.Age or (o.Age = b.Age and o.id < b.id))
टोडर

8
अतुल्य! 20M रिकॉर्ड के लिए यह "भोले" एल्गोरिथ्म की तुलना में 50 गुना अधिक तेज है (अधिकतम के साथ एक
उपश्रेणी के

3
पूरी तरह से @ टिप्पणी के साथ काम करता है। मुझे लगता है कि अगर वहाँ आगे क्वेरी शर्तों वे FROM में और बाएँ जोड़ी में जोड़ा जाना चाहिए रहे हैं। कुछ पसंद: FROM (SELECT * FROM Person WHERE Age! = 32) o LEFT JOIN (Select * FROM Person WHERE Age! = 32) b - अगर आप 32 साल के लोगों को बर्खास्त करना चाहते हैं
Alain Zelink

1
@AlainZelink इन "आगे की क्वेरी शर्तों" को बेहतर नहीं बनाया जा सकता है, जो अंतिम रूप से सूची की सूची में डाल दी जाएं, ताकि उपश्रेणियों को प्रस्तुत न किया जा सके - जिन्हें मूल @ अक्षीय उत्तर की आवश्यकता नहीं थी?
tarilabs

5
इस समाधान ने काम किया; हालाँकि, यह धीमी क्वेरी लॉग में सूचित किया जाने लगा जब उसी आईडी को साझा करने वाली 10,000+ पंक्तियों के साथ प्रयास किया गया। अनुक्रमित स्तंभ पर शामिल हो रहा था। एक दुर्लभ मामला है, लेकिन यह उल्लेख के लायक है।
चेसिसबेल जुले

49

आप एक सबक्वेरी के खिलाफ जुड़ सकते हैं जो MAX(Group)और खींचती है Age। यह विधि अधिकांश आरडीबीएमएस में पोर्टेबल है।

SELECT t1.*
FROM yourTable t1
INNER JOIN
(
    SELECT `Group`, MAX(Age) AS max_age
    FROM yourTable
    GROUP BY `Group`
) t2
    ON t1.`Group` = t2.`Group` AND t1.Age = t2.max_age;

माइकल, इसके लिए धन्यवाद- लेकिन क्या आपके पास बोहेमियन की टिप्पणियों के अनुसार संबंधों पर कई पंक्तियों को वापस करने के मुद्दे का जवाब है?
यारिन

1
@ यारिन यदि उदाहरण के लिए 2 पंक्तियाँ होतीं, तो उपनगर Group = 2, Age = 20उनमें से किसी एक को वापस कर देता, लेकिन सम्मिलित ONक्लॉज़ उन दोनों से मेल खाता था, इसलिए आपको एक ही समूह / आयु के साथ 2 पंक्तियाँ वापस मिलेंगी, हालाँकि अन्य स्तंभों के लिए अलग-अलग वैलियाँ, एक के बजाय।
माइकल बेर्कोव्स्की

तो क्या हम कह रहे हैं कि जब तक हम बोहेमियन MySQL-ओनली रूट पर नहीं जाते, तब तक परिणाम को एक समूह में सीमित करना असंभव है।
यारिन

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

यह स्वीकृत उत्तर होना चाहिए (वर्तमान में स्वीकृत उत्तर अधिकांश अन्य RDBMS पर विफल हो जाएगा, और वास्तव में MySQL के कई संस्करणों पर भी विफल हो जाएगा)।
टिम बैजलेसेन

28

SQLite के लिए मेरा सरल समाधान (और शायद MySQL):

SELECT *, MAX(age) FROM mytable GROUP BY `Group`;

हालाँकि यह PostgreSQL और शायद कुछ अन्य प्लेटफार्मों में काम नहीं करता है।

PostgreSQL में आप DISTINCT ON क्लॉज का उपयोग कर सकते हैं :

SELECT DISTINCT ON ("group") * FROM "mytable" ORDER BY "group", "age" DESC;

@ बोहेमियन माफ करना, मुझे पता है, यह MySQL-only है क्योंकि इसमें नॉन-एग्रीगेटेड कॉलम शामिल हैं
Cec

2
@IgorKulagin - पोस्टग्रेज में काम नहीं करता है- त्रुटि संदेश: कॉलम "mytable.id" को ग्रुप द्वारा क्लॉज में प्रदर्शित होना चाहिए या एक समग्र समारोह में उपयोग किया जाना चाहिए
यारिन

13
MySQL क्वेरी केवल कई मौकों पर दुर्घटना से काम कर सकती है। "SELECT *" ऐसी जानकारी दे सकता है जो संबंधित MAX (आयु) के अनुरूप नहीं है। यह उत्तर गलत है। यह शायद SQLite के लिए भी मामला है।
अल्बर्ट हेंड्रिक्स

2
लेकिन यह उस मामले में फिट बैठता है जहां हमें समूहीकृत कॉलम और अधिकतम कॉलम का चयन करने की आवश्यकता होती है। यह उपरोक्त आवश्यकता के अनुरूप नहीं है जहां यह परिणाम ('बॉब', 1, 42) होगा, लेकिन अपेक्षित परिणाम है ('शॉन', 1, 42)
राम बाबू एस

1
पोस्टग्रेज के लिए अच्छा है
करोल गेसनिका

4

रैंकिंग विधि का उपयोग करना।

SELECT @rn :=  CASE WHEN @prev_grp <> groupa THEN 1 ELSE @rn+1 END AS rn,  
   @prev_grp :=groupa,
   person,age,groupa  
FROM   users,(SELECT @rn := 0) r        
HAVING rn=1
ORDER  BY groupa,age DESC,person

sel - कुछ स्पष्टीकरण की आवश्यकता है - मैंने पहले कभी नहीं देखा है :=- वह क्या है?
यारिन

1
: = असाइनमेंट ऑपरेटर है। आप dev.mysql.com/doc/refman/5.0/en/user-variables.html
sel

मुझे इस पर खुदाई करनी होगी- मुझे लगता है कि इसका उत्तर हमारे परिदृश्य को दर्शाता है, लेकिन मुझे कुछ नया सिखाने के लिए धन्यवाद ..
यारिन 2:24

3

यकीन नहीं होता कि MySQL में row_number फंक्शन है। यदि ऐसा है तो आप इसका उपयोग वांछित परिणाम प्राप्त करने के लिए कर सकते हैं। SQL सर्वर पर आप कुछ ऐसा ही कर सकते हैं:

CREATE TABLE p
(
 person NVARCHAR(10),
 gp INT,
 age INT
);
GO
INSERT  INTO p
VALUES  ('Bob', 1, 32);
INSERT  INTO p
VALUES  ('Jill', 1, 34);
INSERT  INTO p
VALUES  ('Shawn', 1, 42);
INSERT  INTO p
VALUES  ('Jake', 2, 29);
INSERT  INTO p
VALUES  ('Paul', 2, 36);
INSERT  INTO p
VALUES  ('Laura', 2, 39);
GO

SELECT  t.person, t.gp, t.age
FROM    (
         SELECT *,
                ROW_NUMBER() OVER (PARTITION BY gp ORDER BY age DESC) row
         FROM   p
        ) t
WHERE   t.row = 1;

1
यह 8.0 के बाद से करता है।
इल्जा एवरिल्ला

2

axiac का समाधान है जो अंत में मेरे लिए सबसे अच्छा काम करता है। हालांकि मेरे पास एक अतिरिक्त जटिलता थी: एक गणना "अधिकतम मूल्य", जो दो स्तंभों से प्राप्त होता है।

आइए एक ही उदाहरण का उपयोग करें: मैं प्रत्येक समूह में सबसे पुराना व्यक्ति चाहूंगा। यदि ऐसे लोग हैं जो समान रूप से पुराने हैं, तो सबसे ऊंचे व्यक्ति को लें।

इस व्यवहार को पाने के लिए मुझे दो बार लेफ्ट जॉइन करना पड़ा:

SELECT o1.* WHERE
    (SELECT o.*
    FROM `Persons` o
    LEFT JOIN `Persons` b
    ON o.Group = b.Group AND o.Age < b.Age
    WHERE b.Age is NULL) o1
LEFT JOIN
    (SELECT o.*
    FROM `Persons` o
    LEFT JOIN `Persons` b
    ON o.Group = b.Group AND o.Age < b.Age
    WHERE b.Age is NULL) o2
ON o1.Group = o2.Group AND o1.Height < o2.Height 
WHERE o2.Height is NULL;

उम्मीद है की यह मदद करेगा! मुझे लगता है कि यह करने के लिए बेहतर तरीका होना चाहिए, हालांकि ...


2

मेरा समाधान केवल तभी काम करता है जब आपको केवल एक कॉलम पुनः प्राप्त करने की आवश्यकता होती है, हालांकि मेरी जरूरतों के लिए प्रदर्शन के मामले में सबसे अच्छा समाधान मिला (यह केवल एक ही क्वेरी का उपयोग करता है!)।

SELECT SUBSTRING_INDEX(GROUP_CONCAT(column_x ORDER BY column_y),',',1) AS xyz,
   column_z
FROM table_name
GROUP BY column_z;

यह आदेशित कॉनकट सूची बनाने के लिए GROUP_CONCAT का उपयोग करता है और फिर मैं केवल पहले वाले को प्रतिस्थापित करता हूं।


पुष्टि कर सकते हैं कि आप group_concat के अंदर एक ही कुंजी पर सॉर्ट करके कई कॉलम प्राप्त कर सकते हैं, लेकिन प्रत्येक कॉलम के लिए एक अलग group_concat / index / substring लिखने की आवश्यकता है।
रसिका

यहां बोनस यह है कि आप group_concat के अंदर सॉर्ट करने के लिए कई कॉलम जोड़ सकते हैं और यह आसानी से संबंधों को हल कर देगा और आपके समूह के लिए केवल एक रिकॉर्ड की गारंटी देगा। अच्छी तरह से सरल और कुशल समाधान पर किया!
रसिका

2

मेरे पास उपयोग करके एक सरल उपाय है WHERE IN

SELECT a.* FROM `mytable` AS a    
WHERE a.age IN( SELECT MAX(b.age) AS age FROM `mytable` AS b GROUP BY b.group )    
ORDER BY a.group ASC, a.person ASC

1

CTEs का उपयोग करना - सामान्य टेबल एक्सप्रेशन:

WITH MyCTE(MaxPKID, SomeColumn1)
AS(
SELECT MAX(a.MyTablePKID) AS MaxPKID, a.SomeColumn1
FROM MyTable1 a
GROUP BY a.SomeColumn1
  )
SELECT b.MyTablePKID, b.SomeColumn1, b.SomeColumn2 MAX(b.NumEstado)
FROM MyTable1 b
INNER JOIN MyCTE c ON c.MaxPKID = b.MyTablePKID
GROUP BY b.MyTablePKID, b.SomeColumn1, b.SomeColumn2

--Note: MyTablePKID is the PrimaryKey of MyTable

1

Oracle में क्वेरी नीचे वांछित परिणाम दे सकती है।

SELECT group,person,Age,
  ROWNUMBER() OVER (PARTITION BY group ORDER BY age desc ,person asc) as rankForEachGroup
  FROM tablename where rankForEachGroup=1

0
with CTE as 
(select Person, 
[Group], Age, RN= Row_Number() 
over(partition by [Group] 
order by Age desc) 
from yourtable)`


`select Person, Age from CTE where RN = 1`

0

आप भी आजमा सकते हैं

SELECT * FROM mytable WHERE age IN (SELECT MAX(age) FROM mytable GROUP BY `Group`) ;

1
धन्यवाद, हालांकि यह एक उम्र के लिए कई रिकॉर्ड देता है जब एक टाई होता है
यारिन

साथ ही, यह प्रश्न इस मामले में गलत होगा कि समूह 1 में 39 वर्षीय व्यक्ति है। उस स्थिति में, उस व्यक्ति को भी चुना जाएगा, भले ही समूह 1 में अधिकतम आयु अधिक हो।
जोशुआ रिचर्डसन

0

मैं समूह का उपयोग स्तंभ नाम के रूप में नहीं करूंगा क्योंकि यह आरक्षित शब्द है। हालाँकि एसक्यूएल का अनुसरण करना काम करेगा।

SELECT a.Person, a.Group, a.Age FROM [TABLE_NAME] a
INNER JOIN 
(
  SELECT `Group`, MAX(Age) AS oldest FROM [TABLE_NAME] 
  GROUP BY `Group`
) b ON a.Group = b.Group AND a.Age = b.oldest

धन्यवाद, हालांकि यह एक उम्र के लिए कई रिकॉर्ड देता है जब एक टाई है
यारिन

@ यारिन कैसे तय करेगा कि सबसे सही व्यक्ति कौन है? एकाधिक उत्तर सबसे सही उत्तर प्रतीत होते हैं अन्यथा सीमा और व्यवस्था का उपयोग करें
डंकन

0

इस पद्धति का लाभ आपको एक अलग कॉलम द्वारा रैंक करने की अनुमति है, और अन्य डेटा को ट्रैश नहीं करना है। यह उस स्थिति में काफी उपयोगी है जहां आप आइटम के लिए एक कॉलम के साथ ऑर्डर को सूचीबद्ध करने की कोशिश कर रहे हैं, पहले सबसे भारी सूची।

स्रोत: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat

SELECT person, group,
    GROUP_CONCAT(
        DISTINCT age
        ORDER BY age DESC SEPARATOR ', follow up: '
    )
FROM sql_table
GROUP BY group;

0

टेबल का नाम लोगों को दें

select O.*              -- > O for oldest table
from people O , people T
where O.grp = T.grp and 
O.Age = 
(select max(T.age) from people T where O.grp = T.grp
  group by T.grp)
group by O.grp; 

0

अगर mytable से ID (और सभी coulmns) की जरूरत है

SELECT
    *
FROM
    mytable
WHERE
    id NOT IN (
        SELECT
            A.id
        FROM
            mytable AS A
        JOIN mytable AS B ON A. GROUP = B. GROUP
        AND A.age < B.age
    )

0

यह है कि मैं mysql में प्रति समूह अधिकतम N पंक्तियों को कैसे प्राप्त कर रहा हूं

SELECT co.id, co.person, co.country
FROM person co
WHERE (
SELECT COUNT(*)
FROM person ci
WHERE  co.country = ci.country AND co.id < ci.id
) < 1
;

यह काम किस प्रकार करता है:

  • आत्म तालिका में शामिल हों
  • समूहों द्वारा किया जाता है co.country = ci.country
  • ) < 13 तत्वों के लिए प्रति समूह एन तत्वों को नियंत्रित किया जाता है -) <3
  • अधिकतम या न्यूनतम पाने के लिए निम्न पर निर्भर करता है: co.id < ci.id
    • co.id <ci.id - max
    • co.id> ci.id - मिनट

पूरा उदाहरण यहाँ:

mysql प्रति समूह n अधिकतम मान का चयन करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.