प्रत्येक समूह में अंतिम रिकॉर्ड प्राप्त करना - MySQL


952

एक तालिका messagesहै जिसमें नीचे दिखाए अनुसार डेटा है:

Id   Name   Other_Columns
-------------------------
1    A       A_data_1
2    A       A_data_2
3    A       A_data_3
4    B       B_data_1
5    B       B_data_2
6    C       C_data_1

यदि मैं कोई क्वेरी चलाता हूं select * from messages group by name, तो मुझे इसका परिणाम मिलेगा:

1    A       A_data_1
4    B       B_data_1
6    C       C_data_1

निम्नलिखित परिणाम क्या क्वेरी लौटाएगा?

3    A       A_data_3
5    B       B_data_2
6    C       C_data_1

यही है, प्रत्येक समूह में अंतिम रिकॉर्ड वापस किया जाना चाहिए।

वर्तमान में, यह मेरे द्वारा उपयोग की जाने वाली क्वेरी है:

SELECT
  *
FROM (SELECT
  *
FROM messages
ORDER BY id DESC) AS x
GROUP BY name

लेकिन यह अत्यधिक अक्षम दिखता है। उसी परिणाम को प्राप्त करने के लिए कोई अन्य तरीके?


2
अधिक कुशल समाधान के लिए stackoverflow.com/questions/1379565/… में स्वीकृत उत्तर देखें
पलक

का डुप्लीकेट stackoverflow.com/q/121387/684229
टीएमएस

7
आप DESC क्यों नहीं जोड़ सकते, यानी DESC
किम प्रिंस


2
@KimPrince ऐसा लगता है कि आप जो सुझाव दे रहे हैं वह वैसा नहीं है जैसा कि अपेक्षित है! मैंने सिर्फ आपके तरीके की कोशिश की और इसने प्रत्येक समूह के लिए FIRST रो ले लिया और DESC का आदेश दिया। यह प्रत्येक समूह की अंतिम पंक्ति को नहीं लेता है
एयरात

जवाबों:


966

MySQL 8.0 अब विंडोिंग फ़ंक्शंस का समर्थन करता है, जैसे लगभग सभी लोकप्रिय SQL कार्यान्वयन। इस मानक वाक्यविन्यास के साथ, हम सबसे बड़ा n-per-group प्रश्न लिख सकते हैं:

WITH ranked_messages AS (
  SELECT m.*, ROW_NUMBER() OVER (PARTITION BY name ORDER BY id DESC) AS rn
  FROM messages AS m
)
SELECT * FROM ranked_messages WHERE rn = 1;

नीचे मूल उत्तर मैंने इस प्रश्न के लिए 2009 में लिखा था:


मैं इस तरह से समाधान लिखता हूं:

SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
 ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;

प्रदर्शन के बारे में, आपके डेटा की प्रकृति के आधार पर, एक समाधान या दूसरा बेहतर हो सकता है। इसलिए आपको दोनों प्रश्नों का परीक्षण करना चाहिए और अपने डेटाबेस को दिए गए प्रदर्शन में बेहतर होना चाहिए।

उदाहरण के लिए, मेरे पास स्टैकऑवरफ्लो अगस्त डेटा डंप की एक प्रति है । मैं बेंचमार्किंग के लिए उपयोग करूँगा। Postsतालिका में 1,114,357 पंक्तियाँ हैं । यह My मैक प्रो 2.40GHz पर MySQL 5.0.75 पर चल रहा है ।

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

पहले तकनीक का उपयोग करके सब-वे में दिखाया गया है GROUP BY:

SELECT p1.postid
FROM Posts p1
INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostid
            FROM Posts pi GROUP BY pi.owneruserid) p2
  ON (p1.postid = p2.maxpostid)
WHERE p1.owneruserid = 20860;

1 row in set (1 min 17.89 sec)

यहां तक ​​कि EXPLAINविश्लेषण में 16 सेकंड लगते हैं:

+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| id | select_type | table      | type   | possible_keys              | key         | key_len | ref          | rows    | Extra       |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL                       | NULL        | NULL    | NULL         |   76756 |             | 
|  1 | PRIMARY     | p1         | eq_ref | PRIMARY,PostId,OwnerUserId | PRIMARY     | 8       | p2.maxpostid |       1 | Using where | 
|  2 | DERIVED     | pi         | index  | NULL                       | OwnerUserId | 8       | NULL         | 1151268 | Using index | 
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
3 rows in set (16.09 sec)

अब का उपयोग कर एक ही क्वेरी परिणाम उपज मेरी तकनीक के साथ LEFT JOIN:

SELECT p1.postid
FROM Posts p1 LEFT JOIN posts p2
  ON (p1.owneruserid = p2.owneruserid AND p1.postid < p2.postid)
WHERE p2.postid IS NULL AND p1.owneruserid = 20860;

1 row in set (0.28 sec)

EXPLAINविश्लेषण से पता चलता है कि दोनों तालिकाओं उनके अनुक्रमित उपयोग कर सकते हैं:

