Inbox Functionality with MYSQL - mysql

So I am writing a simple inbox private message system.
My table schema is as follows:
messageID
message
sender id
receiver id
date sent
read ( 0 = no , 1 = yes)
I am able to show the messages for the user by relating his userID to receiverID. However I also want to show the messages he has sent in the inbox to the user.
For example his inbox should show:
Darth Vader - 3 New messages
Luke - 0
new messages (0 because either I read
all of them OR i sent him the message
and he has not replied).
But what i can only come up with is
Darth Vader - 3 New messages.
Can I get any help with how I can accomplish this SQL call?
EDIT: To Clear the Confusion
I am neither Luke or Darth. I have received 3 New messages from darth, and I have sent a message to luke.
EDIT**
Basically I want to be able to make the inbox like how an SMS app would be, where you can see the sms you just sent in a list of sms's by your friends.

SELECT users.username, count(messageID) AS unread
FROM users, messages
WHERE messages.senderID=<USER ID>
AND messages.receiverID=users.userID
AND messages.read=0
Just to make sure I got this right, this query will show all the users I have sent a message to and count the number of those messages which have not been read.

Related

A specific way to pull out message list

I have a project where I am making a virtual phone and currently I need to have a list of people i've messaged. Basically the list you see before going into a private chat with X person.
My problem is currently I can't seem to find the solution to only get latest message I have with X person.
The issue with this is that if I've sent a message to X person and the X person have sent a message to me, i basically get 2 messages in the database when trying to pull out the information, where I only need to get the latest.
This is what I get when doing following and I ALMOST get what I want.
In JQUERY if I remove all where sender = my phone number i would run into a different issue.
https://i.imgur.com/JJzRl6M.png
I've tried following sql
SELECT msg_id, sender, receiver, sender_msg, receiver_read, MAX(msg_date)
FROM nl_phone_messages WHERE sender = '545-3169' OR receiver = '545-3169' GROUP BY sender, receiver
In this case you have to look at the numbers 114-5437 and 545-3169
msg_id 5 should not be in this list as it's not the latest message with this person where msg_id 24 is the latest with that specific person
https://i.imgur.com/WTzRmYv.png
I hope you understand my issue, ask if got any question - Thank you!

Database design for notification system

I read several questions about this argument here, but I can't find a solution.
The system has to send one o more notification to users, so my first idea is
Notification
---------------------
id
sender_id
message
Notification_user
--------------------
id
notification_id
recipient_id
is_read
created_on
read_on
User
---------------------
id
first_name
last_name
In Notification I can save message like "Walter White added a new article in 'Economy' section" and send it to users saved in Notification_user.
but in this way, there are two main problems:
If an user wants to receive the notification in a different language, I should saved the same message in different language.
If the message "Walter White added a new article in 'Economy' section" is saved in Notification, and then the admin changes the name of section from "Economy" to "Economy - Europe", the recipient will read the message with the old section name and not "Walter White added a new article in 'Economy - EUROPE' section"
So I thought to modify the Notification table in this way
Notification
----------------------
id
sender_id
extra_info
where in extra_info I concat a string that contains all info, for example: "sectionId:100,otherColumnId:125"
So, code side I can fetch the correct info, build the message and send it like a notification...but this approach should be expensive. Is there a better solution?

How to select a row just once without checking any fields in SQL

I have a table named tblMessages that has a field named Type .type value can be 1 for user messages and can be 2 for general messages.
in selection of user message i write this sql function:
SELECT * FROM tblMessage WHERE USERID=$Userid and Type=1 and ISseen=0;
then in another SQL code i updated that row and set ISseen=1 for specific user
UPDATE tblMessage SET ISseen=1 Where USERID=$userid;
but in selection of General messages i have a little problem.i want to select general message for all user and show it once!as i said before i can update tblmessage and set ISseen=1 then if ISseen be a value of 1 user message is not selected for another time.but for selection of general message i cant do it with WHERE USERID=$userid.
i dont want to insert general message for every user in table also.then
How can i select general messages ONCE for a user by my table structure?
*****////******
Edit number 1
my tblmessage has 20000 records and just has 10 general messages.in my website when user loged in to his/her acount and go to his/her messages can see Personal Messages and General Messages.when user reach that page i Updated the tblmessage table and set ISseen==1
then when that user loged out and then came back to website and to his/her userpage that personal message didnt show again!cause the field ISseen=1.But in another way the general messages is showed again and again.cause what?cause i dont have any ISseen field for general message.
my question is what can i do then with this explanation of my table.how can i do the same thing with general messages.
Create another table messageTable that will look like this .
ID -- Message
1 -- User message
2 -- General message
now you can apply join to get message on the behalf of type .
your query will be look like this.
SELECT messageTable.*,tblMessage.* FROM tblMessage,messageTable WHERE tblMessage.USERID=$Userid and tblMessage.Type=1 and tblMessage.ISseen=0 and tblMessage.Type=messageTable.ID;

delete message for one user but not for the other

