प्रति उपयोगकर्ता नवीनतम तिथि के साथ पंक्ति का चयन करें


125

मेरे पास उपयोगकर्ताओं के चेक-इन और इस तरह दिखने वाले समय की एक तालिका ("lms_attendance") है:

id  user    time    io (enum)
1   9   1370931202  out
2   9   1370931664  out
3   6   1370932128  out
4   12  1370932128  out
5   12  1370933037  in

मैं इस तालिका का एक दृश्य बनाने की कोशिश कर रहा हूं, जो उपयोगकर्ता आईडी के अनुसार केवल सबसे हालिया रिकॉर्ड का उत्पादन करेगा, जबकि मुझे "इन" या "आउट" मान देगा, इसलिए कुछ इस प्रकार है:

id  user    time    io
2   9   1370931664  out
3   6   1370932128  out
5   12  1370933037  in

मैं अब तक बहुत करीब हूं, लेकिन मुझे एहसास हुआ कि विचार अधीनताओं को स्वीकार नहीं करेंगे, जो इसे बहुत कठिन बना रहा है। निकटतम क्वेरी मुझे मिली:

select 
    `lms_attendance`.`id` AS `id`,
    `lms_attendance`.`user` AS `user`,
    max(`lms_attendance`.`time`) AS `time`,
    `lms_attendance`.`io` AS `io` 
from `lms_attendance` 
group by 
    `lms_attendance`.`user`, 
    `lms_attendance`.`io`

लेकिन मुझे जो मिलता है वह है:

id  user    time    io
3   6   1370932128  out
1   9   1370931664  out
5   12  1370933037  in
4   12  1370932128  out

जो करीब है, लेकिन सही नहीं है। मुझे पता है कि अंतिम समूह वहां नहीं होना चाहिए, लेकिन इसके बिना, यह सबसे हालिया समय लौटाता है, लेकिन इसके सापेक्ष आईओ मूल्य नहीं है।

कोई विचार? धन्यवाद!



मैनुअल पर वापस जाएं। आप देखेंगे कि यह इस समस्या का समाधान (बिना सहसंबद्ध और अनारक्षित) उपश्रेणियों के साथ प्रदान करता है।
स्ट्रॉबेरी

@ बरमार, तकनीकी रूप से, जैसा कि मैंने अपने उत्तर में बताया है, यह सबसे बड़े-एन-प्रति-समूह टैग के साथ सभी 700 सवालों की नकल है ।
टीएमएस

@Prodikl, 'io (enum)' क्या है?
मोनिका हेडडेक

मेरे पास "IO" नामक एक कॉलम था, जो "इन या आउट" के लिए खड़ा था, यह "या" आउट "" संभव मानों के साथ एक एनुम प्रकार था। इसका उपयोग उन लोगों पर नज़र रखने के लिए किया जाता था, जब वे किसी कक्षा में और बाहर जाते थे।
कीथ

जवाबों:


199

प्रश्न:

SQLFIDDLEExample

SELECT t1.*
FROM lms_attendance t1
WHERE t1.time = (SELECT MAX(t2.time)
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user)

परिणाम:

| ID | USER |       TIME |  IO |
--------------------------------
|  2 |    9 | 1370931664 | out |
|  3 |    6 | 1370932128 | out |
|  5 |   12 | 1370933037 |  in |

समाधान जो हर काम करने वाला है:

SQLFIDDLEExample

SELECT t1.*
FROM lms_attendance t1
WHERE t1.id = (SELECT t2.id
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user            
                 ORDER BY t2.id DESC
                 LIMIT 1)

2
वाह! न केवल यह काम किया, मुझे इस क्वेरी के साथ एक दृश्य बनाने की अनुमति दी गई थी, भले ही इसमें उपश्रेणियाँ हों। इससे पहले, जब मैंने उप-श्रृंखलाओं वाला एक दृश्य बनाने की कोशिश की, तो इसने मुझे जाने नहीं दिया। क्या ऐसा करने के लिए नियम हैं, लेकिन इसकी अनुमति क्यों नहीं है?
कीथ