+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| id | select_type | table | type | possible_keys              | key         | key_len | ref   | rows | Extra                                |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
|  1 | SIMPLE      | p1    | ref  | OwnerUserId                | OwnerUserId | 8       | const | 1384 | Using index                          | 
|  1 | SIMPLE      | p2    | ref  | PRIMARY,PostId,OwnerUserId | OwnerUserId | 8       | const | 1384 | Using where; Using index; Not exists | 
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
2 rows in set (0.00 sec)

यहाँ मेरी Postsतालिका के लिए DDL है :

CREATE TABLE `posts` (
  `PostId` bigint(20) unsigned NOT NULL auto_increment,
  `PostTypeId` bigint(20) unsigned NOT NULL,
  `AcceptedAnswerId` bigint(20) unsigned default NULL,
  `ParentId` bigint(20) unsigned default NULL,
  `CreationDate` datetime NOT NULL,
  `Score` int(11) NOT NULL default '0',
  `ViewCount` int(11) NOT NULL default '0',
  `Body` text NOT NULL,
  `OwnerUserId` bigint(20) unsigned NOT NULL,
  `OwnerDisplayName` varchar(40) default NULL,
  `LastEditorUserId` bigint(20) unsigned default NULL,
  `LastEditDate` datetime default NULL,
  `LastActivityDate` datetime default NULL,
  `Title` varchar(250) NOT NULL default '',
  `Tags` varchar(150) NOT NULL default '',
  `AnswerCount` int(11) NOT NULL default '0',
  `CommentCount` int(11) NOT NULL default '0',
  `FavoriteCount` int(11) NOT NULL default '0',
  `ClosedDate` datetime default NULL,
  PRIMARY KEY  (`PostId`),
  UNIQUE KEY `PostId` (`PostId`),
  KEY `PostTypeId` (`PostTypeId`),
  KEY `AcceptedAnswerId` (`AcceptedAnswerId`),
  KEY `OwnerUserId` (`OwnerUserId`),
  KEY `LastEditorUserId` (`LastEditorUserId`),
  KEY `ParentId` (`ParentId`),
  CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`PostTypeId`) REFERENCES `posttypes` (`PostTypeId`)
) ENGINE=InnoDB;

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

2
कुछ परीक्षण किया। एक छोटी सी मेज पर (~ 300k रिकॉर्ड, ~ 190k समूह, इसलिए बड़े पैमाने पर समूह या कुछ भी नहीं), क्वेरीज़ बंधे (प्रत्येक 8 सेकंड)।
एरिक

1
@BillKarwin: देखें meta.stackexchange.com/questions/123017 , विशेष रूप से एडम Rackis 'जवाब नीचे टिप्पणी। यदि आप नए प्रश्न पर अपने उत्तर को पुनः प्राप्त करना चाहते हैं तो मुझे बताएं।
रॉबर्ट हार्वे

3
<=यदि आपके पास कोई गैर-अद्वितीय स्तंभ है , तो @Tim, नहीं, मदद नहीं करेगा। आपको टाईब्रेकर के रूप में एक अद्वितीय कॉलम का उपयोग करना चाहिए।
बिल कारविन

2
जब पंक्तियों की संख्या बढ़ जाती है या समूह बड़े हो जाते हैं, तो प्रदर्शन तेजी से कम हो जाता है। उदाहरण के लिए 5 तिथियों वाले एक समूह में 4 + 3 + 2 + 1 + 1 = 11 पंक्तियाँ होंगी जो बाईं ओर से जुड़ती हैं जिसमें से एक पंक्ति अंत में फ़िल्टर की जाती है। समूहीकृत परिणामों के साथ जुड़ने का प्रदर्शन लगभग रैखिक है। आपके परीक्षण त्रुटिपूर्ण दिखते हैं।
सलमान ए

145

UPD: 2017-03-31, MySQL के संस्करण 5.7.5 ने ONLY_FULL_GROUP_BY स्विच को डिफ़ॉल्ट रूप से सक्षम बनाया (इसलिए, गैर-निर्धारक समूह द्वारा प्रश्न अक्षम हो गए)। इसके अलावा, उन्होंने ग्रुप बाय कार्यान्वयन को अपडेट किया और समाधान अक्षम स्विच के साथ भी अपेक्षा के अनुरूप काम नहीं कर सकता है। एक को जांचने की जरूरत है।

बिल कार्विन के ऊपर का समाधान ठीक काम करता है जब समूहों के भीतर आइटम की गिनती छोटी होती है, लेकिन समूह के बड़े होने पर क्वेरी का प्रदर्शन खराब हो जाता है, क्योंकि समाधान के लिए n*n/2 + n/2केवल IS NULLतुलना की आवश्यकता होती है ।

मैंने समूहों के 18684446साथ पंक्तियों की एक InnoDB तालिका पर अपने परीक्षण किए 1182। तालिका में कार्यात्मक परीक्षणों के लिए वृषण शामिल हैं और (test_id, request_id)प्राथमिक कुंजी के रूप में है। इस प्रकार, test_idएक समूह है और मैं request_idप्रत्येक के लिए अंतिम खोज रहा था test_id

