how to get latest messages from all users - php

I have two tables one is for users and other is for messages i want the latest messages of every user who sent the message to user 1 my code is below
SELECT u.profile_pic
, u.username
, u.firstname
, u.lastname
, m.message_from
, m.message_body
FROM user u
JOIN messages m
ON m.message_from = u.user_no
WHERE m.message_to = $userno
ORDER
BY m.sent_time DESC

It would be easier to give you an answer if you could include your table structures (is there a unique id on the messages table?). But from the information in your question, this should work:
SELECT
u.profile_pic,
u.username,
u.firstname,
u.llastname,
m.message_from,
m2.message_body
FROM user u
JOIN (SELECT message_from,message_to,max(sent_time)
FROM messages
GROUP BY message_from,message_to) m
ON user.user_no = messages.message_from
AND messages.message_to = '$userno'
JOIN messages m2 ON m.message_from = m2.message_from
AND m.message_to = m2.message_to
AND m.sent_time = m2.sent_time
Basically, the subselect will pull out all of the latest messages from one user to another, which you then join against to filter out all the others.

Use LEFT JOIN to message table on same user and newer sent_time. The latest of each user is the one that doesn't have such message:
SELECT profile_pic,username,firstname,lastname,messages.message_from,messages.message_body
FROM user
JOIN messages ON user.user_no = messages.message_from
LEFT JOIN messages AS newermessages ON user.user_no = newermessages.message_from AND messages.sent_time < newermessages.sent_time
WHERE messages.message_to = '$userno'
AND newermessages.id IS NULL
ORDER BY messages.sent_time DESC

The latest message from each user:
SELECT a.*
FROM messages a
JOIN
( SELECT message_from
, MAX(sent_time) max_sent_time
FROM messages
GROUP
BY message_from
) b
ON b.message_from = a.message_from
AND b.max_sent_time = a.sent_time;
The remainder of this problem has been left as an exercise for the reader.

Try adding the field of the ORDER BY clause into your selection:
SELECT u.profile_pic
, u.username
, u.firstname
, u.lastname
, m.message_from
, m.message_body
, m.sent_time
FROM user u
JOIN messages m
ON m.message_from = u.user_no
WHERE m.message_to = $userno
ORDER
BY m.sent_time DESC

Related

get unique records between two fields from mysql in php

