उस पंक्ति को लाएं जिसमें स्तंभ के लिए अधिकतम मान है


574

तालिका:

UserId, Value, Date.

मैं UserId, प्रत्येक UserId के लिए अधिकतम (दिनांक) के लिए मान प्राप्त करना चाहता हूं। यही है, प्रत्येक UserId के लिए मान जिसमें नवीनतम तारीख है। वहाँ बस SQL ​​में ऐसा करने का एक तरीका है? (अधिमानतः ओरेकल)

अपडेट: किसी भी अस्पष्टता के लिए माफी: मुझे सभी उपयोगकर्ता प्राप्त करने की आवश्यकता है। लेकिन प्रत्येक UserId के लिए, केवल वह पंक्ति जहां उस उपयोगकर्ता की नवीनतम तिथि है।


21
क्या होगा यदि किसी विशेष उपयोगकर्ता के लिए अधिकतम दिनांक मान वाली कई पंक्तियाँ हों?
डेविड एल्ड्रिज

तालिका के प्रमुख क्षेत्र क्या हैं?
वामोसेरा

नीचे दिए गए कुछ समाधान: sqlfiddle.com/#
6d4e81/

1
@ डेविड एल्ड्रिज, यह कॉलम संभवतः अद्वितीय है।
पचेरियर

जवाबों:


397

यह उन सभी पंक्तियों को पुनः प्राप्त करेगा जिसके लिए my_date कॉलम मान उस उपयोगकर्ता के लिए my_date के अधिकतम मूल्य के बराबर है। यह उपयोगकर्ता के लिए कई पंक्तियों को पुनः प्राप्त कर सकता है जहाँ अधिकतम तिथि कई पंक्तियों पर होती है।

select userid,
       my_date,
       ...
from
(
select userid,
       my_date,
       ...
       max(my_date) over (partition by userid) max_my_date
from   users
)
where my_date = max_my_date

"विश्लेषणात्मक कार्य रॉक"

संपादित करें: पहली टिप्पणी के संबंध में ...

"विश्लेषणात्मक प्रश्नों का उपयोग करना और स्वयं से जुड़ना विश्लेषणात्मक प्रश्नों के उद्देश्य को हरा देता है"

इस कोड में कोई स्व-शामिल नहीं है। इसके बजाय इनलाइन दृश्य के परिणाम पर एक विधेय रखा गया है जिसमें विश्लेषणात्मक कार्य शामिल हैं - एक बहुत अलग मामला है, और पूरी तरह से मानक अभ्यास।

"Oracle में डिफ़ॉल्ट विंडो विभाजन में पहली पंक्ति से वर्तमान एक तक है"

विंडोिंग क्लॉज केवल क्लॉज द्वारा ऑर्डर की उपस्थिति में लागू होता है। क्लॉज द्वारा कोई आदेश नहीं होने के कारण, कोई भी विंडो क्लॉज डिफ़ॉल्ट रूप से लागू नहीं किया जाता है और कोई भी स्पष्ट रूप से निर्दिष्ट नहीं किया जा सकता है।

कोड काम करता है।


38
जब 8.8 मिलियन पंक्तियों वाली तालिका पर लागू किया जाता है, तो इस क्वेरी ने प्रश्नों के आधे समय को कुछ अन्य उच्च मतदान उत्तरों में लिया।
डेरेक महार

4
किसी को भी MySQL के लिए एक लिंक पोस्ट करने की परवाह है, अगर वहाँ एक है?
Redolent

2
क्या यह वापसी डुप्लिकेट नहीं है? उदाहरण के लिए। अगर दो पंक्तियों में एक ही user_id और एक ही तिथि (जो अधिकतम होती है)।
19

2
@ जास्त्र मुझे लगता है कि प्रश्न में स्वीकार किया गया था
डेविड एल्ड्रिज

3
इसके बजाय MAX(...) OVER (...)आप ROW_NUMBER() OVER (...)(शीर्ष-एन-प्रति-समूह के लिए) या RANK() OVER (...)(महानतम-एन-प्रति-समूह के लिए) का भी उपयोग कर सकते हैं ।
MT0