मेरे dell e4310 पर बिल का समाधान पहले ही कई घंटों से चल रहा है और मुझे नहीं पता कि यह कब खत्म होगा, हालांकि यह एक कवरेज इंडेक्स पर काम करता है (इसलिए using indexEXPLAIN में)।

मेरे पास कुछ अन्य समाधान हैं जो समान विचारों पर आधारित हैं:

  • यदि अंतर्निहित सूचकांक BTREE सूचकांक है (जो आमतौर पर मामला है), सबसे बड़ी (group_id, item_value)जोड़ी प्रत्येक के भीतर अंतिम मूल्य है group_id, group_idअगर हम अवरोही क्रम में सूचकांक से गुजरते हैं तो यह प्रत्येक के लिए पहला है ;
  • यदि हम उन मूल्यों को पढ़ते हैं जो एक सूचकांक द्वारा कवर किए गए हैं, तो मूल्य सूचकांक के क्रम में पढ़े जाते हैं;
  • प्रत्येक सूचकांक में स्पष्ट रूप से प्राथमिक कुंजी कॉलम सम्‍मिलित होते हैं (जो कि प्राथमिक कुंजी कवरेज इंडेक्स में है)। नीचे दिए गए समाधानों में, मैं प्राथमिक कुंजी पर सीधे काम करता हूं, आपके मामले में, आपको परिणाम में प्राथमिक कुंजी कॉलम जोड़ना होगा।
  • कई मामलों में यह आवश्यक है कि उप-पंक्ति में आवश्यक पंक्ति आईडी जमा करने के लिए और आईडी पर उप-वर्ग के परिणाम में शामिल होने के लिए सस्ता हो। चूंकि सबक्वेरी परिणाम में प्रत्येक पंक्ति के लिए MySQL को प्राथमिक कुंजी के आधार पर एक एकल लाने की आवश्यकता होगी, इसलिए सबक्वेरी को पहले ज्वाइन किया जाएगा और उप-पंक्ति में आईडी के क्रम में पंक्तियाँ आउटपुट होंगी (यदि हम स्पष्ट आदेश प्राप्त करते हैं) शामिल होने के लिए)

MySQL इंडेक्स का उपयोग करने के 3 तरीके कुछ विवरणों को समझने के लिए एक शानदार लेख है।

समाधान 1

यह एक अविश्वसनीय रूप से तेज़ है, मेरी 18M + पंक्तियों पर लगभग 0,8 सेकंड लगते हैं:

SELECT test_id, MAX(request_id) AS request_id
FROM testresults
GROUP BY test_id DESC;

यदि आप ASC को ऑर्डर बदलना चाहते हैं, तो इसे एक सबकुछ में डाल दें, केवल आईडी लौटाएँ और उपयोग करें कि सबक्वेरी को बाकी कॉलम में शामिल होने के लिए:

SELECT test_id, request_id
FROM (
    SELECT test_id, MAX(request_id) AS request_id
    FROM testresults
    GROUP BY test_id DESC) as ids
ORDER BY test_id;

यह मेरे डेटा पर लगभग 1,2 सेकंड लेता है।

समाधान २

यहाँ एक और समाधान है जो मेरी मेज के लिए लगभग 19 सेकंड लेता है:

SELECT test_id, request_id
FROM testresults, (SELECT @group:=NULL) as init
WHERE IF(IFNULL(@group, -1)=@group:=test_id, 0, 1)
ORDER BY test_id DESC, request_id DESC

यह परीक्षण को अवरोही क्रम में भी लौटाता है। यह बहुत धीमा है क्योंकि यह एक पूर्ण सूचकांक स्कैन करता है, लेकिन यह यहां आपको एक विचार देने के लिए है कि प्रत्येक समूह के लिए एन अधिकतम पंक्तियों को कैसे आउटपुट किया जाए।

क्वेरी का नुकसान यह है कि इसका परिणाम क्वेरी कैश द्वारा कैश नहीं किया जा सकता है।


कृपया अपनी तालिकाओं के एक डंप से लिंक करें ताकि लोग अपने प्लेटफार्मों पर इसका परीक्षण कर सकें।
पचेरियर

3
समाधान 1 काम नहीं कर सकता है, आप
अनुरोध का

2
@ gi।, यह उत्तर 5 साल पुराना है। MySQL 5.7.5 तक ONLY_FULL_GROUP_BY डिफ़ॉल्ट रूप से अक्षम था और इस समाधान ने बॉक्स से बाहर काम किया dev.mysql.com/doc/relnotes/mysql/5.7/en/… । अब मुझे यकीन नहीं है कि यदि आप मोड को अक्षम करते हैं तो समाधान अभी भी काम करता है, क्योंकि ग्रुप बीवाई के कार्यान्वयन को बदल दिया गया है।
न्यूटावर

यदि आप पहले समाधान में एएससी चाहते हैं, तो क्या यह अधिकतम काम करेगा यदि आप अधिकतम एमआईएएन को बदल दें?
जिन