बहुत अजीब। अनेक अनेक धन्यवाद! शायद यह इसलिए था क्योंकि मेरी उपसमुच्चय एक छद्म तालिका थी जिसे मैं FROM का चयन कर रहा था, जहां इस उदाहरण में इसका उपयोग WHO खंड में किया गया था।
कीथ

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

आह, अंतर्दृष्टि के लिए धन्यवाद! जब मैं कल कार्यालय में हूँ तो मैं नया कोड आज़माऊँगा।
कीथ

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

73

पहिया को सुदृढ़ करने की कोशिश करने की आवश्यकता नहीं है, क्योंकि यह सामान्य रूप से सबसे बड़ी समस्या है । बहुत अच्छा समाधान प्रस्तुत किया गया है

मैं सबसे सरल समाधान पसंद करता हूं ( देखें SQLFiddle, अद्यतन जस्टिन का ) बिना उपश्रेणियों के (इस प्रकार विचारों में उपयोग करना आसान है):

SELECT t1.*
FROM lms_attendance AS t1
LEFT OUTER JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND (t1.time < t2.time 
         OR (t1.time = t2.time AND t1.Id < t2.Id))
WHERE t2.user IS NULL

यह उस मामले में भी काम करता है जहां एक ही समूह के भीतर एक ही सबसे बड़े मूल्य के साथ दो अलग-अलग रिकॉर्ड हैं - के साथ चाल के लिए धन्यवाद (t1.time = t2.time AND t1.Id < t2.Id)। मैं यहां केवल इतना कर रहा हूं कि यह सुनिश्चित करने के लिए कि जब एक ही उपयोगकर्ता के दो रिकॉर्ड एक ही समय के लिए चुने गए हों। क्या वास्तव में कोई फर्क नहीं पड़ता है अगर मानदंड Idया कुछ और है - मूल रूप से अद्वितीय होने की गारंटी देने वाले किसी भी मानदंड से यहां काम हो जाएगा।


1
अधिकतम उपयोग t1.time < t2.timeऔर मिनट होगा t1.time > t2.timeजो मेरे प्रारंभिक अंतर्ज्ञान के विपरीत है।
कोई नहीं

1
@ J.Money क्योंकि निहित निषेध छिपा हुआ है: आप t1 से सभी रिकॉर्ड का चयन करते हैं, जो t2 से संबंधित रिकॉर्ड नहीं है, जहां t1.time < t2.timeस्थिति लागू होती है :-)
TMS

4
WHERE t2.user IS NULLथोड़ा अजीब है। यह रेखा क्या भूमिका निभाती है?
tumultous_rooster

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

@ टीएमएस क्या आप OR (t1.time = t2.time AND t1.Id < t2.Id))अनुभाग को स्पष्ट कर सकते हैं ?
ओलेग कट्स

6

@ टीएमएस के उत्तर के आधार पर, मुझे यह पसंद है क्योंकि इसमें उपश्रेणियों की कोई आवश्यकता नहीं है, लेकिन मुझे लगता है कि 'OR'भाग को समझना पर्याप्त और बहुत सरल होगा समझने और पढ़ने के लिए।

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL

यदि आप अशक्त बार पंक्तियों में रुचि नहीं रखते हैं तो आप उन्हें WHEREखंड में फ़िल्टर कर सकते हैं :

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL and t1.time IS NOT NULL

ORभाग को छोड़ना वास्तव में एक बुरा विचार है यदि दो रिकॉर्ड समान हो सकते हैं time
TMS

मैं प्रदर्शन के लिए इस समाधान से बचूंगा। जैसा कि @OlegKuts ने उल्लेख किया है, यह मध्य-से-बड़े डेटा सेट पर बहुत धीमा हो जाता है।
पीटर मैडले

4

पहले से ही हल किया गया है, लेकिन सिर्फ रिकॉर्ड के लिए, दो दृष्टिकोण बनाने के लिए एक और दृष्टिकोण होगा ...

CREATE TABLE lms_attendance
(id int, user int, time int, io varchar(3));

