MySQL - Get last message from all users - mysql

I've searched on stackoverflow already, but the existing threads did not help me, so im posting this.
My database schema:
user: user_id, displayname
message: id, sender_id, receiver_id, message, dateSent
What i want:
Show me a list of the latest message from each user WHERE receiver_id = 4
What i already tried:
MYSQL select newest posts from tables
query to get last message only from all users
Still can't solve my problem :(

Try this:
SELECT
u1.displayname AS SenderName,
m.date_sent,
...
FROM messages AS m1
INNER JOIN users AS u1 ON m1.sender_id = u1.id
(
SELECT sender_id, MAX(dateSent) MaxDate
FROM messages
GROUP BY sender_id
WHERE receiver_id = 4
) AS m2 ON m1.sender_id = m2.sender_id
AND m1.datesent = m2.maxdate

SELECT u.user_id, u.displayname, m.message
FROM user u
INNER JOIN
(SELECT id, sender_id, receiver_id, message, MAX(dateSent) AS mdate
FROM message msg
WHERE receiver_id = 4
GROUP BY sender_id
) as m
ON u.user_id = m.sender_id AND u.dateSent = m.mdate
HTH

SELECT m.id, MAX as mDate, m.sender_id, u.displayname
FROM message m
LEFT JOIN user u ON u.user_id = m.sender_id
WHERE m.receiver_id = 4
GROUP BY sender_id

Related

Adding a extra subquery to MySQL query

So i have 2 tables, users and messages
users table
uid, username
messages table
mid, senderid, receiverid, message, timestamp
The query i have at the moment is retrieving the uid, username and last timestamp of each chat conversation
SELECT DISTINCT
IF (messages.senderid = '5e9b95786a71f8.25790415', messages.receiverid, messages.senderid) as uid,
(SELECT username FROM users WHERE uid = IF (messages.senderid = '5e9b95786a71f8.25790415', messages.receiverid, messages.senderid)) as username,
MAX(messages.timestamp) as last_timestamp
FROM messages JOIN users ON users.uid = messages.senderid WHERE messages.senderid = '5e9b95786a71f8.25790415' OR messages.receiverid = '5e9b95786a71f8.25790415'
GROUP BY 1,2
which outputs below
uid | username | last_timestamp
1 | Developer | 1601826378
2 | BetaTester | 1601826301
What i need to add to this query is the message field which is based on the last_timestamp i have in the output.
How can the query be updated to include the extra message field?
Try this.
select A.uid, C.username, B.timestamp, B.message from (SELECT DISTINCT
IF (messages.senderid = '5e9b95786a71f8.25790415', messages.receiverid, messages.senderid) as uid,
MAX(messages.mid) as max_mid
FROM messages WHERE messages.senderid = '5e9b95786a71f8.25790415' OR messages.receiverid = '5e9b95786a71f8.25790415'
GROUP BY 1) A join messages B on A.max_mid = B.mid join users C on A.uid = C.uid
I'm a little confuse, since you already got last_timestamp why don't just left join message table again.
left JOIN messages m2 on last_timestamp = m2.timestamp and mid = m2.mid
and just select message column
select m2.message

MySql sorted subquery with group by in main query

I'm developing a simple message system for my website. Here's an image of how the messages table works:
Picture of the MySql table
What I had in mind is to use a subquery and then use GROUP BY to get only the last message for every person that was sent to, in this case, the user 1. This is the first query:
SELECT m.user_id as user_id, u.username as username, u.avatar as avatar, m.message_id as messsage_id, m.message as message, m.date as read_date
FROM users_messages m
INNER JOIN users u
ON u.user_id = m.sender_id
WHERE m.receiver_id = 1
ORDER BY message_id DESC
Some data is from another table, but it isn't relevant. This query returns this result, which is expected:
Image with the results of the first query
I made the messages descriptive so you can see what's going on more clearly. Now, here comes the weird part. As you can see, the messages are sorted by message ID, so the comments are sorted from new to old. All good here. However, when I expecute the following query...
SELECT q.user_id, q.username, q.avatar, q.message, q.read_date
FROM (SELECT u.user_id as user_id, u.username as username, u.avatar as avatar, m.message_id as messsage_id, m.message as message, m.date as read_date
FROM users_messages m
INNER JOIN users u
ON u.user_id = m.sender_id
WHERE m.receiver_id = 1
ORDER BY message_id DESC) AS q
GROUP BY user_id
This should group the messages by user, so only the first one (the newest one in this case) should show up. However, this isn't the case. It ALWAYS returns the first messages for everyone, as seen in the following picture:
Picture with the results that make no sense
I'm really, really confused about this, and I can't find anything that helps me fix it.
The issue is with the GROUP BY, which selects an arbitrary result from the set you give it. I think you should be able to get the result you want by changing your first query:
SELECT u.user_id as user_id, u.username as username, u.avatar as avatar, m.message_id as message_id, m.message as message, m.date as read_date
FROM users_messages m
INNER JOIN users u
ON u.user_id = m.sender_id
WHERE m.receiver_id = 1 AND
m.message_id = (SELECT MAX(message_id)
FROM users_messages m1
WHERE m1.sender_id = u.user_id)