@JinIzzraeel, आपके पास प्रत्येक समूह के शीर्ष पर डिफ़ॉल्ट रूप से MIN है (यह कवरिंग इंडेक्स का क्रम है): SELECT test_id, request_id FROM testresults GROUP BY test_id;प्रत्येक test_id के लिए न्यूनतम request_id लौटाएगा।
newtover

101

सही समूह को वापस करने के लिए अपनी अधीनता का उपयोग करें , क्योंकि आप वहां आधे रास्ते पर हैं।

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

select
    a.*
from
    messages a
    inner join 
        (select name, max(id) as maxid from messages group by name) as b on
        a.id = b.maxid

यदि यह idआप का अधिकतम नहीं है:

select
    a.*
from
    messages a
    inner join 
        (select name, max(other_col) as other_col 
         from messages group by name) as b on
        a.name = b.name
        and a.other_col = b.other_col

इस तरह, आप अपनी उप-श्रेणियों में सहसंबद्ध उपश्रेणियों और / या आदेश देने से बचते हैं, जो बहुत धीमी / अक्षम होती हैं।


1
के साथ समाधान के लिए एक चेतावनी पर ध्यान दें other_col: यदि वह कॉलम अद्वितीय नहीं है name, तो आपको उसी के साथ कई रिकॉर्ड वापस मिल सकते हैं , यदि वे इसके लिए टाई करते हैं max(other_col)। मुझे यह पद मिला जो मेरी जरूरतों के लिए एक समाधान का वर्णन करता है, जहां मुझे प्रति एक रिकॉर्ड की आवश्यकता है name
एरिक सिमोनटॉन

कुछ स्थितियों में आप केवल इस समाधान का उपयोग कर सकते हैं लेकिन स्वीकार किए जाते हैं।
tom10271 2

मेरे अनुभव में, यह संपूर्ण लानत संदेश तालिका को समूहीकृत कर रहा है जो धीमा / अक्षम है! दूसरे शब्दों में, ध्यान दें कि उपशम को एक पूर्ण तालिका स्कैन की आवश्यकता होती है, और उस पर बूट करने के लिए एक समूहीकरण करता है ... जब तक कि आपका अनुकूलक कुछ ऐसा नहीं कर रहा है जो मेरा नहीं है। तो यह समाधान पूरी तालिका को स्मृति में रखने पर निर्भर करता है।
तिमो

उन से लाभ होगा INDEX(name, id)औरINDEX(name, other_col)
रिक जेम्स

55

मैं एक अलग समाधान पर पहुंचा, जो प्रत्येक समूह के भीतर अंतिम पोस्ट के लिए आईडी प्राप्त करना है, फिर एक WHERE x INनिर्माण के लिए तर्क के रूप में पहली क्वेरी से परिणाम का उपयोग करके संदेश तालिका से चुनें :

SELECT id, name, other_columns
FROM messages
WHERE id IN (
    SELECT MAX(id)
    FROM messages
    GROUP BY name
);

मुझे नहीं पता कि यह कुछ अन्य समाधानों की तुलना में कैसा प्रदर्शन करता है, लेकिन इसने मेरी तालिका के लिए 3+ मिलियन पंक्तियों के साथ शानदार काम किया। (1200+ परिणामों के साथ 4 सेकंड का निष्पादन)

यह MySQL और SQL सर्वर दोनों पर काम करना चाहिए।


बस सुनिश्चित करें कि आपके पास (नाम, आईडी) पर एक सूचकांक है।
शमूएल undसलंद

1
बहुत बेहतर है कि स्वयं से जुड़ता है
एवरज

मैंने आपसे कुछ सीखा जो एक अच्छी नौकरी है और यह क्वेरी तेज़ है
हम्फ्री

33

उप क्वेरी फिडल लिंक द्वारा समाधान

select * from messages where id in
(select max(id) from messages group by Name)

समाधान हालत बेला लिंक से जुड़ें

select m1.* from messages m1 
left outer join messages m2 
on ( m1.id<m2.id and m1.name=m2.name )
where m2.id is null

इस पोस्ट का कारण केवल फिडल लिंक देना है। वही एसक्यूएल पहले से ही अन्य उत्तरों में प्रदान किया गया है।


1
@AlexanderSuraphel mysql5.5 अभी फिडल में उपलब्ध नहीं है, फिडल लिंक का उपयोग करके बनाया गया था। अब एक दिन fiddle mysql5.6 का समर्थन करता है, मैंने डेटाबेस को mysql 5.6 में बदल दिया और मैं स्कीमा का निर्माण और sql चलाने में सक्षम हूं।
विपिन

8

काफी गति के साथ एक दृष्टिकोण इस प्रकार है।

SELECT * 
FROM messages a
WHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)

परिणाम

Id  Name    Other_Columns
3   A   A_data_3
5   B   B_data_2
6   C   C_data_1

यह मान लिया idजाता है कि आपको इसकी आवश्यकता है। सामान्य स्थिति में कुछ अन्य कॉलम की जरूरत होती है।
रिक जेम्स

6

यहाँ दो सुझाव दिए गए हैं। सबसे पहले, अगर mysql ROW_NUMBER () का समर्थन करता है, तो यह बहुत आसान है:

