I am developing a messaging inbox system. Users create message threads directed at another user. It will work as follows:
Message threads you receive appear in your inbox.
Message threads you create also appear in your inbox.
Message threads you delete are only deleted from your own inbox.
I have the following fields in my message_thread table:
id
from_user_id
to_user_id
subject
deleted
updated_at
I have managed to implement 1 and 2 using the following query:
SELECT * FROM message_thread mt
WHERE ((mt.from_user_id = 1 OR mt.to_user_id = 1) AND mt.deleted = 0)
ORDER BY mt.updated_at DESC
I am trying to figure out a way of implementing 3.
I assume new columns will need to be introduced (from_user_deleted and to_user_deleted). Is it possible to retrieve the correct resultset by extending the query or will this need to be done in the server side script (currently using PHP)?
EDIT: If user A deletes a thread and then user B sends a reply to that thread, it should re-appear in user A's inbox.
Try below code:
1.You can add new field as status
2.Status
1 = from_user_deleted
2 = to_user_deleted
Then Query
SELECT * FROM message_thread mt
WHERE ((mt.from_user_id = 1 OR mt.to_user_id = 1) AND mt.deleted = 0 AND mt.status<>2)
ORDER BY mt.updated_at DESC
In plain english:
Where
( The message is to you
OR
( the message is from you
AND
the message is not deleted )
)
I have managed to solve this by adding a new table:
message_thread_status
message_thread_id (FK)
user_id (FK)
deleted (default 0)
When a message_thread is created, it creates two entries in the message_thread_status table (one for each user in the message thread).
I then just need to find the record for the current user in the message_thread_status table and add in a condition to check for the value of deleted.
New query:
SELECT
* FROM message_thread mt
LEFT OUTER JOIN
message_thread_status mts ON mts.message_thread_id = mt.id
WHERE
((mt.from_user_id = 1 OR mt.to_user_id = 1)
AND (mts.user_id = 1 AND mts.deleted = 0))
ORDER BY
mt.updated_at DESC
Related
I am building a mobile chat web application and I have stuck in a problem. Coming straight to the issue, I have a screen which displays messages list from all users (like WhatsApp). I want to display the last message sent or received between the users in the list (as in the screenshot below). My current query extracts the message from the 1st row for all users right now. That's not what I want.
Little more brief details of what is happening
As you can see in messages table, the fields msg_from and msg_to represents the sender and the receiver respectively. In my data, the messages are transferred between user 1 & 8 and user 1 & 11. For user 1 & 8 the last record fetched should be record 9 which has msg_message Are you there? and similarly, for user 1 & 11 the last record to be fetched would be record 10 which has msg_message Would you like to join?. But currently, for all users the record getting fetched is the 1st record with message How are you?. What changes should my query have to get the desired result? Please have a look at the fiddle below.
Fiddle Here: DB Fiddle
I learned a lot from researching in order to solve this. When grouping, groupBy will take the first row of non-grouped columns (suck as msg_message), so we may order it when joining with the help of a subquery, just like this:
SELECT swp_by, swp_to, msg_from, msg_to, mem_fname, mem_lname, mem_last_activity, msg_message, GREATEST(MAX(msg_time), swipes.swp_date) as msgdate, COUNT(msg_id) as msgcnt FROM swipes
LEFT JOIN
(
SELECT * FROM messages order by msg_time desc -- this is the magic, we use this subquery to order before grouping
)
messages ON
((
messages.msg_from = swipes.swp_by
AND messages.msg_to = swipes.swp_to)
OR (messages.msg_from = swipes.swp_to
AND messages.msg_to = swipes.swp_by
))
solution is in your fiddle: https://www.db-fiddle.com/f/xnh4jiUb8rDLFHpL2gWHrM/5
I think I got expected output
I am displaying the message status that differs from each user. Let's say user1 sends a message to user2, user1's message status then sets to read, while user2's message is set to unread by default. It will be updated after user2 clicks the message.
So in these scenario, the message of user1 (from the inbox) will have a gray-colored font which indicates that the message is set to read (since user1 is the one who is sending). On the other side, user2 have a bold font that indicates that the message is unread.
Here is the first structure of the table:
message(messageid, fromid, toid, message, timestamp, status)
The problem here is that if I update the message status to read, it affects the other side (user2). So I add another column that will set the status differently from user1 and user2:
message(messageid, fromid, toid, message, timestamp, from_status, to_status)
Here, from_status is for the fromid and to_status is for toid. But I'm having a problem on how to use these values to display the status.
The PHP code of that I use during my first attempt is these:
<?php
$id = $_SESSION['id'];
$query = mysql_query("SELECT m.* FROM message m
LEFT JOIN message m2 ON (
(m.fromid=m2.fromid AND m.toid=m2.toid) OR
(m.fromid=m2.toid AND m.toid=m2.fromid)
) AND m.timestamp<m2.timestamp
WHERE (m.fromid='$id' OR m.toid='$id') AND m2.toid IS NULL ORDER BY timestamp DESC");
while ($message = mysql_fetch_array($query)) {
if ($message['status'] === 'unread') {
// bold font style will be applied
}
else {
// gray-colored font will be applied
}
}
?>
(The query fetches each conversation from every user with the latest conversation.)
These code works fine for the main user, which is user1, but affects the other side, which views that the message received from user2 is set to read instead or unread.
So, I'm having some trouble on what to do on the modified table, having 2 separate status each for each user. How can I get these done?
#andrewsi comment is quite nice, when you'll have for example many receivers. In your case it's only one additional field, so in my opinion it's not an overflow to use just one table.
Regarding your case you can do this in one simple sql:
SELECT m.*,
CASE
WHEN m.fromid = $id THEN m.from_status
WHEN m.toid = $id THEN m.to_status
END as read_status
FROM message m
WHERE
m.fromid = $id OR m.toid = $id
ORDER BY timestamp DESC;
And in your view you are only checking the read_status field
(Posted on behalf of the OP.)
I've managed to solve the problem, thanks to #Rafal Mnich. So I grab a part of his query and do a bit of modification, and it works.
Here's the query:
SELECT m.msgid, m.fromid, m.toid, m.content,
CASE
WHEN m.fromid = '$id' THEN m.frommsgstatus
WHEN m.toid = '$id' THEN m.tomsgstatus
END AS msgstatus
FROM msg m
LEFT JOIN msg m2
ON ((m.fromid=m2.fromid AND m.toid=m2.toid) OR (m.fromid=m2.toid AND m.toid=m2.fromid)) AND m.timestamp<m2.timestamp
WHERE (m.fromid='$id' OR m.toid='$id') AND m2.toid IS NULL
ORDER BY m.timestamp DESC
These displays the messages grouped by the sender fromid, which also displays the latest conversation you have from each of the senders. And it displays the correct message status in both sides of the users.
I am creating php messaging system based with conversations. And I got a problem.
I have a table with columns: id, to, from, msgtext, timesent, viewed, deleted.
I want to select just one conversation between to and from. Take a notice that if user to can write a message to user from.
How to select seperate conversations?
I am trying with this SQL:
SELECT to, from FROM pms WHERE to='$userid' OR from = $userid group by to ORDER by id desc LIMIT 50
But this does not work another way. Because if user1 wrote message to user2, and user2 replied to user1, and again if user1 replies and user2 replies, i see as 2 conversations, but it should be as one conversation.
It should be like this:
User sends a message to some other user. It should show in conversations list That other user's name.
If user gets a message from another user, he should see his name in the conversation list.
Try this code
SELECT `id`, `to`, `from`, `msgtext`, `timesent`, `viewed`, `deleted` FROM table_name
WHERE `to` = 'username_to' AND `from` = 'username_from'
LIMIT 1
OFFSET 0
If you understand the concept behind you will realize that you need to play only with OFFSET number to get the next record.
P.S. A shortcut to the LIMIT & OFFSET lines above is:
LIMIT 0, 1
Also, when you ask something on Stack Overflow you are supposed to show what you have tried, and ask why is not working, instead of simply asking for code.
I'm structuring tables for creating a dialog-look user message function in my OA system.
I got a table indicating participants of a dialog like this
dialog_user:
dialog user
1 1
1 2
2 1
2 3
now user 1 send a message to user 2. So I need to create a message, and insert the message into the dialog which user 1 and user 2 participates.
How can i find the dialog id within one or limited queries?
Furthermore, I'm also implementing one-to-more message sending function. Each receiver will see the messages in the dialog which he and the sender participates. So i need to find all the dialogs which sender and each receiver participates, and insert the new message id into it.
Is that possible to be done in one nice-look query?
I've think about save participants imploded-ordered string like
dialog users
1 1,2
1 1,3
but i don't think this is a good practice cause the users field should be varchar and with limited length(say,255).
Does anyone has any idea about this?
SELECT d1.dialog
FROM dialog_user d1
JOIN dialog_user d2 USING (dialog)
WHERE d1.user = 1
AND d2.user = 2
Maybe something like this (assuming DialogId/UserId is indexed):
SELECT
DISTINCT(DialogId)
FROM
Dialogs d
WHERE
EXISTS (SELECT NULL FROM Dialogs p1 WHERE p1.DialogId = d.DialogId AND p1.UserId = 1)
AND EXISTS (SELECT NULL FROM Dialogs p1 WHERE p1.DialogId = d.DialogId AND p1.UserId = 2)
I'm making private messaging system using mysql. Created this tables:
1) users (id, name)
2) messages(id, text, created)
3) user_has_messages(id, user_id, message_id, is_sender)
Table user_has_messages stores messaging history, so there are 2 rows(for "sender" user and for "receiver" user.) per 1 message. 2 rows per message because sender should see his message even if receiver deleted it.
So i need to fetch list of all dialogs for concrete user with last message in it. It should be easier to understend if you take a look a this pic: Explanation
The problem is that i cannot construct a proper query for this task. Maybe bad db design?
Looks like this query is what i need:
SELECT * FROM users_has_messages uhm1
WHERE uhm1.message_id=(
SELECT message_id FROM users_has_messages uhm2
WHERE (uhm1.receiver_id=uhm2.receiver_id AND uhm1.sender_id=uhm2.sender_id)
OR uhm1.receiver_id=uhm2.sender_id ORDER BY message_id DESC limit 1)
AND user_id=1
I believe the database design may be wrong, because if the recipient deletes his message (by deleting the user_has_messages row) then the sender can no longer see who they sent it to - information is lost.
If a message always has one sender and one recipient, then I would have the tables like:
1) users (id, name)
2) messages(id, text, created, sender_id, recipient_id,
deleted_by_sender, deleted_by_recipient)
Even with this simplified design the SQL for your requirement is a bit complicated:
select m.recipient_id, m.text
from messages m
where m.sender_id = ?
and m.created = (select max(created)
from messages m2
where m2.sender_id = m.sender_id
and m2.recipient_id = m.recipient_id
and m2.deleted_by_sender = 0
and m2.deleted_by_recipient = 0);
(and that assumes that (sender_id, recipient_id, created) is a unique key).