I have a table where my private message are stored which I done with my friends.
I have table structure as shown in image
Suppose my logged user_id is 1, Now I want show his last five unique conversations with last message of each conversation.
Not very elegant but working:
SELECT m1.*
FROM private_msgs m1 LEFT OUTER JOIN private_msgs m2
ON (m1.message_from_uid=m2.message_from_uid AND m1.message_to_uid=m2.message_to_uid AND m1.message_time<m2.message_time)
WHERE m2.message_id IS NULL AND m1.message_from_uid=$uid
ORDER BY message_time DESC;
Add limit to 5 or whatever you want
EDIT. Sorry I didn't notice that UID can be as sender or receiver. Can you publish table with sample data for example here: http://sqlfiddle.com?
Anyway that should to the trick:
WHERE m2.message_id IS NULL AND (m1.message_from_uid=$uid OR m1.message_to_uid=$uid)
Here it is, long and ugly. You can replace temporary tables as subqueries in one big final query. Explanations are at the end.
CREATE TEMPORARY TABLE IF NOT EXISTS t1 AS
(SELECT message_id, message_from_uid, message_to_uid, message_time
FROM private_msgs
WHERE message_from_uid=$UID)
UNION
(SELECT message_id, message_to_uid, message_from_uid, message_time
FROM private_msgs
WHERE message_to_uid=$UID);
CREATE TEMPORARY TABLE IF NOT EXISTS t2 AS SELECT * FROM t1;
SELECT m.*
FROM (t1 LEFT OUTER JOIN t2
ON (t1.message_from_uid=t2.message_from_uid AND t1.message_to_uid=t2.message_to_uid AND t1.message_time<t2.message_time))
JOIN private_msgs m ON t1.message_id=m.message_id
WHERE t2.message_id IS NULL AND t1.message_from_uid=$UID
ORDER BY t1.message_time DESC;
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
t1 temporary table takes all messages where $UID is a sender and union them with messages where $UID is receiver, but puts $UID as sender. In t1 doesn't matter who is sender/receiver. What does matter that these are conversations between $UID and someone else.
t2 is copy of t1, because in MySQL you can't join temporary table with itself.
And finally we do the query. We join t1 and t2, take only newest messages in every conversation and sort it from latest to oldest. As in t1 and t2 sender and receiver are mixed we do the last join with private_msg to get original data.
PS. Sorry for my english, I'm not native speaker.
Found among my old code:
select m.*
from private_messages m
join user_table s ON m.sender_id = s.id
join user_table r ON m.receiver_id = r.id
join (select if(sender_id = $UID, receiver_id, sender_id) as user_id_other,
max(posted_at) AS date_time_max from private_messages
where (sender_id = $UID OR receiver_id = $UID)
group by if(sender_id=$UID, receiver_id, sender_id)
) AS t
on if(m.sender_id = $UID, m.receiver_id, m.sender_id) = user_id_other
AND m.posted_at=date_time_max
where m.sender_id = $UID OR m.receiver_id = $UID
order by m.posted_at desc
Not responsible for result, but this can help. By itself variables must be replaced, I did not modify the code very much, as you can see. But this piece of... code... worked.
In your case smths like:
select m.*
from YOUR_MESSAGES_TABLE m
--- OPTIONAL: join YOUR_USER_TABLE s ON m.message_from_uid = s.id
--- OPTIONAL: join YOUR_USER_TABLE r ON m.message_to_uid = r.id
join (select if(message_from_uid = $YOUR_USER_ID, message_to_uid, message_from_uid) as user_id_other,
max(message_time) AS date_time_max from YOUR_MESSAGES_TABLE
where (message_from_uid = $YOUR_USER_ID OR message_to_uid = $YOUR_USER_ID)
group by if(message_from_uid=$YOUR_USER_ID, message_to_uid, message_from_uid)
) AS t
on if(m.message_from_uid = $UID, m.message_to_uid, m.message_from_uid) = user_id_other
AND m.message_time=date_time_max
where m.message_from_uid = $UID OR m.message_to_uid = $UID
order by m.message_time desc limit 5
Test: http://sqlfiddle.com/#!9/f66fd8/4

MySql query for selecting latest message from the conversation and messages table join

Please tell me what is wrong with this code, this returns all the conversations, but it returns latest message for only first conversation.
SELECT
conversations.*,
messages.message,
patients.first_name as fullname,
patients.city,
patients.thumb,
patients.gender,
patients.online_status
FROM
patients
INNER JOIN conversations
ON conversations.patient_id_fk = patients.id
LEFT JOIN messages
ON messages.conversation_id = conversations.id
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.therapist_id_fk = conversations.therapist_id_fk
)
WHERE conversations.therapist_id_fk='1'
GROUP BY conversations.id
ORDER BY messages.message_id DESC
You want the maximum message for the conversation not the therapist, so I suspect you want this in this subquery:
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.message_id = conversations.id
)
I'm not sure the outer GROUP BY is correct (it may not be needed at all and it seems to conflict with the columns of the SELECT), but without sample data and desired results, it is hard to tell.
EDIT:
I see, the problem is the filtering on therapist id in the outer query. That makes this a bit more complicated:
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z JOIN
conversations c
ON m.conversation_id = c.id
WHERE c.therapist_id_fk = 1 AND -- restriction on subquery
z.message_id = conversations.id -- correlation to outer query
)
Don't use single quotes for constants unless the column is a string or date.
SELECT
conversations.*,
messages.message,
patients.first_name as fullname,
patients.city,
patients.thumb,
patients.gender,
patients.online_status
FROM
patients
INNER JOIN conversations
ON conversations.patient_id_fk = patients.id
LEFT JOIN messages
ON messages.conversation_id = conversations.id
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.conversation_id = conversations.id
)
WHERE conversations.therapist_id_fk='1'
ORDER BY messages.message_id DESC
Found the issue. It was here:
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.conversation_id = conversations.id
)
I was using this:
AND messages.message_id =
(
SELECT MAX(message_id)
FROM messages z
WHERE z.therapist_id_fk = conversations.therapist_id_fk
)
I was able to solve the problem with this query
SELECT conversations.*,m.message FROM `conversations` LEFT JOIN messages as m on conversations.id = m.conversation_id AND m.id = (SELECT MAX(ms.id) from messages as ms where ms.conversation_id = conversations.id)
I left join the messages and when doing so i select the top most message. Order by can be used to give the order by date
Your problem is the bit:
SELECT MAX(message_id)
FROM messages z
WHERE z.therapist_id_fk = conversations.therapist_id_fk
This query only matches exactly one message. You'd need something a bit different.
Try this:
SELECT
conversations.*,
messages.message,
patients.first_name as fullname,
patients.city,
patients.thumb,
patients.gender,
patients.online_status
FROM
patients
INNER JOIN conversations
ON conversations.patient_id_fk = patients.id
LEFT JOIN messages
ON messages.conversation_id = conversations.id
WHERE conversations.therapist_id_fk='1'
AND messages.message_id IN ( -- new bit
SELECT MAX(message_id) FROM messages z GROUP BY z.conversation_id
)
GROUP BY conversations.id
ORDER BY messages.message_id DESC