WITH Ranked AS (
  SELECT Id, Name, OtherColumns,
    ROW_NUMBER() OVER (
      PARTITION BY Name
      ORDER BY Id DESC
    ) AS rk
  FROM messages
)
  SELECT Id, Name, OtherColumns
  FROM messages
  WHERE rk = 1;

मैं "लास्ट" से अनुमान लगा रहा हूं कि आपका मतलब आईडी क्रम में अंतिम है। यदि नहीं, तो ROW_NUMBER () विंडो के अनुसार ORDER BY क्लॉज को बदल दें। यदि ROW_NUMBER () उपलब्ध नहीं है, तो यह एक और समाधान है:

दूसरा, यदि यह नहीं है, तो यह अक्सर आगे बढ़ने का एक अच्छा तरीका है:

SELECT
  Id, Name, OtherColumns
FROM messages
WHERE NOT EXISTS (
  SELECT * FROM messages as M2
  WHERE M2.Name = messages.Name
  AND M2.Id > messages.Id
)

दूसरे शब्दों में, उन संदेशों का चयन करें जहाँ समान नाम वाला कोई बाद का आईडी संदेश नहीं है।


8
MySQL ROW_NUMBER () या CTE's का समर्थन नहीं करता है।
बिल कारविन

1
MySQL 8.0 (और MariaDB 10.2) अब समर्थन ROW_NUMBER()और CTEs।
रिक जेम्स

6

मैंने अभी तक बड़े DB के साथ परीक्षण नहीं किया है, लेकिन मुझे लगता है कि यह तालिकाओं में शामिल होने से तेज हो सकता है:

SELECT *, Max(Id) FROM messages GROUP BY Name

14
यह मनमाना डेटा लौटाता है। दूसरे शब्दों में, लौटे हुए कॉलम MAX (Id) वाले रिकॉर्ड से नहीं हो सकते हैं।
नुकसान

WHERE की स्थिति वाले रिकॉर्ड के सेट से अधिकतम Id का चयन करने के लिए उपयोगी: "Select Max (Id) FROM Prod WHERE Pn = '" + Pn + "" "यह उसी Pn.In # # रिकॉर्ड के सेट से अधिकतम Id देता है। परिणाम प्राप्त करने के लिए Reader.GetString (0) का उपयोग करें
निकोला

5

यहां GROUP_CONCATऑर्डर के साथ अंतिम संबंधित रिकॉर्ड प्राप्त करने और SUBSTRING_INDEXसूची में से एक रिकॉर्ड लेने का एक और तरीका है

SELECT 
  `Id`,
  `Name`,
  SUBSTRING_INDEX(
    GROUP_CONCAT(
      `Other_Columns` 
      ORDER BY `Id` DESC 
      SEPARATOR '||'
    ),
    '||',
    1
  ) Other_Columns 
FROM
  messages 
GROUP BY `Name` 

उपरोक्त क्वेरी समूह को Other_Columnsएक ही Nameसमूह में ORDER BY id DESCशामिल करेगी और उपयोग करने वाले सभी Other_Columnsएक विशिष्ट समूह में शामिल होंगे जो मेरे द्वारा उपयोग किए गए मेरे मामले में विभाजक के साथ अवरोही क्रम में शामिल होंगे ||, SUBSTRING_INDEXइस सूची का उपयोग करके पहले एक को चुना जाएगा

फिडल डेमो


इस बात से अवगत रहें कि group_concat_max_lenआप कितनी पंक्तियों को संभाल सकते हैं।
रिक जेम्स

5

स्पष्ट रूप से एक ही परिणाम प्राप्त करने के कई अलग-अलग तरीके हैं, आपका प्रश्न यह प्रतीत होता है कि MySQL में प्रत्येक समूह में अंतिम परिणाम प्राप्त करने का एक प्रभावी तरीका क्या है। यदि आप भारी मात्रा में डेटा के साथ काम कर रहे हैं और मान रहे हैं कि आप MySQL के नवीनतम संस्करणों (जैसे 5.7.21 और 8.0.4-आरसी) के साथ भी इनोबीडी का उपयोग कर रहे हैं, तो ऐसा करने का एक कारगर तरीका नहीं हो सकता है।

हमें कभी-कभी 60 मिलियन से अधिक पंक्तियों वाली तालिकाओं के साथ भी ऐसा करने की आवश्यकता होती है।

इन उदाहरणों के लिए, मैं केवल लगभग 1.5 मिलियन पंक्तियों के साथ डेटा का उपयोग करूंगा जहां डेटा में सभी समूहों के लिए प्रश्नों को परिणाम खोजने की आवश्यकता होगी। हमारे वास्तविक मामलों में हमें अक्सर लगभग 2,000 समूहों से डेटा वापस करने की आवश्यकता होती है (जो काल्पनिक रूप से बहुत अधिक डेटा की जांच करने की आवश्यकता नहीं होगी)।

मैं निम्नलिखित तालिकाओं का उपयोग करूंगा:

