GROUP BY क्लॉज़ में प्रकट होना चाहिए या एक समग्र फ़ंक्शन में उपयोग किया जाना चाहिए


276

मेरे पास एक टेबल है जो इस कॉलर 'मेकर' की तरह दिखती है

 cname  | wmname |          avg           
--------+-------------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

और मैं प्रत्येक cname के लिए अधिकतम avg का चयन करना चाहता हूं।

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

लेकिन मुझे एक त्रुटि मिलेगी,

ERROR:  column "makerar.wmname" must appear in the GROUP BY clause or be used in an   aggregate function 
LINE 1: SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

तो मैं ऐसा करता हूं

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname, wmname;

हालाँकि, यह इरादा परिणाम नहीं देगा, और नीचे गलत आउटपुट दिखाया गया है।

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

वास्तविक परिणाम होना चाहिए

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

मैं इस मुद्दे को ठीक करने के बारे में कैसे जा सकता हूं?

नोट: यह तालिका पिछले ऑपरेशन से निर्मित एक दृश्य है।



मुझे समझ नहीं आ रहा है। क्यों wmname="usopp"अपेक्षित है और उदाहरण के लिए नहीं wmname="luffy"?
आंद्रेकेआर

जवाबों:


226

हां, यह एक आम एकत्रीकरण समस्या है। SQL3 (1999) से पहले , चयनित फ़ील्ड को GROUP BYक्लॉज़ [*] में दिखाई देना चाहिए ।

इस समस्या को हल करने के लिए, आपको एक उप-क्वेरी में कुल की गणना करनी होगी और फिर अतिरिक्त स्तंभों को प्राप्त करने के लिए स्वयं से जुड़ना होगा और आपको यह बताना होगा:

SELECT m.cname, m.wmname, t.mx
FROM (
    SELECT cname, MAX(avg) AS mx
    FROM makerar
    GROUP BY cname
    ) t JOIN makerar m ON m.cname = t.cname AND t.mx = m.avg
;

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

लेकिन आप विंडो फ़ंक्शंस का उपयोग भी कर सकते हैं, जो सरल दिखता है:

SELECT cname, wmname, MAX(avg) OVER (PARTITION BY cname) AS mx
FROM makerar
;

इस पद्धति के साथ एकमात्र बात यह है कि यह सभी रिकॉर्ड दिखाएगा (विंडो फ़ंक्शन समूह नहीं है)। लेकिन यह प्रत्येक पंक्ति में देश के लिए सही (यानी अधिकतम cnameस्तर पर) दिखाएगा MAX, इसलिए यह आपके ऊपर है:

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  |     5.0000000000000000
 spain  | usopp  |     5.0000000000000000

समाधान, निश्चित रूप से कम सुरुचिपूर्ण, (cname, wmname)अधिकतम मूल्य से मेल खाते केवल टुपल्स को दिखाने के लिए , यह है:

SELECT DISTINCT /* distinct here matters, because maybe there are various tuples for the same max value */
    m.cname, m.wmname, t.avg AS mx
FROM (
    SELECT cname, wmname, avg, ROW_NUMBER() OVER (PARTITION BY avg DESC) AS rn 
    FROM makerar
) t JOIN makerar m ON m.cname = t.cname AND m.wmname = t.wmname AND t.rn = 1
;


 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

[*]: दिलचस्प रूप से पर्याप्त है, भले ही गैर-समूह वाले क्षेत्रों का चयन करने की अनुमति देता है, लेकिन प्रमुख इंजन वास्तव में इसे पसंद नहीं करते हैं। ओरेकल और SQLServer बस यह अनुमति नहीं है। मैसकल इसे डिफ़ॉल्ट रूप से अनुमति देता था, लेकिन अब 5.7 के बाद से प्रशासक को ONLY_FULL_GROUP_BYइस सुविधा को समर्थित करने के लिए सर्वर कॉन्फ़िगरेशन में इस विकल्प ( ) को मैन्युअल रूप से सक्षम करने की आवश्यकता है ...


1
धन्यवाद वाक्य रचना corect है, लेकिन, जब आप mx और avg के मूल्यों की तुलना करते हैं, तो शामिल होना चाहिए
randomGuy

1
हां, आपका सिंटैक्स सही है और डुप्लिकेट को समाप्त कर देता है, लेकिन आपको इच्छित परिणाम प्राप्त करने के लिए अंत में m.avg = t.mx की आवश्यकता होती है (बाद में आपने लिखा था)
RandomGuy

