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.
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 have a table which has 2 numeric (int) values (IDs) as sender_id and receiver_id.
What I'm trying to do is group these rows as "conversations". the conversation between :
sender_id = 1, and receiver_id = 5,
is the same conversation where sender_id = 5 and receiver_id = 1
So when I fetch the database rows, I want to have only 1 row for each conversation.
I will fetch these conversation for a specific user. the user ID (which I already know and will refer to as userId) can be in sender_id and receiver_id.
So my algorithm would be something like:
Select All rows
from Database,
where sender_id = userId OR receiver_id = userId,
group by sender_id+user_id
the result rows have to include the other column value (not the one equal to userId), it can include both though and I will have to detect the other one by comparing them both to the userId I have.
To be honest I have always been uncomfortable with SQL manual. For some reason I'm failing to find what I need from there. Forgive me..
I'm not sure if I should make a temp table or use the SQL SUM function, or if I can use something like GROUP BY sender_id+receiver_id
More info (not sure if relevant):
I'm using php
The table has few more columns like record_id, create_date and read_date
Your help and replies are greatly appreciated.
Update:
The solution to my question is marked below. However, I encountered another problem that I thought of sharing, as it can maybe potentially help someone viewing this question later.
The problem I faced is, when I show the conversations, I wanna show the last message row. Now when grouping is done, the grouping stacks on the first row found. and I want the opposite. I want the rows returned to contain the last message row per conversation.
I could do that with the following query. Please note that this is only one way of doing it and I'm not yet sure of the performance of this query. I will test it later and update this if needed.
the $this->id is the id of the user I wanna display the conversations for.
"SELECT t1.* FROM table t1
INNER JOIN (
SELECT sender_id, receiver_id, max(create_date) lastCreateDate
FROM table
WHERE receiver_id = ".$this->id." OR sender_Id = ".$this->id."
GROUP BY GREATEST(sender_id ,receiver_id), LEAST(sender_id,receiver_id)
) t2
ON ((t1.sender_id = t2.sender_id
OR t1.sender_id = t2.receiver_id)
AND t1.create_date = t2.lastCreateDate)
ORDER BY create_date DESC LIMIT 0,15"
LEAST returns the lowest and GREATEST the greatest value:
GROUP BY LEAST(sender_id, receiver_id), GREATEST(sender_id, receiver_id)
GROUP BY max(sender_id, receiver_id), min(sender_id, receiver_id)
I have a table with conversation ids which is a one-to-many relationship to another table that has conversation users. The conversation users table has the id for the conversation, a unique id and a userID. I can use this:
SELECT c_id FROM conversations INNER JOIN conversation_users on conversation_users.cu_convo_id = conversations.c_id
which gives me all the rows but I need to find a way to do something like an IN statement because I need to find the conversation id given two user IDs.
Any ideas? Am I approaching this the wrong way completely?
WORKING ANSWER:
IN case anyone needs it, the following worked for me:
SELECT cu_convo_id FROM conversations
INNER JOIN conversation_users on
conversation_users.cu_convo_id = conversations.c_id
WHERE cu_user_id IN (31,42)
GROUP BY cu_convo_id
HAVING count(distinct cu_user_id) = 2;
if I have 5 IDs to find, i put them on line 4 and then put the number 5 on line 6
Simply add a WHERE to your SQL:
SELECT c_id FROM conversations
INNER JOIN conversation_users on
conversation_users.cu_convo_id = conversations.c_id
WHERE conversations.usedID IN (1,2);
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 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