CREATE TABLE temperature(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT, 
  groupID INT UNSIGNED NOT NULL, 
  recordedTimestamp TIMESTAMP NOT NULL, 
  recordedValue INT NOT NULL,
  INDEX groupIndex(groupID, recordedTimestamp), 
  PRIMARY KEY (id)
);

CREATE TEMPORARY TABLE selected_group(id INT UNSIGNED NOT NULL, PRIMARY KEY(id)); 

तापमान तालिका लगभग 1.5 मिलियन यादृच्छिक रिकॉर्ड और 100 विभिन्न समूहों के साथ आबादी है। चयनित_ समूह उन 100 समूहों के साथ आबाद है (हमारे मामलों में यह सामान्य रूप से सभी समूहों के लिए 20% से कम होगा)।

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

यदि काल्पनिक रूप से MySQL में एक अंतिम () फ़ंक्शन होता है जो एक विशेष ORDER BY क्लॉज में अंतिम पंक्ति से मान लौटाता है तो हम बस यह कर सकते हैं:

SELECT 
  last(t1.id) AS id, 
  t1.groupID, 
  last(t1.recordedTimestamp) AS recordedTimestamp, 
  last(t1.recordedValue) AS recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
ORDER BY t1.recordedTimestamp, t1.id
GROUP BY t1.groupID;

जो केवल इस मामले में कुछ 100 पंक्तियों की जांच करने की आवश्यकता होगी क्योंकि यह किसी भी सामान्य ग्रुप BY फ़ंक्शन का उपयोग नहीं करता है। यह 0 सेकंड में निष्पादित होगा और इसलिए अत्यधिक कुशल होगा। ध्यान दें कि आम तौर पर MySQL में हम एक BYDER को क्लॉज के बाद ग्रुप बाय क्लॉज के बाद देखेंगे, हालांकि इस ORDER BY क्लॉज का उपयोग ORDER को अंतिम () फंक्शन के लिए निर्धारित करने के लिए किया जाता है, अगर यह ग्रुप BY के बाद था तो यह GROUPS को ऑर्डर करेगा। यदि कोई ग्रुप बाय क्लॉज मौजूद नहीं है, तो अंतिम मान सभी दिए गए पंक्तियों में समान होगा।

हालाँकि MySQL में यह नहीं होता है, तो आइए देखें कि इसके अलग-अलग विचार क्या हैं और यह साबित करते हैं कि इनमें से कोई भी कुशल नहीं है।

उदाहरण 1

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
  SELECT t2.id
  FROM temperature t2 
  WHERE t2.groupID = g.id
  ORDER BY t2.recordedTimestamp DESC, t2.id DESC
  LIMIT 1
);

इसने 3,009,254 पंक्तियों की जांच की और 5.7.21 पर ~ 0.859 सेकंड और 8.0.4-rc पर थोड़ा लंबा समय लिया

उदाहरण 2

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM temperature t1
INNER JOIN ( 
  SELECT max(t2.id) AS id   
  FROM temperature t2
  INNER JOIN (
    SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
    FROM selected_group g
    INNER JOIN temperature t3 ON t3.groupID = g.id
    GROUP BY t3.groupID
  ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
  GROUP BY t2.groupID
) t5 ON t5.id = t1.id;

इसने 1,505,331 पंक्तियों की जांच की और 5.7.21 पर ~ 1.25 सेकंड और 8.0.4-rc पर थोड़ा लंबा समय लिया

उदाहरण 3

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM temperature t1
WHERE t1.id IN ( 
  SELECT max(t2.id) AS id   
  FROM temperature t2
  INNER JOIN (
    SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
    FROM selected_group g
    INNER JOIN temperature t3 ON t3.groupID = g.id
    GROUP BY t3.groupID
  ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
  GROUP BY t2.groupID
)
ORDER BY t1.groupID;

इसने 3,009,685 पंक्तियों की जांच की और 5.7.21 पर ~ 1.95 सेकंड और 8.0.4-rc पर थोड़ा लंबा समय लिया

उदाहरण 4

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
  SELECT max(t2.id)
  FROM temperature t2 
  WHERE t2.groupID = g.id AND t2.recordedTimestamp = (
      SELECT max(t3.recordedTimestamp)
      FROM temperature t3 
      WHERE t3.groupID = g.id
    )
);

इसने 6,137,810 पंक्तियों की जांच की और 5.7.21 पर ~ 2.2 सेकंड और 8.0.4-आरसी पर थोड़ा लंबा समय लिया

उदाहरण 5

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
  SELECT 
    t2.id, 
    t2.groupID, 
    t2.recordedTimestamp, 
    t2.recordedValue, 
    row_number() OVER (
      PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC
    ) AS rowNumber
  FROM selected_group g 
  INNER JOIN temperature t2 ON t2.groupID = g.id
) t1 WHERE t1.rowNumber = 1;

इसने 6,017,808 पंक्तियों की जांच की और 8.0.4-आरसी पर ~ 4.2 सेकंड का समय लिया