1
@ सीबास यह बिना ज्वाइन किए किया जा सकता है MAX(@ypercube द्वारा उत्तर देखें, मेरे उत्तर में भी एक और समाधान है) लेकिन आप इसे करने के तरीके को नहीं। अपेक्षित आउटपुट की जाँच करें।
शून्य 323

1
@ सबास आपका समाधान केवल एक कॉलम (अधिकतम avgप्रति cname) जोड़ता है, लेकिन यह परिणाम की पंक्तियों को प्रतिबंधित नहीं करता है (जैसा कि ओपी चाहता है)। प्रश्न में वास्तविक परिणाम पैराग्राफ होना चाहिए देखें ।
ypercube y

1
टर्निंग बंद ONLY_FULL_GROUP_BY MySQL 5.7 में जिस तरह से सक्रिय नहीं करता है SQL मानक को निर्दिष्ट जब स्तंभों से छोड़ा जा सकता है group by(या Postgres तरह MySQL व्यवहार करता है)। यह सिर्फ पुराने व्यवहार का विरोध करता है जहां MySQL यादृच्छिक (= "अनिश्चित") परिणाम देता है।
a_horse_with_no_name

126

Postgres में, आप विशेष DISTINCT ON (expression)सिंटैक्स का भी उपयोग कर सकते हैं :

SELECT DISTINCT ON (cname) 
    cname, wmname, avg
FROM 
    makerar 
ORDER BY 
    cname, avg DESC ;

5
यह काम नहीं करेगा क्योंकि यह अपेक्षित है अगर कोई avg
amenzhinsky

@amenzhinsky तुम्हारा क्या मतलब है? यदि कोई चाहता है कि परिणाम सेट को किसी भिन्न क्रम से क्रमबद्ध किया जाए BY cname?
ypercube y

@ypercube, वास्तव में psql पहले सॉर्ट करता है और फिर DISTINCT लागू होता है।
एवीजी

3
बेशक। यदि आप मेरे द्वारा पोस्ट की गई क्वेरी नहीं चलाते हैं, तो आपको अलग परिणाम मिलेंगे! यह "उम्मीद के अनुरूप काम नहीं करेगा" के समान नहीं है ...
ypercube

1
@ बाटफान thnx ध्यान दें कि जबकि यह काफी शांत, कॉम्पैक्ट और लिखने में आसान है, यह अक्सर इस तरह के प्रश्नों के लिए सबसे कुशल तरीका नहीं है।
ypercube y

27

group byचयनों में गैर-समूहीकृत और गैर-कुल क्षेत्रों को निर्दिष्ट करने में समस्या यह है कि इंजन के पास यह जानने का कोई तरीका नहीं है कि इस मामले में किस रिकॉर्ड के क्षेत्र में वापस आना चाहिए। क्या यह पहले है? क्या यह अंतिम है? आमतौर पर कोई रिकॉर्ड नहीं है जो स्वाभाविक रूप से कुल परिणाम से मेल खाता है ( minऔर maxअपवाद हैं)।

हालाँकि, एक वर्कअराउंड है: आवश्यक फ़ील्ड को भी एकत्र करें। पॉज़र्स में, यह काम करना चाहिए:

SELECT cname, (array_agg(wmname ORDER BY avg DESC))[1], MAX(avg)
FROM makerar GROUP BY cname;

ध्यान दें कि यह सभी wnames की एक सरणी बनाता है, जिसे avg द्वारा आदेश दिया गया है, और पहला तत्व लौटाता है (पोस्टग्रेज में सरणियाँ 1-आधारित हैं)।


अच्छी बात। हालांकि यह संभव लगता है कि गैर-एग्रीगेट फ़ील्ड को प्रत्येक पंक्ति से एग्रीगेटेड परिणाम में जोड़ने के लिए डीबी एक बाहरी जुड़ाव कर सकता है जिसमें पंक्ति ने योगदान दिया। मैं अक्सर उत्सुक हूं कि उनके पास इसके लिए कोई विकल्प क्यों नहीं है। हालाँकि मैं इस विकल्प से अनभिज्ञ हो सकता था :)
बेन सिमंस

16
SELECT t1.cname, t1.wmname, t2.max
FROM makerar t1 JOIN (
    SELECT cname, MAX(avg) max
    FROM makerar
    GROUP BY cname ) t2