CREATE VIEW latest_all AS
SELECT la.user, max(la.time) time
FROM lms_attendance la 
GROUP BY la.user;

CREATE VIEW latest_io AS
SELECT la.* 
FROM lms_attendance la
JOIN latest_all lall 
    ON lall.user = la.user
    AND lall.time = la.time;

INSERT INTO lms_attendance 
VALUES
(1, 9, 1370931202, 'out'),
(2, 9, 1370931664, 'out'),
(3, 6, 1370932128, 'out'),
(4, 12, 1370932128, 'out'),
(5, 12, 1370933037, 'in');

SELECT * FROM latest_io;

SQL फिडल में इसे देखने के लिए यहां क्लिक करें


1
पालन ​​करने के लिए धन्यवाद! यदि कोई आसान तरीका नहीं होता, तो मैं कई विचार पैदा करने जा रहा था। धन्यवाद फिर से
कीथ

0
select b.* from 

    (select 
        `lms_attendance`.`user` AS `user`,
        max(`lms_attendance`.`time`) AS `time`
    from `lms_attendance` 
    group by 
        `lms_attendance`.`user`) a

join

    (select * 
    from `lms_attendance` ) b

on a.user = b.user
and a.time = b.time

धन्यवाद। मुझे पता है कि मैं इसे एक उपकेंद्र का उपयोग कर सकता हूं, लेकिन मैं इसे एक दृश्य में बदलने की उम्मीद कर रहा था, और यह AFAIK को देखने में उपश्रेणियों की अनुमति नहीं देगा। क्या मुझे प्रत्येक उप क्वेरी को एक दृश्य आदि में बदलना होगा?
कीथ

join (select * from lms_attendance ) b= join lms_attendance b
अज़ेरफ़ती


0

यदि MySQL 8.0 या उच्चतर पर आप विंडो फ़ंक्शंस का उपयोग कर सकते हैं :

प्रश्न:

DBFiddleExample

SELECT DISTINCT
FIRST_VALUE(ID) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS ID,
FIRST_VALUE(USER) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS USER,
FIRST_VALUE(TIME) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS TIME,
FIRST_VALUE(IO) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS IO
FROM lms_attendance;

परिणाम:

| ID | USER |       TIME |  IO |
--------------------------------
|  2 |    9 | 1370931664 | out |
|  3 |    6 | 1370932128 | out |
|  5 |   12 | 1370933037 |  in |

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

और अगर आपका HANA चल रहा है तो यह भी ~ 7 गुना तेज है: D


-1

ठीक है, यह एक हैक या त्रुटि-प्रवण हो सकता है, लेकिन किसी तरह यह भी काम कर रहा है-

SELECT id, MAX(user) as user, MAX(time) as time, MAX(io) as io FROM lms_attendance GROUP BY id;

-2

इस प्रश्न को आज़माएं:

  select id,user, max(time), io 
  FROM lms_attendance group by user;

इस का SQLFiddle बनाने का प्रयास करें। आप संभवतः पाएंगे कि idऔर ioगैर-पृथक कॉलम हैं, जिनका उपयोग नहीं किया जा सकता है group by
डेवी मॉर्गन

1
कोई गारंटी नहीं है कि आईडी अधिकतम (समय) के साथ आईडी होगी, यह समूह के भीतर आईडी में से कोई भी हो सकता है। यह वह समस्या है जिसे मैं हल करने के लिए यहां आया था, फिर भी देख रहा हूं
रोबिसब्रोब

-3

संभवतः आप उपयोगकर्ता द्वारा समूह बना सकते हैं और फिर समय के अनुसार आर्डर कर सकते हैं। नीचे जैसा कुछ है

  SELECT * FROM lms_attendance group by user order by time desc;

-3

यह मेरे लिए काम किया:

SELECT user, time FROM 
(
    SELECT user, time FROM lms_attendance --where clause
) AS T 
WHERE (SELECT COUNT(0) FROM table WHERE user = T.user AND time > T.time) = 0
ORDER BY user ASC, time DESC
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.