Search code examples
mysqlmessage

Mysql - select last row for each user


this is my query :

 SELECT msgId as `ID`,msgFromUserId , msgToUserId ,  
            DATE_FORMAT( msgDate,'%d/%m/%y %H:%i') as `time` ,  MID(msgText,1,30) as `text` , 
            (CASE WHEN (msgFromUserId=292646) then  b.user_login else  a.user_login END) as `sender`
            FROM tbl_messages inner join wp_users as a on tbl_messages.msgFromUserId=a.ID 
            inner join wp_users as b on tbl_messages.msgToUserId=b.ID 
            inner join tbl_forum_users u1 on tbl_messages.msgFromUserId=u1.user_ID 
            inner join tbl_forum_users u2 on tbl_messages.msgToUserId=u2.user_ID 
where (msgFromUserId=292646 or msgToUserId=292646)
and tbl_messages.msgId in (SELECT max(msgId) FROM tbl_messages   GROUP BY msgFromUserId, msgToUserId )
order by msgId desc

i get this :

result table

I dont want duplication row. Just get the last line of conversation between them


Solution

  • A fast fix could be to change your subquery

    SELECT max(msgId) FROM tbl_messages   GROUP BY msgFromUserId, msgToUserId
    

    to

    SELECT max(msgId) 
    FROM tbl_messages
    GROUP BY LEAST(msgFromUserId, msgToUserId), GREATEST(msgFromUserId, msgToUserId)
    

    This will group messages from 292646 to 1 and from 1 to 292646 together.

    Complete query:

    SELECT msgId as `ID`,msgFromUserId , msgToUserId ,  
                DATE_FORMAT( msgDate,'%d/%m/%y %H:%i') as `time` ,  MID(msgText,1,30) as `text` , 
                (CASE WHEN (msgFromUserId=292646) then  b.user_login else  a.user_login END) as `sender`
                FROM tbl_messages inner join wp_users as a on tbl_messages.msgFromUserId=a.ID 
                inner join wp_users as b on tbl_messages.msgToUserId=b.ID 
                inner join tbl_forum_users u1 on tbl_messages.msgFromUserId=u1.user_ID 
                inner join tbl_forum_users u2 on tbl_messages.msgToUserId=u2.user_ID 
    where (msgFromUserId=292646 or msgToUserId=292646)
    and tbl_messages.msgId in (
        SELECT max(msgId) 
        FROM tbl_messages
        GROUP BY LEAST(msgFromUserId, msgToUserId), GREATEST(msgFromUserId, msgToUserId)
    )
    order by msgId desc
    

    To improve the performance you should also move the user-id-condition into the subquery:

    SELECT max(msgId) 
    FROM tbl_messages
    where (msgFromUserId=292646 or msgToUserId=292646) -- <-- here
    GROUP BY LEAST(msgFromUserId, msgToUserId), GREATEST(msgFromUserId, msgToUserId)
    

    To use the indexes the best way you should use a UNION ALL optimisation. But this is going to look pretty complex:

    SELECT max(msgId)
    FROM (
        SELECT msgToUserId as otherUserId, max(msgId) as msgId
        FROM tbl_messages 
        WHERE msgFromUserId=292646 
        GROUP BY msgToUserId
    
        UNION ALL
    
        SELECT msgFromUserId as otherUserId, max(msgId) as msgId
        FROM tbl_messages 
        WHERE msgToUserId=292646 
        GROUP BY msgFromUserId
    ) sub
    GROUP BY otherUserId
    

    Note this is only the subquery to use in the WHERE clause (tbl_messages.msgId in (...)).

    This subquery can also be used as a derived table, so we can join it with tbl_messages:

    SELECT msgId as `ID`,
           msgFromUserId,
           msgToUserId,  
           DATE_FORMAT( msgDate,'%d/%m/%y %H:%i') as `time`,
           MID(msgText,1,30) as `text` , 
           (CASE WHEN (msgFromUserId=292646) then  b.user_login else  a.user_login END) as `sender`
    FROM (
        SELECT max(msgId) as msgId
        FROM (
            SELECT msgToUserId as otherUserId, max(msgId) as msgId
            FROM tbl_messages 
            WHERE msgFromUserId=292646 
            GROUP BY msgToUserId
            UNION ALL
            SELECT msgFromUserId as otherUserId, max(msgId) as msgId
            FROM tbl_messages 
            WHERE msgToUserId=292646 
            GROUP BY msgFromUserId
        ) sub
        GROUP BY otherUserId
    ) sub
    inner join tbl_messages on tbl_messages.msgId = sub.msgId
    inner join wp_users as a on tbl_messages.msgFromUserId=a.ID 
    inner join wp_users as b on tbl_messages.msgToUserId=b.ID 
    inner join tbl_forum_users u1 on tbl_messages.msgFromUserId=u1.user_ID 
    inner join tbl_forum_users u2 on tbl_messages.msgToUserId=u2.user_ID 
    order by tbl_messages.msgId desc
    

    You need the following indexes to support the subquery:

    tbl_messages(msgFromUserId, msgToUserId [, msgId])
    tbl_messages(msgToUserId, msgFromUserId [, msgId])