group messages by same users

I have 2 tables .. 1- users,,2-messages
I wrote this query but it doesn't show the last subject and I need last subject
select (
CASE WHEN messages.sender = 68314 THEN messages.receiver ELSE messages.sender END
) AS user_id,
MAX(messages.added) last_added,messages.subject,
MAX(messages.id) as last_id,users.username
FROM messages
INNER JOIN users ON users.id = IF(messages.sender = 68314, messages.receiver, messages.sender)
WHERE (messages.sender = 68314 or messages.receiver = 68314) AND messages.sender!=0
GROUP BY
(
CASE WHEN messages.sender = 68314 THEN messages.receiver ELSE messages.sender END
)
ORDER BY last_added DESC
It shows first subject, not the last one.
please give this query a try. It first select max(id) of messages of people talking to id 68314 assuming those are created last...and joins back with messages, and the joined with users to get the other person's name.
SELECT last_messages.user_id,
m.added as last_added,
m.subject,
last_messages.last_id,
u.username
FROM messages m
INNER JOIN
(SELECT IF(sender = 68314,receiver,sender) as user_id,
MAX(id) as last_id
FROM messages
WHERE IF(sender = 68314,sender,receiver) = 68314
GROUP BY user_id
)last_messages
ON last_messages.last_id = m.id
INNER JOIN users u ON last_messages.user_id = u.id
ORDER by last_added;
sqlfiddle

Resolve data for 2 tables