441

मैं देखता हूं कि बहुत से लोग ऐसा करने के लिए उपश्रेणी या अन्य विक्रेता-विशिष्ट सुविधाओं का उपयोग करते हैं, लेकिन मैं अक्सर इस तरह की क्वेरी बिना उपश्रेणी के निम्नलिखित तरीके से करता हूं। यह सादे, मानक SQL का उपयोग करता है इसलिए इसे RDBMS के किसी भी ब्रांड में काम करना चाहिए।

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;

दूसरे शब्दों में: उस पंक्ति को लाएँ t1जहाँ से कोई अन्य पंक्ति समान न होUserId और अधिक दिनांक के ।

(मैंने पहचानकर्ता "तिथि" को परिसीमन में डाल दिया क्योंकि यह एक एसक्यूएल आरक्षित शब्द है।)

मामले में अगर t1."Date" = t2."Date", दोहरीकरण प्रकट होता है। आमतौर पर टेबल में auto_inc(seq)चाबी होती है, जैसे id। दोहरीकरण से बचने के लिए निम्न प्रकार इस्तेमाल किया जा सकता है:

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date") 
         OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;

@ फरहान से पुनः टिप्पणी:

यहां अधिक विस्तृत विवरण दिया गया है:

एक बाहरी जुड़ने के t1साथ जुड़ने का प्रयास करता है t2। डिफ़ॉल्ट रूप से, सभी परिणाम t1लौटा दिए जाते हैं, और यदि कोई मैच होता है t2, तो उसे भी लौटा दिया जाता है। यदि t2दी गई पंक्ति के लिए कोई मेल नहीं है t1, तो क्वेरी अभी भी की पंक्ति को लौटाती है t1, और NULLसभी t2स्तंभों के लिए प्लेसहोल्डर के रूप में उपयोग करती है । यह सिर्फ इतना है कि बाहरी जोड़ सामान्य रूप से कैसे काम करते हैं।

इस क्वेरी में ट्रिक यह है कि जॉइन की मैचिंग कंडीशन को ऐसे डिजाइन करें , t2जो एक समान हो userid और एक बड़ा हो date । विचार यह है कि यदि एक पंक्ति में मौजूद है , तो t2अधिक है date, तो उसके t1खिलाफ तुलना में पंक्ति उस के लिए सबसे बड़ी नहीं हो सकती dateहै userid। लेकिन अगर कोई मेल नहीं है - यानी अगर कोई पंक्ति पंक्ति से t2अधिक के साथ मौजूद नहीं dateहै t1- हम जानते हैं कि पंक्ति दी गई के लिए t1सबसे बड़ी पंक्ति थी ।dateuserid

उन मामलों में (जब कोई मिलान नहीं होता है), के कॉलम t2होंगे NULL- यहां तक ​​कि जुड़ने की स्थिति में निर्दिष्ट कॉलम भी। इसलिए हम इसका उपयोग करते हैं WHERE t2.UserId IS NULL, क्योंकि हम उन मामलों की खोज कर रहे हैं जहां कोई पंक्ति dateदी गई के लिए अधिक नहीं मिली userid


7
वाह बिल। इस समस्या का सबसे रचनात्मक समाधान मैंने देखा है। यह मेरे काफी बड़े डेटा सेट पर बहुत अच्छा प्रदर्शन है। यह सुनिश्चित करता है कि मैंने इस समाधान को देखने के लिए कई अन्य समाधानों को देखा है।
जस्टिन नोएल

36
जब 8.8 मिलियन पंक्तियों वाली तालिका पर लागू किया जाता है, तो इस क्वेरी को स्वीकृत उत्तर में लगभग दो बार लिया गया।
डेरेक महार

16
@ डेरेक: अनुकूलन आरडीबीएमएस के ब्रांड और संस्करण पर निर्भर करता है, साथ ही उपयुक्त सूचकांक, डेटा प्रकार, आदि की उपस्थिति
बिल कार्विन