hello I have a database with 3 tables.
USERS('user_id','name','surname')
MESSAGE_GROUP('user_one','user_two', 'hash')
MESSAGES('from_id','group_hash', 'messages')
My php code enables me to send messages between users. My question is how to enable a user to delete a message from its mailbox but the other user still watching the message. The messages must be full deleted only if both users delete the message. I am not interesting about the code, I am interesting only in finding the logic behind this. Any proposals that includes mysql code are welcome. thanks
I think you should follow this. :)
You can keep an extra field in message_group table something like 'deleted_from' which will be initially 0
If user one deletes it make the value of 'deleted_from'=1, if user two deletes it, make the value of 'deleted_from' = 2.
When you go to delete the message for a user, and you find the value 'deleted_from' other than 0, delete the message completely, else mark the value of 'deleted_from' as '1' or '2'.
You will need to either:
Make a new table that specifies the mailboxes the message resides in, so that you can connect it to both users mailboxes
Or duplicate the message so that each user has their own copy that can be deleted
Add flags to the message table (not recommended) indicating whether the sender or recipient has deleted it. This I would avoid as it will not scale well if you have (or intend to add) group messaging.
add to MESSAGE_GROUP a status field with values :
0 no owner and should be deleted
1 only the sender owns the message
2 only the reciever owns the message
3 both sender and recievers own it
I would change the fields from the table in between like this (example that every user only can send a message to one person at a time):
USERS('user_id','name','surname')
MESSAGE_GROUP('user_id','message_id')
MESSAGES('from_id', 'to_id', 'messages')
So every user that has a message, will have a row in the MESSAGE_GROUP. When one user deletes the post, delete that row in MESSAGE_GROUP

Is this a case for denormalisation?

I have a site with about 30,000 members to which I'm adding a functionality that involves sending a random message from a pool of 40 possible messages. Members can never receive the same message twice.
One table contains the 40 messages and another table maps the many-to-many relationship between messages and members.
A cron script runs daily, selects a member from the 30,000, selects a message from the 40 and then checks to see if this message has been sent to this user before. If not, it sends the message. If yes, it runs the query again until it finds a message that has not yet been received by this member.
What I'm worried about now is that this m-m table will become very big: at 30,000 members and 40 messages we already have 1.2 million rows through which we have to search to find a message that has not yet been sent.
Is this a case for denormalisation? In the members table I could add 40 columns (message_1, message_2 ... message_40) in which a 1 flag is added each time a message is sent. If I'm not mistaken, this would make the queries in the cron script run much faster
?
I know that doesn't answer your original question, but wouldn't it be way faster if you selected all the messages that weren't yet sent to a user and then select one of those randomly?
See this pseudo-mysql here:
SELECT
CONCAT_WS(',', messages.ids) unsent_messages,
user.id user
FROM
messages,
user
WHERE
messages.id NOT IN (
SELECT
id
FROM
sent_messages
WHERE
user.id = sent_messages.user
)
GROUP BY ids
You could also append the id of the sent messages to a varchar-field in the members-table.
Despite of good manners, this would make it easily possible to use one statement to get a message that has not been sent yet for a specific member.
Just like this (if you surround the ids with '-')
SELECT message.id
FROM member, message
WHERE member.id = 2321
AND member.sentmessages NOT LIKE '%-' && id && '-%'
1.2 M rows # 8 bytes (+ overhead) per row is not a lot. It's so small I wouldn't even bet it needs indexing (but of course you should do it).
Normalization reduces redundancy and it is what you'll do if you have large amount of data which seems to be your case. You need not denormalize. Let there be an M-to-M table between members and messages.
You can archive the old data as your M-to-M data increases. I don't even see any conflicts because your cron job runs daily for this task and accounts only for the data for the current day. So you can archive M-to-M table data every week.
I believe there will be maintenance issue if you denormalize by adding additional coloumns to members table. I don't recommend the same. Archiving of old data can save you from trouble.
You could store only available (unsent) messages. This implies extra maintenance when you add or remove members or message types (nothing that can't be automated with foreign keys and triggers) but simplifies delivery: pick a random line from each user, send the message and remove the line. Also, your database will get smaller as messages get sent ;-)
You can achieve the effect of sending random messages by preallocating the random string in your m-m table and a pointer to the offset of the last message sent.
In more detail, create a table MemberMessages with columns
memberId,
messageIdList char(80) or varchar ,
lastMessage int,
primary key is memberId.
Pseudo-code for the cron job then looks like this...
ONE. Select next message for a member. If no row exists in MemberMessages for this member, go to step TWO. The sql to select next message looks like
select substr(messageIdList, 2*lastMessage + 1, 2) as nextMessageId
from MemberMessages
where member_id = ?
send the message identified by nextMessageId
then update lastMessage incrementing by 1, unless you have reached 39 in which case reset it to zero.
update MemberMessages
set lastMessage = MOD(lastMessage + 1, 40)
where member_id = ?
TWO. Create a random list of messageIds as a String of couplets like 2117390740... This is your random list of message IDs as an 80 char String. Insert a row to MemberMessages for your member_id setting message_id_list to your 80 char String and set last_message to 1.
Send the message identified by the first couplet from the list to the member.
You can create a kind of queue / heap.
ReceivedMessages
UserId
MessageId
then:
Pick up a member and select message to send:
SELECT * FROM Messages WHERE MessageId NOT IN (SELECT MessageId FROM ReceivedMessages WHERE UserId = #UserId) LIMIT 1
then insert MessageId and UserId to ReceivedMessages
and do send logic here
I hope that helps.
There are potential easier ways to do this, depending on how random you want "random" to be.
Consider that at the beginning of the day you shuffle an array A, [0..39] which describes the order of the messages to be sent to users today.
Also, consider that you have at most 40 Cron jobs, which are used to send messages to the users. Given the Nth cron job, and ID the selected user ID, numeric, you can choose M, the index of the message to send:
M = (A[N] + ID) % 40.
This way, a given ID would not receive the same message twice in the same day (because A[N] would be different), and two randomly selected users have a 1/40 chance of receiving the same message. If you want more "randomness" you can potentially use multiple arrays.