Let's say, I have 3 tables. "users", "topics" and "replies.
The "topics" and "replies" table do have a column "user_id" which references to "id" on the "users" table. The "replies" table also have a column "topic_id" which references "id" on "topics". Also imagine that each topic has 1 reply.
Now, I want to fetch all topics and resolve the username for both tables, so the output should include a "topic_username" and a "reply_username".
I know how to select the username for the topic, but how can I do that for both the topics and replies table?
Thanks
Updated code:
SELECT
t.*,
t_users.username as topic_user,
last_reply.user_id as last_reply_user_id,
r_users.username as last_reply_username,
last_reply.replies_count,
ORDER BY IFNULL(last_reply.created_at,t.created_at DESC
FROM
topics as t
left join users as t_users on t_users.id = t.user_id
left join ( select r.*,count(r.id) as replies_count from (select * from replies order by id desc) as r group by r.topic_id ) as last_reply on last_reply.topic_id = t.id
left join users as r_users on r_users.id = last_reply.user_id
Try this
SELECT
t.*,
t_users.name as topic_user,
r_users.name as reply_user
FROM
topics as t
left join replies as r on r.topic_id=t.id
left join users as t_users on t_users.id = t.user_id
left join users as r_users on r_users.id = r.user_id
this would work well if there is 1:1 topic and reply, if you have 1 to many replies then you are better off group by topic id and get the usernames by group concat
Version 2 (to get reply count and last reply user)
SELECT
t.*,
t_users.name as topic_user,
last_reply.user_id as last_reply_user_id,
r_users.name as last_reply_username,
last_reply.replies_count,
IFNULL(last_reply.created_at,t.created_at) as last_update
FROM
topics as t
left join users as t_users on t_users.id = t.user_id
left join ( select r.*,count(r.id) as replies_count from (select * from replies order by id desc) as r group by r.topic_id ) as last_reply on last_reply.topic_id = t.id
left join users as r_users on r_users.id = last_reply.user_id
ORDER BY IFNULL(last_reply.created_at,t.created_at) DESC
You wanna do this in a single query? You can do it like this but I believe a separate query for fetching replies and topics might be better (fetch replies first, get the unique topic IDs and then fetch topics in a separate query)
SELECT r.id, r.topic_id, ru.username AS reply_username, tu.username AS topic_username
FROM replies AS r
INNER JOIN topics AS t ON (t.id = r.topic_id)
INNER JOIN users AS ru ON (r.user_id = u.id)
INNER JOIN users AS tu ON (t.user_id = u.id)

How to speed up this MySQL query? Latest messages query

i need speed up this query for list latest messages. This query running too long (eg. 10 seconds ...)
SELECT datas.uid,
datas.message,
datas.date,
CONCAT(conv.first_name, ' ', conv.last_name) AS conversation_name
FROM (SELECT m.message_id,
m.message,
IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
m.readed,
m.sended AS `date`
FROM users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
WHERE m.message_id IN (SELECT MAX(message_id)
FROM messages
WHERE to_uid = 1
OR from_uid = 1
GROUP BY LEAST(from_uid, to_uid),
GREATEST(from_uid, to_uid))) datas
LEFT JOIN users conv
ON conv.user_id = datas.uid
ORDER BY datas.date DESC
LIMIT 5
This query use 2 tables (users and messages).
Table users:
user_id (primary, autoincrement)
login
pass
first_name
last_name
....
Table messages:
message_id (primary, autoincrement)
from_uid (sender message, reference to table users -> user_id)
to_uid (receiver message, reference to table users -> user_id)
sended (timestamp)
message (varchar)
EDIT
I added indexes to messages:
- from_uid
- to_uid
- sended
and this is without efect...
Try creating indexed on the id's you're checking.
In this case you might want to create an index on: conv.user_id, datas.uid, m.message_id, messages.to_uid, messages.from_uid, and datas.date might be a good idea as well, since you're sorting on that.
Add indexes on from_uid and to_uid to speed up the SELECT MAX(message_id) subquery. Otherwise, it has to do a full scan of the table.
I would try and remove the sub query(s)
You can clean it up a bit by joining against users twice when getting the user details from te from and to uid. This way the joins can use indexes effectively. Then just use IF in the SELECT to decide which one to return:-
SELECT IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
m.message,
m.sended AS `date`,
IF (m.from_uid = 1, CONCAT(to_conv.first_name, ' ', to_conv.last_name), CONCAT(from_conv.first_name, ' ', from_conv.last_name)) AS conversation_name
FROM users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
LEFT JOIN users to_conv
ON to_conv.user_id = m.to_uid
LEFT JOIN users from_conv
ON from_conv.user_id = m.from_uid
WHERE m.message_id IN
(
SELECT MAX(message_id)
FROM messages
WHERE to_uid = 1
OR from_uid = 1
GROUP BY LEAST(from_uid, to_uid),
GREATEST(from_uid, to_uid)
)
ORDER BY date DESC
LIMIT 5
The sub query to check the message id is a bit more difficult to remove. Generally IN performs badly. Might be worth changing it to use EXISTS (although this will require the check in the HAVING clause which might not be good)
SELECT IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
m.message,
m.sended AS `date`,
IF (m.from_uid = 1, CONCAT(to_conv.first_name, ' ', to_conv.last_name), CONCAT(from_conv.first_name, ' ', from_conv.last_name)) AS conversation_name
FROM users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
LEFT JOIN users to_conv
ON to_conv.user_id = m.to_uid
LEFT JOIN users from_conv
ON from_conv.user_id = m.from_uid
WHERE EXISTS
(
SELECT MAX(message_id) AS max_message_id
FROM messages
WHERE to_uid = 1
OR from_uid = 1
GROUP BY LEAST(from_uid, to_uid), GREATEST(from_uid, to_uid)
HAVING m.message_id = max_message_id
)
ORDER BY date DESC
LIMIT 5

Categories