7
माईएसक्यूएल पर, इस तरह की क्वेरी वास्तव में दिखाई देती है, क्योंकि यह कार्टेशियन के तालिकाओं के बीच तालमेल के परिणाम को लूप करता है, जिसके परिणामस्वरूप ओ (एन ^ 2) समय होता है। इसके बजाय सबक्वेरी पद्धति का उपयोग करके क्वेरी समय को 2.0s से 0.003 s तक कम कर दिया गया। YMMV।
जेसी

1
क्या यह उन पंक्तियों से मेल खाने के लिए अनुकूलित करने का एक तरीका है जहां तारीख किसी उपयोगकर्ता द्वारा दी गई तारीख से कम या उसके बराबर सबसे बड़ी तारीख है? उदाहरण के लिए यदि उपयोगकर्ता "23-OCT-2011" की तारीख देता है, और तालिका में "24-OCT-2011", "22-OCT-2011", "20-OCT-2011" की पंक्तियाँ शामिल हैं, तो मैं चाहता हूँ "22-OCT-2011" प्राप्त करें। अब मेरे सिर को खरोंच रहा है और थोड़ी देर के लिए इस स्निपेट को पढ़ रहा है ...
कोरी केंडल

164
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid

3
बड़ी संख्या में पंक्तियों वाली तालिका का उपयोग करके मेरे परीक्षणों में, इस समाधान को स्वीकृत उत्तर में लगभग दो बार लिया गया।
डेरेक महार

7
कृपया अपना परीक्षण दिखाएं,
रोब वैन विज्क

मैं पुष्टि करता हूं कि यह अन्य समाधानों की तुलना में बहुत तेज है
1

5
परेशानी यह है कि पूरा रिकॉर्ड वापस नहीं करता है
Used_By_Already

@ user2067753 नहीं, यह पूरा रिकॉर्ड वापस नहीं करता है। आप एक ही MAX () .. KEEP .. का प्रयोग कई कॉलमों पर कर सकते हैं, इसलिए आप अपनी ज़रूरत के सभी कॉलमों का चयन कर सकते हैं। लेकिन यह असुविधाजनक है यदि आप बड़ी संख्या में कॉलम चाहते हैं और SELECT * का उपयोग करना पसंद करेंगे।
डेव कोस्टा

51

मुझे आपके सटीक कॉलम के नाम नहीं पता हैं, लेकिन यह कुछ इस तरह होगा:

    उपयोगकर्ता का चयन करें, मूल्य
      उपयोगकर्ताओं से u1
     जहाँ तिथि = (अधिकतम का चयन करें)
                     उपयोगकर्ताओं से u2
                    जहाँ u1.userid = u2.userid)

3
शायद बहुत प्रभावशाली नहीं, स्टीव।
डेविड एल्ड्रिज

7
आप शायद Oracle क्वेरी ऑप्टिमाइज़र को कम कर रहे हैं।
राफेल डाउगर्ड 14

3
हर्गिज नहीं। यह निश्चित रूप से एक नेस्टेड लूप के साथ पूर्ण स्कैन के रूप में लागू किया जाएगा ताकि तारीखें मिल सकें। आप तालिका में पंक्तियों की संख्या के 4 गुना के क्रम में तार्किक io के बारे में बात कर रहे हैं और गैर-तुच्छ मात्रा में डेटा के लिए भयानक हैं।
डेविड एल्ड्रिज

4
FYI करें, "कुशल नहीं है, लेकिन काम करता है" "वर्क्स के समान है, लेकिन कुशल नहीं है"। हमने डिजाइन लक्ष्य के रूप में कुशल को कब छोड़ा?
डेविड एल्ड्रिज

6
+1 क्योंकि जब आपके डेटाटैब की लंबाई में लाखों पंक्तियाँ नहीं होती हैं, तो यह सबसे आसानी से समझा जाने वाला समाधान है। जब आपके पास कोड को संशोधित करने वाले सभी कौशल स्तरों के कई डेवलपर्स होते हैं, तो समझदारी अधिक महत्वपूर्ण होती है, फिर प्रदर्शन में एक सेकंड का एक अंश जो अस्वीकार्य है।
n00b

