Saving chat message in MySQL DB, scheme design - mysql

I'm building a pretty simple chat app that allows both 1 on 1 message and chat rooms for groups of people. I'm planning to have one Message table to store all chat messages, each message will also keep the sender ID and receiver ID, in the case of messages sent in a chat room, we also keep the ID of that chat room. Below is the table:
Message Table
ID Message Sender Receiver Chatroom Timestamp
1 Hello, David 123 321 1495330074
2 Hi, Linda 321 123 1495930032
3 Hi everyone! 456 999 1495930132
4 What up? 321 123 1495930192
...
Then if I'm user 321, and I want to retrieve my conversation with user 123, I just need to SELECT * FROM Message WHERE Sender=123 or Receiver=123 or Sender=321 or Receiver=321 and Chatroom IS NULL
There is one issue with this design - a user can't delete a message that he doesn't want to see any more.
To solve that, I think I can have a separate table to store what messages a user received or sent, like below:
User Message Table
ID UserID MessageID
1 123 1
2 321 1
3 321 4
...
It seems a little redundant, but this way David can delete a message in his conversation with Linda, while Linda can still see full conversation history.
Is there better design of the tables? And is this good practice to throw all chat messages in one giant table? Should I add some index to make query faster?

You can use this query.
delete from Message
where sender = 123 and user = 321
But this will delete all the chat messages between this user and sender. To delete specific message you can use ID
delete from Message
where ID = 1

Related

mySQL Messaging Between Two Users

I have a private messaging system that uses a mysql database.
Messages are passed between two users.
However, when any user deletes the conversation history, it should not be deleted in the other user.
I could create an Additional column called "deleted_users" and use LIKE when listing messages.
But I'm worried about performance and I need your help.
id
user_from
user_to
msg
1
82
85
test
2
85
82
test

Reading data from table using LIMIT while the order changes

Okay, let me explain.
You know how Facebook Messenger and Discord both have the last messaged friends list? It is a list of all of your friends order by whoever texted you last. Now, if you had over 100 people in that list, it would be better to send the list to the client in chunks of 10. Once the client reaches the bottom, it asks for the next 10. This can be done with the LIMIT offset, amount.
But now, the problem. The user might open the messenger, talk to someone for 10 minutes, and then scroll further down in the last messaged friends list. In this case, the table has changed before the user has retrieved the full list. The list in database is now in a different order because someone messaged them in the meanwhile and is now on top of the list, but the client already has the first chunk of the list, but this doesn't contain the people that texted them.
In case my explanation was not enough, here's a visual demonstration:
Visual demo
'Last messaged friends' list in database (ordered by latest timestamp):
Person 23
Person 77
Person 93
Person 99
Person 67
Person 85
User connects, asks for the first 3 entries.
Client now has (ordered by latest timestamp):
Person 23
Person 77
Person 93
'Person 99' messages that user. 'Person 99' is now on top of the list.
'Last messaged friends' list in database (ordered by latest timestamp):
Person 99
Person 23
Person 77
Person 93
Person 67
Person 85
User scrolls down. Client asks for the next 3 entries.
Client now has (ordered by latest timestamp):
Person 23
Person 77
Person 93
Person 93 (duplicate)
Person 67
Person 85
('Person 99' missing)
Is this something I could fix/implement with a more advanced SQL query?
If I can't, how could I implement this in other ways?
For information, I have a Socket.io (a.k.a more advanced WebSocket) connection between the server and the client, I can send whatever necessary information thru that.
It sounds like you want aggregation to avoid duplicates:
select person_id
from messages
group by person_id
order by max(timestamp) desc;
Thank you, Hector Vido, for the suggestion. (he made a comment right below my question, go upvote)
"Selecting messages by timestamp don't solve this? You keep the last timestamp and then ask by anoter 10 registries >= that timestamp"
Solution
I'll keep the oldest and newest timestamp in the client side.
If the client scrolls down, I will request 10 entries before the oldestTimestamp and then the new oldestTimestamp will be the oldest timestamp of the received entries.
Also after every 10 seconds, I could request for entries after the newestTimestamp and then the new newestTimestamp will be the newest timestamp of the received entries.

