I am coding a message system for user communication. In the inbox I do not want to show the user the messages he/she received. I just want to show the conversations. So as an example, if one user send or receive more than one message then in the inbox there should only be the conversation (which includes the newest message, either written or received) with the user and when the user clicks on the conversation he/she can see all past messages.
The table structure (simplified) of 'messages' is as followed:
message_id
user_id_sender
user_id_recipient
message
Now the problem is that the messages are saved in a database where each row is one message, so I have to group these messages in a certain way.
The select statement I came up with is the following:
SELECT * FROM messages
WHERE user_id_sender = 1 OR user_id_recipient = 1
GROUP BY user_id_sender
But now I obviously get two messages because one which has been written by user '1' and one that he has received..
Does anybody have an idea how to solve this?
I've solved this problem some month ago. I suppose you have also a date field. This query give you a well structured results with date of last message and last message.
$qry = 'SELECT
CONCAT(GREATEST(user_id_sender,user_id_recipient)," ",LEAST(user_id_sender,user_id_recipient)) AS thread,
MAX(CONCAT(date,"|",message)) as date_message,
MAX(date) AS last_message,
messages.*
FROM messages
WHERE user_id_sender= ? || user_id_recipient=? GROUP BY thread ORDER BY last_message DESC';
$rows = $db->fetchAll($qry, Array($current_user_id,$current_user_id));
Assuming message_id is ascending (i.e. higher ids are for later messages). #user_id is just a placeholder for the user_id of the inbox you are looking at. I've used Andrea's trick for getting the other_recipient_id concisely.
Select
mm.other_recipient_id,
m.*
From (
Select
user_id_sender + user_id_recipient - #user_id as other_recipient_id,
Max(message_id) as message_id
From
messages
Where
user_id_sender = #user_id Or
user_id_recipient = #user_id
Group By
user_id_sender + user_id_recipient - #user_id
) mm
Inner Join
messages m
On
mm.message_id = m.message_id
Example fiddle http://sqlfiddle.com/#!2/05d191/3/0
You get only one message, as long as the messages (message_id) are unique. You will get multiple messages, of course, if there is a longer communication going on.
N.B.: You don't need group by here, because this is used only for aggregating columns, e.g.:
SELECT user_id_sender, sum(*) FROM messages
GROUP BY user_id_sender;
where you get the sum of messages each user has sent.
So, if you want to see the communication between two users:
SELECT * FROM messages
WHERE user_id_sender = 1 OR user_id_recipient = 1;
You can restrict this further, if you store the timestamps as well, message_time for example or limit the number of messages displayed:
select * from ... limit 10;
Assuming we want to see conversations of user with certain _ ID _, this query can be useful:
SELECT (user_id_sender + user_id_recipient) - _ID_ AS correspondent, COUNT(*) AS total
FROM messages
WHERE user_id_sender = _ID_ OR user_id_recipient = _ID_
GROUP BY (user_id_sender + user_id_recipient)
The resulting query returns the ID of the other user of the conversation ("correspondent") and the number of messages between the two users ("total").
Regards
Related
I have 2 MYSQL tables.
My 2 tables in a simple way
The first table, stores the conversations, second table stores the messages related to conversation in the first table, I need to write a query to view each conversation from the first table and only the last received message (order by msg_time) from the second table.
I have tried this, it works but gives the oldest message from second table:
$query = "SELECT * FROM tbl_conversations inner JOIN
tbl_messages ON tbl_messages.convers_id = tbl_conversations.id
GROUP BY id ORDER BY tbl_messages.msg_time DESC";
And also I need to display the conversation that has the most recent message on top, I mean to display the conversations according to the time of their last messages. Thanks for your support.
I think you get ambiguous error
because id in group by must be write like this tbl_conversations.id
$query = "SELECT * FROM tbl_conversations inner JOIN
tbl_messages ON tbl_messages.convers_id = tbl_conversations.id
GROUP BY tbl_conversations.id ORDER BY tbl_messages.msg_time DESC";
I'm trying to grab some conversation records in my database however only show the latest result. I've tried the below however it groups by the first record found is there a way I can make it group by the last record on the dateline colum
SELECT DISTINCT*
FROM table_messages
WHERE fromid=4 OR toid=4
GROUP BY convoid
ORDER BY dateline desc
My table is like the below
pmid - fromid - toid - convoid - dateline
1 4 15 3 1461079193
2 4 15 3 1461079200
3 15 4 3 1461079220
4 15 4 3 1461079230
5 4 15 3 1461079270
To get a list of all the conversations for a specific user, and the last message sent (and it's details) for each conversation, in a single query:
SELECT
convo.convoid,
message.pmid,
message.dateline,
CASE WHEN fromid = 4 then "SENT" ELSE "RECEIVED" as direction
FROM
(SELECT convoid, max(dateline) as maxdateline FROM table_messages GROUP BY convoid) convo
INNER JOIN table_messages message
ON convo.convoid = message.convoid AND
convo.maxdateline = message.dateline
WHERE
fromid = 4 or toid = 4
Here we are working with two sets again. The first (from the FROM) is a list of all convoid's and their max dateline. Then we INNER JOIN those with the same table on the relationship of convoid and dateline. This will give us a record set of every conversation and it's most recent message.
Then we restrict in the WHERE clause to get just the conversations where either the fromid or the toid is the user you are interested in.
For the fun, I added the CASE statement, just to bring in more information about that last message sent (whether it was sent by the user or received by the user).
It MIGHT be quicker to do:
SELECT
convo.convoid,
message.pmid,
message.dateline,
CASE WHEN fromid = 4 then "SENT" ELSE "RECEIVED" as direction
FROM
(SELECT convoid, max(dateline) as maxdateline FROM table_messages GROUP BY convoid WHERE fromid = 4 or toid = 4) convo
INNER JOIN table_messages message
ON convo.convoid = message.convoid AND
convo.maxdateline = message.dateline
Depending on how MySQL optimizes the first query. If it tries to get every conversation and then the last message, then restricts it for the user then you can force the optimizer to instead, with this version, only get conversations for the user, then get the latest message for those conversations. My bet though is that it's a wash and that MySQL will properly optimize the first query to do what is explicitly stated in the second. But... I figured just in case, I would put it here.
For readability sake, I like the first query better since one can very quickly see the conditions in the main query's WHERE clause. Otherwise you have to hunt in the subquery.
I'm trying to get the last result of each conversation between two users with no prevail. I've looked at a few examples online such as
[php Mysql Grouping and Ordering user messages together
AND a more advanced query
[GROUP BY messages MySQL
My database structure below.
My conversations rely on getting id's of both message_creator and message_target to link them into one chat.
message_id,
message_content,
message_target,
message_creator,
message_status,
message_time
I need message_status to also select 1 AND 2 in the query so if a user has read the last message it still shows as last message in the conversation.
Here is the query I currently have.
$callmessage=" SELECT message_id,MAX(message_content) AS message_content ,message_target,message_status,message_creator,message_throughurl,MAX(message_time) AS message_time FROM messages WHERE message_target='$user1_id' OR message_creator='$user1_id' AND message_status=1 OR message_status=2
Group By
(if(message_creator > message_target, message_creator,message_target))
,(if(message_creator > message_target, message_target,message_creator))
ORDER BY message_id DESC";
If you want to get the very last message between two users, then this should work:
SELECT *
FROM messages
WHERE (
(message_creator='$user1_id' AND message_target='$user2_id')
OR
(message_creator='$user2_id' AND message_target='$user1_id')
)
AND message_status IN (1,2)
ORDER BY message_id DESC
LIMIT 1
Ok. I've worked it out and its working. This is what I've done with my query, just a few little bits to add.
$user1_id= mysqli_real_escape_string($mysqli,$_SESSION['id']);
$callmessage=" SELECT * FROM messages WHERE message_id IN
(SELECT MAX(message_id) AS message_id FROM
(SELECT message_id, message_creator AS id_with
FROM messages
WHERE message_target = '$user1_id'
UNION ALL
SELECT message_id, message_target AS id_with
FROM messages
WHERE message_creator= '$user1_id') t
GROUP BY id_with)";
I am storing replies in a separate table with the original message as the reference id. What I'm trying to do is when a message is clicked it grabs all the corresponding replies and displays them under the main message but how to get the corresponding messages?
This is what I've tried but can't find a WHERE clause that works. Is there any way to group these by the to_id and from_id and reference_id or am I on the wrong track all together?
Basic SQL is pretty much my ceiling at this point so any pointer appreciated.
replies table
MESSAGE TABLE
What I want to do is to have one main conversation with someone and if you message them once all further replies and/or new messages appear underneath
EXAMPLE:
MAIN MESSAGE to USER 1
//grouped by date and if a certain period passes block with a line
reply from user 2
reply from user 1
----------------------------
days later
new message from user 2
so on...
Not sure if this is what you are looking for
SELECT m.subject, r.Message, r.from_id, r.to_id FROM MESSAGE m
JOIN replies r on m.reply_id = r.id
WHERE ((m.from_id = `user_1_id` AND m.to_id = `user_2_id`)
OR (m.from_id = `user_2_id` AND m.to_id = `user_1_id`))
ORDER BY r.date_sent asc
Are you looking for something like this? If you can provide the layout of both the messages and replies tables it may help...
SELECT
`messages`.`message`,
`replies`.`reply`
FROM
`messages`
JOIN
`replies` ON(`messages`.`id` = `replies`.`message_id`)
I am trying to create a conversations based messaging system.
I want to group all messages that have the same conversation_id so that when I display a list of current conversations you only see the latest message from each conversation.
Can I group the values in the mysql query, or would I have to do it in the php?
select m.convos_id, m.message_content from messages m
where m.id in
(select MAX(m1.id) from messages m1 GROUP BY m1.convos_id)
Thanks for your help, this is what I have ended with.
SELECT messages.*, messages_conversations.subject
FROM messages, messages_conversations
WHERE messages.to_user_id = '".$userid."'
AND messages_conversations.id = messages.conversation_id
GROUP BY messages.conversation_id
You can group that in mysql query something like t this:
select * from table where conversation_id = id_here
This will get all the records that have conversation_id set to id_here and then you can use php to work on that array/group.
Its a basic example of parent child relation in database.
each message much have unique id and iys should also refer to the parent key of convo id.
then you can get the latest message for a given convo id.