35

काम पर नहीं होने के कारण, मेरे पास ओरेकल को हाथ लगाने के लिए नहीं है, लेकिन मुझे याद है कि ओरेकल एक कॉलम में कई कॉलमों को मिलान करने की अनुमति देता है, जो कि कम से कम उन विकल्पों से बचना चाहिए जो एक सहसंबद्ध उप-वर्ग का उपयोग करते हैं, जो शायद ही कभी एक अच्छा होता है विचार।

कुछ इस तरह से, शायद (याद नहीं कर सकता है कि कॉलम सूची को छोटा किया जाना चाहिए या नहीं):

SELECT * 
FROM MyTable
WHERE (User, Date) IN
  ( SELECT User, MAX(Date) FROM MyTable GROUP BY User)

संपादित करें: बस इसे असली के लिए आज़माया:

SQL> create table MyTable (usr char(1), dt date);
SQL> insert into mytable values ('A','01-JAN-2009');
SQL> insert into mytable values ('B','01-JAN-2009');
SQL> insert into mytable values ('A', '31-DEC-2008');
SQL> insert into mytable values ('B', '31-DEC-2008');
SQL> select usr, dt from mytable
  2  where (usr, dt) in 
  3  ( select usr, max(dt) from mytable group by usr)
  4  /

U DT
- ---------
A 01-JAN-09
B 01-JAN-09

तो यह काम करता है, हालांकि कहीं और उल्लिखित कुछ नए-नुकीले सामान अधिक प्रदर्शन करने वाले हो सकते हैं।


4
यह PostgreSQL पर भी अच्छी तरह से काम करता है। और मुझे इसकी सरलता और सामान्यता पसंद है - उपश्रेणी "यहाँ मेरे मापदंड हैं", बाहरी क्वेरी कहती है "और यहाँ विवरण मैं देखना चाहता हूँ"। +1।
j_random_hacker

13

मुझे पता है कि आपने Oracle के लिए कहा था, लेकिन SQL 2005 में अब हम इसका उपयोग करते हैं:


-- Single Value
;WITH ByDate
AS (
SELECT UserId, Value, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) RowNum
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE RowNum = 1