Reliable and accurate threading structure for emails?

I'm working on a email project. I would like to display email in threads just like gmail.
What is the best approach to display mails in thread?
I have checked jwz threading algorithm. But looks like that algorithm is written for projects that has no databases.That algorithm focuses on these three header keys. Message-ID, In-Reply-To and References
Can someone tell me what is the proper, efficient and most accurate way to achieve threading using mysql database.?
Do I have to use separate table for threads and references?
If possible give me some sample mysql queries. So I can understand better.
Thank you.
Any message board design eg wordpress etc should also works for email. I also come up with a design:
email.id user_id subject status folder created updated ...other info you'd save
1 123 Hello New Inbox Y-m-d.. Y-m-d.. ...
2 3456 World Replied Inbox ...
reply.id email_id reply_to_id user_id created email_txt ip ...
10 1 0 890 Y-m-d.. Hi ...
20 2 0 5678 ...
30 2 20 3456 ... Replyto 2
55 2 30 5678 .... Replyto 3
So in your email.folder.index page:
SELECT * FROM email WHERE user_id = 12345 ORDER BY updated desc LIMIT 50
And when you click one an email on index page, goto email details page:
SELECT * FROM reply WHERE email_id = 2 ORDER BY created, reply_to_id
The key magic is that the tree is built based on reply_to_id

Query for extracting info from one table based on other

I have two tables post and share, post has many share. I want to fetch all data in post table using userId(posted by owner user) and also check share table using same userid that is if some one shared post to other user, if any condition is true, I need to fetch data.
I want to fetch data in post table if posted by owner or shared by other user in share table.
Example :
table name: post
id(pk) postname userid
1 abc 10
2 xxx 10
3 yyy 11
4 zzz 12
5 bbb 13
table name:share
id postid(fk) userid
1 3 10
2 4 10
3 3 11
4 1 12
Expected output: example find by userid 10
id postname userid
1 abc 10 // this record created by user 10 (owner)
2 xxx 10 // this record created by user 10 (owner)
3 yyy 11 // this record shared by other user to user 10.
4 zzz 12 // this record shared by other user to user 10.
You may want to print created_by and shared_by in two different columns as the example seems a bit confusing. Below query should produce the expected output in that case:
select p.id, p.postname, p.userid as 'created_by', s.user_id as 'shared_by'
from post p left outer join share s on p.id = s.postid
where p.userid = 10
order by p.id;
It finally clicked what you are looking for..
select p.id, p.postname, p.userid
from post p
join share s on s.postid=p.id
where s.userid='10' or p.userid='10'
That should return all interactions related to a specific user (id 10), either if a post was created by that user, or a post was shared to that user.

How can I write a SQL query with multiple COUNTs for different GROUP BYs?

One of my coworkers is working on a SQL query. After several joins (workers to accounts to tasks), she's got some information sort of like this:
Worker Account Date Task_completed
Bob Smith 12345 01/01/2010 Received
Bob Smith 12345 01/01/2010 Received
Bob Smith 12345 01/01/2010 Processed
Sue Jones 23456 01/01/2010 Received
...
Ultimately what she wants is something like this - for each date, for each account, how many tasks did each worker complete for that account?
Worker Account Date Received_count Processed_count
Bob Smith 12345 01/01/2010 2 1
... and there are several other statuses to count.
Getting one of these counts is pretty easy:
SELECT
COUNT(Task_completed)
FROM
(the subselect)
WHERE
Task_completed = 'Received'
GROUP BY
worker, account, date
But I'm not sure the best way to get them all. Essentially we want multiple COUNTs using different GROUP BYs. The best thing I can figure out is to copy and paste the subquery several times, change the WHERE to "Processed", etc, and join all those together, selecting just the count from each one.
Is there a more obvious way to do this?
SELECT worker, account, date,
SUM(task_completed = 'Received') AS received_count,
SUM(task_completed = 'Processed') AS processed_count
FROM mytable
GROUP BY
worker, account, date