उदाहरण 6

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM (
  SELECT 
    last_value(t2.id) OVER w AS id, 
    t2.groupID, 
    last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp, 
    last_value(t2.recordedValue) OVER w AS recordedValue
  FROM selected_group g
  INNER JOIN temperature t2 ON t2.groupID = g.id
  WINDOW w AS (
    PARTITION BY t2.groupID 
    ORDER BY t2.recordedTimestamp, t2.id 
    RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  )
) t1
GROUP BY t1.groupID;

इसने 6,017,908 पंक्तियों की जांच की और 8.0.4-आरसी पर ~ 17.5 सेकंड का समय लिया

उदाहरण 7

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
LEFT JOIN temperature t2 
  ON t2.groupID = g.id 
  AND (
    t2.recordedTimestamp > t1.recordedTimestamp 
    OR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id)
  )
WHERE t2.id IS NULL
ORDER BY t1.groupID;

यह हमेशा के लिए ले रहा था इसलिए मुझे इसे मारना पड़ा।


यह एक अलग समस्या है। और समाधान एक विशाल यूनिअन ऑल क्वेरी है।
पॉल स्पीगल

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

मैं बिल्कुल गंभीर हूं। मैंने परीक्षण किया है कि अतीत में सौ समूहों के एक जोड़े के साथ। जब आपको बड़े समूहों में संबंधों को संभालने की आवश्यकता होती है, तो इष्टतम निष्पादन योजना को बाध्य करने के लिए MySQL में UNION ALL एकमात्र तरीका है। SELECT DISTINCT(groupID)तेज़ है और आपको वह सभी डेटा देगा जो आपको इस तरह की क्वेरी बनाने के लिए चाहिए। आपको क्वेरी आकार के साथ ठीक होना चाहिए, जब तक कि यह अधिक न हो जाए max_allowed_packet, जो कि MySQL 5.7 में 4MB से चूक जाता है।
पॉल स्पीगेल

5

हम देखेंगे कि आप MySQL का उपयोग समूह रिकॉर्ड के अंतिम रिकॉर्ड में कैसे कर सकते हैं। उदाहरण के लिए यदि आपके पास यह परिणाम पदों का सेट है।

id category_id post_title

1 1 Title 1

2 1 Title 2

3 1 Title 3

4 2 Title 4

5 2 Title 5

6 3 Title 6

मैं प्रत्येक श्रेणी में अंतिम पद प्राप्त करने में सक्षम होना चाहता हूं जो शीर्षक 3, शीर्षक 5 और शीर्षक 6 हैं। श्रेणी के अनुसार पदों को प्राप्त करने के लिए आप MySQL ग्रुप बाय कीबोर्ड का उपयोग करेंगे।

select * from posts group by category_id

लेकिन इस क्वेरी से वापस मिलने वाले परिणाम हैं।

id category_id post_title

1 1 Title 1

4 2 Title 4

6 3 Title 6

समूह हमेशा परिणाम सेट पर समूह में पहला रिकॉर्ड लौटाएगा।

SELECT id, category_id, post_title FROM posts WHERE id IN ( SELECT MAX(id) FROM posts GROUP BY category_id );

यह प्रत्येक समूह में उच्चतम आईडी वाले पदों को लौटाएगा।

id category_id post_title

3 1 Title 3

5 2 Title 5

6 3 Title 6

संदर्भ यहाँ क्लिक करें


4
SELECT 
  column1,
  column2 
FROM
  table_name 
WHERE id IN 
  (SELECT 
    MAX(id) 
  FROM
    table_name 
  GROUP BY column1) 
ORDER BY column1 ;

क्या आप अपने उत्तर पर थोड़ा विस्तार कर सकते हैं? आपकी क्वेरी को विजय मूल क्वेरी के लिए बेहतर क्यों माना जाता है?
जंफोह

4

यहाँ मेरा समाधान है:

SELECT 
  DISTINCT NAME,
  MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES 
FROM MESSAGE;

यह प्रति नाम नवीनतम संदेश नहीं लौटाता है। और यह सिर्फ एक अधूरा संस्करण है SELECT NAME, MAX(MESSAGES) MESSAGES FROM MESSAGE GROUP BY NAME
पॉल स्पीगेल

इसके अलावा, यह सूत्रीकरण अत्यधिक अक्षम है।
रिक जेम्स

3

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

SELECT jos_categories.title AS name,
       joined .catid,
       joined .title,
       joined .introtext
FROM   jos_categories
       INNER JOIN (SELECT *
                   FROM   (SELECT `title`,
                                  catid,
                                  `created`,
                                  introtext
                           FROM   `jos_content`
                           WHERE  `sectionid` = 6
                           ORDER  BY `id` DESC) AS yes
                   GROUP  BY `yes`.`catid` DESC
                   ORDER  BY `yes`.`created` DESC) AS joined
         ON( joined.catid = jos_categories.id )  

3

हाय @ वीजय देव अगर आपकी टेबल मैसेज में आईडी है जो कि ऑटो इन्क्रीमेंट प्राइमरी की है तो प्राइमरी की पर लेटेस्ट रिकॉर्ड के आधार पर आपकी क्वेरी को नीचे की तरह पढ़ना चाहिए:

SELECT m1.* FROM messages m1 INNER JOIN (SELECT max(Id) as lastmsgId FROM messages GROUP BY Name) m2 ON m1.Id=m2.lastmsgId