-- Multiple values where dates match
;WITH ByDate
AS (
SELECT UserId, Value, RANK() OVER (PARTITION BY UserId ORDER BY Date DESC) Rnk
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE Rnk = 1

7

मेरे पास इसका परीक्षण करने के लिए ओरेकल नहीं है, लेकिन विश्लेषणात्मक प्रश्नों का उपयोग करने के लिए सबसे कुशल समाधान है। यह कुछ इस तरह दिखना चाहिए:

SELECT DISTINCT
    UserId
  , MaxValue
FROM (
    SELECT UserId
      , FIRST (Value) Over (
          PARTITION BY UserId
          ORDER BY Date DESC
        ) MaxValue
    FROM SomeTable
  )

मुझे संदेह है कि आप बाहरी क्वेरी से छुटकारा पा सकते हैं और आंतरिक पर अलग रख सकते हैं, लेकिन मुझे यकीन नहीं है। इस बीच मुझे पता है कि यह काम करता है।

यदि आप विश्लेषणात्मक प्रश्नों के बारे में जानना चाहते हैं, तो मैं http://www.orafaq.com/node/55 और http://www.akadia.com/services/ora_analytic_functions.html पढ़ना चाहूंगा । यहाँ संक्षिप्त सारांश है।

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

इस मामले में यहां आंतरिक क्वेरी क्या है। पूरे डेटासेट को UserId द्वारा फिर दिनांक DESC द्वारा क्रमबद्ध किया जाता है। फिर इसे एक पास में संसाधित करता है। प्रत्येक पंक्ति के लिए आप UserId लौटाते हैं और उस UserId के लिए देखी गई पहली तारीख (चूंकि तारीखें DESC को क्रमबद्ध किया जाता है, यही अधिकतम राशि है)। यह आपको डुप्लीकेट पंक्तियों के साथ अपना जवाब देता है। तब बाहरी DISTINCT स्क्वैश डुप्लिकेट होते हैं।

यह विश्लेषणात्मक प्रश्नों का विशेष रूप से शानदार उदाहरण नहीं है। बहुत बड़ी जीत के लिए वित्तीय प्राप्तियों की एक तालिका लेने और प्रत्येक उपयोगकर्ता और रसीद की गणना करने पर विचार करें, जो उन्होंने भुगतान किया था। विश्लेषणात्मक प्रश्न कुशलता से हल करते हैं। अन्य समाधान कम कुशल हैं। यही कारण है कि वे 2003 एसक्यूएल मानक का हिस्सा हैं। (दुर्भाग्य से Postgres अभी तक उनके पास नहीं है। Grrr ...)


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

सवाल बयान में तारीख लौटाने के बारे में कुछ नहीं कहा गया है। आप ऐसा कर सकते हैं कि या तो एक और FIRST (दिनांक) को जोड़कर या फिर केवल तारीख को क्वेरी करके और बाहरी क्वेरी को एक समूह BY में बदलकर। मैं पहले का उपयोग करता हूं और आशा करता हूं कि अनुकूलक दोनों को एक पास में गणना करेगा।
user11318

"प्रश्न कथन दिनांक वापस करने के बारे में कुछ नहीं कहता है" ... हाँ, आप सही कह रहे हैं। माफ़ करना। लेकिन अधिक FIRST_VALUE क्लॉस जोड़ने से यह बहुत जल्दी गड़बड़ हो जाएगा। यह एक एकल विंडो सॉर्ट है, लेकिन अगर आपके पास उस पंक्ति के लिए लौटने के लिए 20 कॉलम थे, तो आपने बहुत सारे कोड लिखे होंगे।
डेविड एल्ड्रिज

मेरे साथ यह भी होता है कि यह समाधान डेटा के लिए गैर-नियतात्मक है जहां एक एकल उपयोगकर्ता के पास कई पंक्तियाँ हैं जिनमें अधिकतम तिथि और विभिन्न VALUE हैं। हालांकि उत्तर की तुलना में प्रश्न में अधिक दोष।
डेविड एल्ड्रिज

1
मैं मानता हूं कि यह दर्दभरी क्रिया है। हालांकि यह आमतौर पर SQL के साथ मामला नहीं है? और आप सही हैं कि समाधान गैर-नियतात्मक है। संबंधों से निपटने के कई तरीके हैं, और कभी-कभी प्रत्येक वह होता है जो आप चाहते हैं।
user11318 19

6

एक प्रश्न का खंड दोनों सबसे सरल और सबसे अच्छा नहीं होगा?

select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

संदर्भ के लिए, टेराडाटा पर यहाँ इस क्वैलफी संस्करण के साथ 17 के दशक में और 23 के दशक में 'इनलाइन दृश्य' / एल्ड्रिज समाधान # 1 के साथ एक सभ्य आकार का परीक्षण चलता है।


1
मेरी राय में यह सबसे अच्छा जवाब है। हालांकि, उन rank()स्थितियों में फ़ंक्शन से सावधान रहें जहां संबंध हैं। आप एक से अधिक के साथ समाप्त कर सकते हैं rank=1। उपयोग करने के लिए बेहतर है row_number()यदि आप वास्तव में सिर्फ एक रिकॉर्ड वापस चाहते हैं।
कार्टिबेस्फोरस

1
इसके अलावा, इस बात का ध्यान रखें कि यह QUALIFYखंड टेराडाटा के लिए विशिष्ट है। ओरेकल (कम से कम) में आपको अपनी क्वेरी और फ़िल्टरिंग WHEREको रैपिंग सिलेक्ट स्टेटमेंट पर क्लॉज़ का उपयोग करना होगा (जो संभवतः प्रदर्शन को छूता है, मैं कल्पना करता हूं)।
कार्टिबेस्फोरस

5

में Oracle 12c+, आप उपयोग कर सकते हैं शीर्ष n विश्लेषणात्मक समारोह के साथ प्रश्नों rankबहुत संक्षेप में इस लक्ष्य को हासिल करने के लिए बिना सबक्वेरी:

select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;

उपर्युक्त सभी पंक्तियों को अधिकतम my_date प्रति उपयोगकर्ता के साथ लौटाता है।

यदि आप अधिकतम तिथि के साथ केवल एक पंक्ति चाहते हैं, तो इसके rankसाथ बदलें row_number:

select *
from your_table
order by row_number() over (partition by user_id order by my_date desc)
fetch first 1 row with ties; 

5

प्रत्येक के लिए ROW_NUMBER()अवरोही पर एक अद्वितीय रैंकिंग असाइन करने के लिए उपयोग करें , फिर Dateप्रत्येक के UserIdलिए पहली पंक्ति को फ़िल्टर करें UserId(यानी, ROW_NUMBER= 1)।

SELECT UserId, Value, Date
FROM (SELECT UserId, Value, Date,
        ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) rn
      FROM users) u