Can't integrate INNER JOIN in my query

Hello I'm using the query below to select the latest message from each unique conversation. Everything works perfectly except when I try to integrate an INNER JOIN to try to link the user's user_id with their username in the users table.
I've tried pretty much every combination to integrate the INNER JOIN so I decided to separate it from the working query until I get help.
I made a Fiddle http://sqlfiddle.com/#!9/a9bbc/1 I just want user_id on the right to print the user's username
SELECT message_id,
msg,
user_id
FROM messages
JOIN (SELECT user_id,
Max(dat) m
FROM ((SELECT message_id,
recipient_id user_id,
dat
FROM messages
WHERE owner_id = 1)
UNION
(SELECT message_id,
owner_id user_id,
dat
FROM messages
WHERE recipient_id = 1)) t1
GROUP BY user_id) t2
ON ( ( owner_id = 1
AND recipient_id = user_id )
OR ( owner_id = user_id
AND recipient_id = 1 ) )
AND ( dat = m )
ORDER BY dat DESC
.
users.username INNER JOIN users ON messages.user_id = users.user_id;
If messages.message_id is an AUTO_INCREMENT PRIMARY KEY column, this will probably be the fastest way to receive the desired result:
select m.message_id, m.msg, u.username,
case sub.other_user_id
when m.owner_id then 'received from'
when m.recipient_id then 'sent to'
end as direction
from (
select other_user_id, max(message_id) as message_id
from (
select recipient_id as other_user_id, max(message_id) as message_id
from messages
where owner_id = #uid
group by recipient_id
union all
select owner_id as other_user_id, max(message_id) as message_id
from messages
where recipient_id = #uid
group by owner_id
) sub
group by other_user_id
) sub
join messages m on m.message_id = sub.message_id
join users u on u.user_id = sub.other_user_id
order by sub.message_id desc
I've also added the column direction. This way you will know if the message has been sent or received. The result would be like:
| message_id | msg | username | direction |
|------------|------------------------------------------------------------------|----------|---------------|
| 9 | You should also see this | User3 | sent to |
| 8 | you should Now see this instead even with the owner_id flipped.. | User2 | received from |
http://sqlfiddle.com/#!9/a9bbc/34
Note that you can still add any column from the users and messages tables in the SELECT clause.
To get this query work fast you will need indexes on messages(owner_id, recipient_id) and messages(recipient_id, owner_id).
I think this is correct... finally. The not exists filters out responses after the start.. the inner query grabs the most recent conversation. This should match the expected output.
SELECT dT.maxid AS message_id
,(SELECT msg FROM messages M WHERE M.message_id = dT.maxid) AS message_id
,(SELECT username FROM users U WHERE U.user_id = dT.owner_id) AS user_id
FROM (
SELECT MAX(message_id) as maxid
,owner_id
,recipient_id
FROM messages M
GROUP BY owner_id, recipient_id
) AS dT
WHERE NOT EXISTS(SELECT *
FROM messages M2
WHERE M2.recipient_id = dT.owner_id
AND M2.owner_id = dT.recipient_id
AND M2.message_id > dT.maxid)
ORDER BY dT.maxid
Produces output:
message_id message_id user_id
8 you should Now see this instead even with the User2
owner_id flipped.. The users_id in the query is 1
and it gets the conversation with user_id 2
9 You should also see this User1
fiddle
When you have joined any tables or subqueries, you really really really need to include table name or table aliases with all column references. I changed the maximum dat to maxdat below so I could use m for messages.
SELECT
m.message_id
, m.msg
, m.user_id
FROM messages m
JOIN (
SELECT
user_id
, MAX(dat) maxdat
FROM (
(SELECT
message_id
, recipient_id user_id
, dat
FROM messages
WHERE owner_id = 1)
UNION
(SELECT
message_id
, owner_id user_id
, dat
FROM messages
WHERE recipient_id = 1)
) t1
GROUP BY
user_id
) t2 ON (
(m.owner_id = 1 AND m.recipient_id = t2.user_id)
OR (m.owner_id = t2.user_id AND m.recipient_id = 1)
)
AND (m.dat = t2.maxdat)
ORDER BY
m.dat DESC
You need to join your final result with users table and get the value of users.username based on user_id. Try this:
SELECT messages.message_id,
messages.msg,
users.user_id,
users.username
FROM users, messages
JOIN (SELECT user_id,
Max(dat) m
FROM ((SELECT message_id,
users.username,
recipient_id user_id,
dat
FROM messages
INNER JOIN users ON messages.recipient_id = users.user_id
WHERE owner_id = 1)
UNION
(SELECT message_id,
users.username,
owner_id user_id,
dat
FROM messages
INNER JOIN users ON messages.owner_id = users.user_id
WHERE recipient_id = 1)) t1
GROUP BY user_id) t2
ON ( ( owner_id = 1
AND recipient_id = user_id )
OR ( owner_id = user_id
AND recipient_id = 1 ) )
AND ( dat = m )
WHERE users.user_id = messages.user_id
ORDER BY dat DESC
If I understand correctly, you want the most recent messages where the send or recipient is "1" and associated user names.
That suggests:
select m.*, ur.username as recipient_name, us.username as sender_name
from messages m join
users ur
on m.recipient_id = ur.user_id join
users us
on m.sender_id = us.user_id
where m.date = (select max(m2.dat)
from messages m2
where 1 in (m2.recipient_id, m2.sender_id)
) and
1 in (m.recipient_id, m.sender_id);
EDIT:
Based on your SQL Fiddle the above understanding is not correct. You want to consider pairs of recipients, so I think you want:
select m.*, ur.username as recipient_name, us.username as sender_name
from messages m left join
users ur
on m.recipient_id = ur.user_id left join
users us
on m.owner_id = us.user_id
where m.dat = (select max(m2.dat)
from messages m2
where (m2.recipient_id, m2.owner_id) = (m.recipient_id, m.owner_id) or
(m2.recipient_id, m2.owner_id) = (m.owner_id, m.recipient_id)
) and
1 in (m.recipient_id, m.owner_id);
Here is the SQL Fiddle.
If the ordering of the pair is import (so (1, 2) is different from (2, 1) remove the second condition in the innermost where clause.

how to select the last text from message table

I have two tables (messages and user). I want to select the last (msg_id,text) from the messages table for a particular ad_id and need to select the name of the user from the user table.
SELECT u.id
, m.date
, m.ad_id
, max(m.msg_id)as msg_id
, u.first_name
, m.text
, m.u_to_id
, m.u_from_id
FROM user u
JOIN messages m
ON CASE WHEN m.u_from_id ='14' THEN u.id = m.u_to_id
ELSE u.id = m.u_from_id END
AND (m.u_from_id='14' OR m.u_to_id='14')
AND m.ad_id='20'
GROUP BY CONCAT(m.ad_id,u.id)
ORDER by m.msg_id DESC
this query is working but I can't select t the last m.textTable structure
SELECT u.id, m.text
FROM user u
JOIN messages m ON m.msg_id = (SELECT max(msg_id) FROM messages WHERE u_from_id = u.id)
I simplified your query to show the logic relevant to your question. Basically you want to join your messages table on the msg_id that is equal to the inner query of the max msg_id with that user.
After so many experiments added a new column(bargainer) for identify the recipient and this query working fine for me
select m.msg_id,m.text,m.status,m.date,m.bargainer,m.ad_id,u.first_name,u.id from user u JOIN messages m where msg_id in (select max(msg_id) from messages m where m.ad_id=20 and u.id=m.bargainer group by(m.bargainer))group by(m.msg_id) order by msg_id DESC

Query: How to obtain the most recent message per one user conversations?

I have a conversations table that is very simple but important (created_by is the code of the user who sent the message). Then, I have a conversations_messages table (sent_by is the code of the user who sent the message, message is my message text and created_at is the timestamp). Finally I have a conversations_users table that has the user_id (code of the user who received the message) and created_by (code of the user who sent the message).
I am trying to implement the functionality of inbox, I want to show the last message per conversation. user_id in conversations_users is the user who received the message and the user who is consulting his inbox.
I tried this query but I don't know how to deal with the created_at timestamp (in table conversations_messages) in order to get the most recent message per conversation.
select CM.id, CM.message, CM.created_at, C.id as 'conversation_id' from conversations_users CU
join conversations C on CU.conversation_id = C.id join conversations_messages CM on C.id = CM.conversation_id where CU.user_id = 1 and CM.created_at = '2015-03-10 14:18:02'
I have to replace '2015-03-10 14:18:02' for some most recent created_at timestamp thats my problem.
My tables are:
conversations_users: id, conversation_id, user_id, created_by
conversations: id, created_by
conversations_messages: id, conversation_id, sent_by, message, created_at
Thanks!
SELECT cm.*
FROM conversations_messages cm
JOIN conversations c ON cm.conversation_id = c.id
JOIN conversations_users cu ON cu.conversation_id = c.id
WHERE cu.user_id = 1
AND NOT EXISTS ( SELECT 'a'
FROM conversations_messages cm2
WHERE cm2.conversation_id = cm.conversation_id
AND cm2.created_at > cm.created_at
)
GROUP BY c.id
Here's one approach joining the table back to itself using the max aggregate, grouping by each conversation:
select CM.id, CM.message, CM.created_at, C.id as 'conversation_id'
from conversations_users CU
join conversations C on CU.conversation_id = C.id
join conversations_messages CM on C.id = CM.conversation_id
join (
select max(created_at) maxcreated_at, conversation_id
from conversations
group by conversation_id
) T on T.maxcreated_at = CM.created_at
and T.conversation_id = CM.conversation_id
where CU.user_id = 1