यह सबसे तेज मैंने पाया
कोर्सायर


3
SELECT * FROM table_name WHERE primary_key IN (SELECT MAX(primary_key) FROM table_name GROUP BY column_name )


2

क्या कोई तरीका है जिससे हम इस पद्धति का उपयोग किसी तालिका में डुप्लिकेट को हटाने के लिए कर सकते हैं? परिणाम सेट मूल रूप से अनूठे रिकॉर्ड का एक संग्रह है, इसलिए यदि हम परिणाम रिकॉर्ड में नहीं सभी रिकॉर्ड हटा सकते हैं, तो हम प्रभावी रूप से कोई डुप्लिकेट नहीं होगा? मैंने यह कोशिश की लेकिन mySQL ने 1093 त्रुटि दी।

DELETE FROM messages WHERE id NOT IN
 (SELECT m1.id  
 FROM messages m1 LEFT JOIN messages m2  
 ON (m1.name = m2.name AND m1.id < m2.id)  
 WHERE m2.id IS NULL)

वहाँ शायद एक अस्थायी चर करने के लिए उत्पादन को बचाने के लिए एक रास्ता है तो नहीं में (अस्थायी चर) से हटा दें? @ बहुत उपयोगी समाधान के लिए धन्यवाद।

संपादित करें: सोचें कि मुझे इसका समाधान मिला:

DROP TABLE IF EXISTS UniqueIDs; 
CREATE Temporary table UniqueIDs (id Int(11)); 

INSERT INTO UniqueIDs 
    (SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON 
    (T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison Fields  
    AND T1.ID < T2.ID) 
    WHERE T2.ID IS NULL); 

DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);

2

नीचे दिए गए प्रश्न आपके प्रश्न के अनुसार ठीक काम करेंगे।

SELECT M1.* 
FROM MESSAGES M1,
(
 SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_data
 FROM MESSAGES
 GROUP BY 1
) M2
WHERE M1.Others_data = M2.Max_Others_data
ORDER BY Others_data;

2

यदि आप प्रत्येक के लिए अंतिम पंक्ति चाहते हैं Name, तो आप प्रत्येक पंक्ति समूह को एक पंक्ति संख्या दे सकते हैं Nameऔर Idअवरोही क्रम से आदेश दे सकते हैं।

क्वेरी के

SELECT t1.Id, 
       t1.Name, 
       t1.Other_Columns
FROM 
(
     SELECT Id, 
            Name, 
            Other_Columns,
    (
        CASE Name WHEN @curA 
        THEN @curRow := @curRow + 1 
        ELSE @curRow := 1 AND @curA := Name END 
    ) + 1 AS rn 
    FROM messages t, 
    (SELECT @curRow := 0, @curA := '') r 
    ORDER BY Name,Id DESC 
)t1
WHERE t1.rn = 1
ORDER BY t1.Id;

एसक्यूएल फिडल


2

इस बारे में कैसा है:

SELECT DISTINCT ON (name) *
FROM messages
ORDER BY name, id DESC;

मेरे पास इसी तरह का मुद्दा था (कठिन पोस्टग्रेजल पर) और 1 एम रिकॉर्ड टेबल पर। यह समाधान LEFT JOIN वाले एक द्वारा उत्पादित 1.7s बनाम 44s को लेता है। मेरे मामले में मुझे NULL मानों के विरुद्ध आपके नाम फ़ील्ड के भ्रष्ट को फ़िल्टर करना पड़ा , जिसके परिणामस्वरूप 0.2 सेकंड तक और भी बेहतर प्रदर्शन हुआ।


1

यदि प्रदर्शन वास्तव में आपकी चिंता है, तो आप IsLastInGroupटाइप बीआईटी नामक तालिका पर एक नया कॉलम पेश कर सकते हैं ।

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

तो आपकी क्वेरी इस तरह दिखाई देगी:

SELECT * FROM Messages WHERE IsLastInGroup = 1

Moodle में कुछ तालिकाओं में इस तरह का एक ध्वज स्तंभ है।
लॉरेंस


0

आप गिनती करके समूह बना सकते हैं और समूह का अंतिम आइटम भी प्राप्त कर सकते हैं जैसे:

SELECT 
    user,
    COUNT(user) AS count,
    MAX(id) as last
FROM request 
GROUP BY user

0

ओरेकल क्वेरी से नीचे की उम्मीद मदद कर सकती है:

WITH Temp_table AS
(
    Select id, name, othercolumns, ROW_NUMBER() over (PARTITION BY name ORDER BY ID 
    desc)as rank from messages
)
Select id, name,othercolumns from Temp_table where rank=1

0

एक और तरीका:

प्रत्येक प्रोग्राम (अधिकतम 1 प्रोग्राम में एन गुण) के साथ अधिकतम एम 2_प्राइस के साथ उचितता खोजें:

select * from properties p
join (
    select max(m2_price) as max_price 
    from properties 
    group by program_id
) p2 on (p.program_id = p2.program_id)
having p.m2_price = max_price
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.