WHERE rn = 1;

5

PostgreSQL 8.4 या बाद में, आप इसका उपयोग कर सकते हैं:

select user_id, user_value_1, user_value_2
  from (select user_id, user_value_1, user_value_2, row_number()
          over (partition by user_id order by user_date desc) 
        from users) as r
  where r.row_number=1

3

मुझे लगता है कि आप पिछले क्वेरी के लिए इस प्रकार बना shuold बात:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

3
Select  
   UserID,  
   Value,  
   Date  
From  
   Table,  
   (  
      Select  
          UserID,  
          Max(Date) as MDate  
      From  
          Table  
      Group by  
          UserID  
    ) as subQuery  
Where  
   Table.UserID = subQuery.UserID and  
   Table.Date = subQuery.mDate  

3

बस काम पर "लाइव" उदाहरण लिखना था :)

यह एक ही तिथि पर UserId के लिए कई मानों का समर्थन करता है ।

कॉलम: UserId, मान, दिनांक

SELECT
   DISTINCT UserId,
   MAX(Date) OVER (PARTITION BY UserId ORDER BY Date DESC),
   MAX(Values) OVER (PARTITION BY UserId ORDER BY Date DESC)
FROM
(
   SELECT UserId, Date, SUM(Value) As Values
   FROM <<table_name>>
   GROUP BY UserId, Date
)

आप MAX के बजाय FIRST_VALUE का उपयोग कर सकते हैं और इसे व्याख्या योजना में देख सकते हैं। मेरे पास इसके साथ खेलने का समय नहीं था।

बेशक, अगर विशाल तालिकाओं के माध्यम से खोज करते हैं, तो शायद यह बेहतर है यदि आप अपनी क्वेरी में पूर्ण संकेत का उपयोग करते हैं।


3
select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))

2

मुझे ऐसा कुछ लगता है। (किसी भी वाक्यविन्यास गलतियों के लिए मुझे क्षमा करें; मैं इस बिंदु पर HQL का उपयोग करने के लिए उपयोग किया जाता हूं!)

संपादित करें: प्रश्न को भी गलत तरीके से पढ़ें! क्वेरी ठीक की गई ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)

"प्रत्येक UserId के लिए" शर्त को पूरा नहीं करता
डेविड एल्ड्रिज

यह कहाँ असफल होगा? उपयोगकर्ताओं में प्रत्येक UserID के लिए, यह गारंटी दी जाएगी कि कम से कम एक पंक्ति जिसमें UserID वापस आ जाएगी। या मुझे एक विशेष मामला याद आ रहा है?
jddichal

2

(टी-एसक्यूएल) पहले सभी उपयोगकर्ताओं और उनकी अधिकतम राशि प्राप्त करें। उपयोगकर्ताओं के लिए अधिकतम मानों को खोजने के लिए तालिका के साथ जुड़ें।