ON t1.cname = t2.cname AND t1.avg = t2.max;

rank() विंडो फ़ंक्शन का उपयोग करना :

SELECT cname, wmname, avg
FROM (
    SELECT cname, wmname, avg, rank() 
    OVER (PARTITION BY cname ORDER BY avg DESC)
    FROM makerar) t
WHERE rank = 1;

ध्यान दें

या तो प्रति समूह में कई अधिकतम मूल्यों को संरक्षित करेगा। यदि आप प्रति समूह में केवल एकल रिकॉर्ड चाहते हैं, भले ही अधिकतम के बराबर एवीजी के साथ एक से अधिक रिकॉर्ड हो तो आपको @ ypercube के उत्तर की जांच करनी चाहिए।


16

मेरे लिए, यह एक "आम एकत्रीकरण समस्या" के बारे में नहीं है, बल्कि एक गलत SQL क्वेरी के बारे में है। "प्रत्येक cname के लिए अधिकतम औसत चुनें ..." के लिए एकल सही उत्तर है

SELECT cname, MAX(avg) FROM makerar GROUP BY cname;

परिणाम होगा:

 cname  |      MAX(avg)
--------+---------------------
 canada | 2.0000000000000000
 spain  | 5.0000000000000000

यह परिणाम सामान्य रूप से प्रश्न का उत्तर देता है "प्रत्येक समूह के लिए सबसे अच्छा परिणाम क्या है?" । हम देखते हैं कि स्पैन के लिए सबसे अच्छा परिणाम 5 है और कैनाडा के लिए सबसे अच्छा परिणाम है। यह सच है, और कोई त्रुटि नहीं है। यदि हमें wmname भी प्रदर्शित करने की आवश्यकता है , तो हमें इस प्रश्न का उत्तर देना होगा: " निर्धारित मूल्य से wm चुनें का नियम क्या है ?" आइए गलती को स्पष्ट करने के लिए इनपुट डेटा को थोड़ा बदलें:

  cname | wmname |        avg           
--------+--------+-----------------------
 spain  | zoro   |  1.0000000000000000
 spain  | luffy  |  5.0000000000000000
 spain  | usopp  |  5.0000000000000000

आप इस क्वेरी पर रननिग पर किस परिणाम की उम्मीद करते हैं SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;:? यह होना चाहिए spain+luffyया spain+usopp? क्यों? यह क्वेरी में निर्धारित नहीं है कि कैसे "बेहतर" wmname का चयन करें यदि कई उपयुक्त हैं, इसलिए परिणाम भी निर्धारित नहीं किया गया है। यही कारण है कि SQL दुभाषिया एक त्रुटि देता है - क्वेरी सही नहीं है।

दूसरे शब्द में, इस सवाल का कोई सही उत्तर नहीं है कि spain"समूह में सबसे अच्छा कौन है ?" । Luffy usopp से बेहतर नहीं है, क्योंकि usopp में समान "स्कोर" है।


इस समाधान ने मेरे लिए भी काम किया। मुझे क्वेरी की समस्या थी क्योंकि मेरे ORM में संबद्ध प्राथमिक कुंजी भी शामिल थी, जिसके परिणामस्वरूप निम्नलिखित गलत क्वेरी थी: SELECT cname, id, MAX(avg) FROM makerar GROUP BY cname;जिसने यह भ्रामक त्रुटि दी थी।
रॉबर्टो

1

यह भी काम करने लगता है

SELECT *
FROM makerar m1
WHERE m1.avg = (SELECT MAX(avg)
                FROM makerar m2
                WHERE m1.cname = m2.cname
               )

0

मैं हाल ही में इस समस्या में चला गया, जब उपयोग करने की कोशिश कर रहा था case when, और पाया कि आदेश whichऔर countबयानों को बदलना समस्या को हल करता है:

SELECT date(dateday) as pick_day,
COUNT(CASE WHEN (apples = 'TRUE' OR oranges 'TRUE') THEN fruit END)  AS fruit_counter

FROM pickings

GROUP BY 1

उपयोग करने के बजाय - उत्तरार्द्ध में, जहां मुझे त्रुटियां मिलीं कि सेब और संतरे को समग्र कार्यों में दिखाई देना चाहिए

CASE WHEN ((apples = 'TRUE' OR oranges 'TRUE') THEN COUNT(*) END) END AS fruit_counter

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