create table users (userid int , value int , date datetime)
insert into users values (1, 1, '20010101')
insert into users values (1, 2, '20020101')
insert into users values (2, 1, '20010101')
insert into users values (2, 3, '20030101')

select T1.userid, T1.value, T1.date 
    from users T1,
    (select max(date) as maxdate, userid from users group by userid) T2    
    where T1.userid= T2.userid and T1.date = T2.maxdate

परिणाम:

userid      value       date                                    
----------- ----------- -------------------------- 
2           3           2003-01-01 00:00:00.000
1           2           2002-01-01 00:00:00.000

2

यहाँ उत्तर ओरेकल ही है। यहाँ सभी SQL में थोड़ा और अधिक परिष्कृत उत्तर दिया गया है:

सबसे अच्छा समग्र होमवर्क परिणाम (होमवर्क पॉइंट्स की अधिकतम राशि) कौन है?

SELECT FIRST, LAST, SUM(POINTS) AS TOTAL
FROM STUDENTS S, RESULTS R
WHERE S.SID = R.SID AND R.CAT = 'H'
GROUP BY S.SID, FIRST, LAST
HAVING SUM(POINTS) >= ALL (SELECT SUM (POINTS)
FROM RESULTS
WHERE CAT = 'H'
GROUP BY SID)

और अधिक कठिन उदाहरण, जिसके लिए कुछ स्पष्टीकरण की आवश्यकता है, जिसके लिए मेरे पास समय नहीं है:

2008 में सबसे अधिक लोकप्रिय पुस्तक (ISBN और शीर्षक) दें, जो कि 2008 में सबसे अधिक बार उधार ली गई है।

SELECT X.ISBN, X.title, X.loans
FROM (SELECT Book.ISBN, Book.title, count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title) X
HAVING loans >= ALL (SELECT count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title);

आशा है कि यह (किसी को भी) मदद करता है :)

सादर, गुस


स्वीकृत उत्तर "ओरेकल ओनली" नहीं है - यह मानक एसक्यूएल (कई डीबीएमएस द्वारा समर्थित) है
a_horse_with_no_name

2

मान लिया गया दिनांक किसी दिए गए UserID के लिए अद्वितीय है, यहाँ कुछ TSQL है:

SELECT 
    UserTest.UserID, UserTest.Value
FROM UserTest
INNER JOIN
(
    SELECT UserID, MAX(Date) MaxDate
    FROM UserTest
    GROUP BY UserID
) Dates
ON UserTest.UserID = Dates.UserID
AND UserTest.Date = Dates.MaxDate 

2

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

select
    userid,
    to_number(substr(max(to_char(date,'yyyymmdd') || to_char(value)), 9)) as value,
    max(date) as date
from 
    users
group by
    userid

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


यह अधिकांश की तुलना में एक अच्छा निष्पादन योजना है, लेकिन उन सभी चालों को अधिक से अधिक लागू करना कुछ क्षेत्रों में थकाऊ होगा और इसके खिलाफ काम कर सकता है। लेकिन बहुत दिलचस्प - धन्यवाद। sqlfiddle.com/#/4/2749b5/23
Used_By_Already

आप सही हैं कि यह थकाऊ हो सकता है, यही कारण है कि यह केवल तभी किया जाना चाहिए जब क्वेरी के प्रदर्शन के लिए इसकी आवश्यकता होती है। ऐसा अक्सर ETL स्क्रिप्ट के साथ होता है।
ALevelOfIndirection

यह बहुत अच्छा है। LISTAGG का उपयोग करके कुछ ऐसा ही किया लेकिन बदसूरत लग रहा है। array_agg का उपयोग करके पोस्टग्रैज का एक बेहतर उपचारात्मक है। मेरा जवाब देखिए :)
ब्रूनो कालजा


1

मुझे लगता है कि यह काम करना चाहिए?

Select
T1.UserId,
(Select Top 1 T2.Value From Table T2 Where T2.UserId = T1.UserId Order By Date Desc) As 'Value'
From
Table T1
Group By
T1.UserId
Order By
T1.UserId

1

पहले प्रयास करें कि मैं प्रश्न को गलत समझूं, शीर्ष उत्तर के बाद, यहां सही परिणाम के साथ एक पूर्ण उदाहरण है:

CREATE TABLE table_name (id int, the_value varchar(2), the_date datetime);

INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'a','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'b','2/2/2002');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'c','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'d','3/3/2003');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'e','3/3/2003');

-

  select id, the_value
      from table_name u1
      where the_date = (select max(the_date)
                     from table_name u2
                     where u1.id = u2.id)

-

id          the_value
----------- ---------
2           d
2           e
1           b

(3 row(s) affected)

1

यह डुप्लिकेट का भी ध्यान रखेगा (प्रत्येक user_id के लिए एक पंक्ति लौटाएं):

SELECT *
FROM (
  SELECT u.*, FIRST_VALUE(u.rowid) OVER(PARTITION BY u.user_id ORDER BY u.date DESC) AS last_rowid
  FROM users u
) u2
WHERE u2.rowid = u2.last_rowid

1

बस इसका परीक्षण किया गया है और यह लॉगिंग टेबल पर काम करता है

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc

1

यह उतना ही सरल होना चाहिए:

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)

1

MySQL के लिए समाधान जिसमें KEEP, DENSE_RANK विभाजन की अवधारणाएँ नहीं हैं।

select userid,
       my_date,
       ...
from
(
select @sno:= case when @pid<>userid then 0
                    else @sno+1
    end as serialnumber, 
    @pid:=userid,
       my_Date,
       ...
from   users order by userid, my_date
) a
where a.serialnumber=0

संदर्भ: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html


यह " अन्य DBs पर भी " काम नहीं करता है । यह केवल MySQL पर और संभवतः SQL सर्वर पर काम करता है क्योंकि इसमें चर की एक समान अवधारणा है। यह निश्चित रूप से Oracle, Postgres, DB2, Derby, H2, HSQLDB, Vertica, Greenplum पर काम नहीं करेगा। इसके अतिरिक्त स्वीकृत उत्तर मानक एएनएसआई एसक्यूएल है (जो केवल MySQL का समर्थन नहीं करता है)
a_horse_with_no_name

घोड़ा, मुझे लगता है कि तुम सही हो। मुझे अन्य DBs या ANSI के बारे में जानकारी नहीं है। मेरा समाधान MySQL में समस्या को हल करने में सक्षम है, जिसमें मानक तरीके से इसे हल करने के लिए ANSI SQL के लिए उचित समर्थन नहीं है।
बेन लिन

1

आप Postgres का उपयोग कर रहे हैं, तो आप उपयोग कर सकते हैं array_aggकी तरह

SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid

मैं ओरेकल से परिचित नहीं हूँ। मैंने ये ढूंढ निकाला

SELECT 
  userid,
  MAX(adate),
  SUBSTR(
    (LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)),
    0,
    INSTR((LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)), ',')-1
  ) as value 
FROM YOURTABLE
GROUP BY userid 

दोनों प्रश्न स्वीकार किए गए उत्तर के समान परिणाम लौटाते हैं। SQLFiddles देखें:

  1. स्वीकृत उत्तर
  2. Postgres के साथ मेरा समाधान
  3. ओरेकल के साथ मेरा समाधान

0

यदि (UserID, दिनांक) अद्वितीय है, तो कोई भी तिथि एक ही उपयोगकर्ता के लिए दो बार दिखाई नहीं देती है:

select TheTable.UserID, TheTable.Value
from TheTable inner join (select UserID, max([Date]) MaxDate
                          from TheTable
                          group by UserID) UserMaxDate
     on TheTable.UserID = UserMaxDate.UserID
        TheTable.[Date] = UserMaxDate.MaxDate;

मुझे विश्वास है कि आपको प्रयोक्ता आईडी के साथ जुड़ने की आवश्यकता है
टॉम एच।

0
select   UserId,max(Date) over (partition by UserId) value from users;

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