1

I got stucked with this query in the past couple of hours, and I badly need someone to help me figure it out. I'm try to finish my private message system, but I lost myself in the database tables. I've created three tables for the system, and they are as follows:

CONVERSATION(**conversation_id**, subject)

CONVERSATION_MEMBER(**conversation_id**, **user_id**, conversation_last_view, conversation_deleted)

CONVERSATION_MESSAGE(**message_id**, conversation_id, user_id, message_date, message_text)

Ok, so in my function to get all conversations of a specific user, I'm fetching the subject, the date, and at last I want to show the user who is he/she talking to. I wrote the following query:

SELECT
   c.conversation_id,
   c.conversation_subject, 
   MAX(cmes.message_date) AS conversation_last_reply, 
FROM conversation c, conversation_member cmem, conversation_message cmes
WHERE c.conversation_id = cmes.conversation_id
AND c.conversation_id = cmem.conversation_id                          
AND cmem.user_id = {$_SESSION['user_id']}
AND cmem.conversation_deleted = 0
GROUP BY c.conversation_id
ORDER BY conversation_last_reply DESC

And, at last I'm tryin to get the user's first and last name (the other user in the conversation), but I lost myself on how to do that. I've tried to create another function that will get the conversation id, while looping through the results of the first query, and return the user's first and last name, but it didn't work out.

Btw, for the users I have another table... I guess I don't have to tell you. Ok, thank you.

3
  • You're not actually selecting anything from conversation_member Commented Apr 17, 2012 at 23:23
  • What do you want to say? I don't know how am I going to select anything, because I am doing cmem.user_id = sessionID. Would you like to tell something more on your answer? Commented Apr 17, 2012 at 23:30
  • added a proper answer, hope it helps Commented Apr 17, 2012 at 23:31

5 Answers 5

2
$sql = "SELECT (user.forename, user.surname, other_fields...)
          FROM conversation
    INNER JOIN conversation_member
            ON conversation.conversation_id = conversation_member.conversation_id
    INNER JOIN conversation_message
            ON conversation.conversation_id = conversation_message.conversation_id
    INNER JOIN users_table /* replace this with the name of your user table */ AS user
            ON user.user_id = conversation_member.user_id
         WHERE user.user_id = :userid
           AND conversation_member.conversation_deleted = 0
      GROUP BY conversation.conversation_id;"

$query = $db->prepare($sql);
$query->bindParam(':userid', $userid, PDO::PARAM_INT);
$query->execute();
$results = $query->fetchAll();
$user = $results[0]["user"]; //stores array of user fields (forename, surname, etc)
Sign up to request clarification or add additional context in comments.

6 Comments

How is your query going to return the user's first and last name? I don't get it.
@Darko So long as you Join the tables together, you can select their fields (see above)
The thing is that this will return the first and last name of the logged in user, and not from the other participant in the conversation...
@Darko But you've only stored one user ID? Have I misunderstood your question? All you need is, to find ALL users in one conversation?
In the session I hold only the logged in user. So, the conversation is possible between the logged in and some other user. Both users id's are stored in the conversation_member table for that specific conversation. What I want to do is to take the other user's first and last name (not the one that is logged in, but the other one)
|
1
SELECT
   c.conversation_id,
   c.conversation_subject,
   user.firstname,
   user.lastname,
   MAX(cmes.message_date) AS conversation_last_reply, 
FROM conversation c, conversation_member cmem, conversation_message cmes, user_tablename user
WHERE c.conversation_id = cmes.conversation_id
AND c.conversation_id = cmem.conversation_id                          
AND cmem.user_id = {$_SESSION['user_id']}
AND cmem.conversation_deleted = 0
AND user.user_id_column = whatever.you_used_as_foriegn_key
GROUP BY c.conversation_id
ORDER BY conversation_last_reply DESC

Assuming the columns names are like that

8 Comments

I don't store the users in the conversation_member table, I store only their id's. So, I need to connect a fourth table (which is the users table). Even if it was like that, you answer would have returned the first and last name of the logged in user, and not of the user that he is talking to...
In that case add on the final user table and change the reference to cmem.firstname to the new table, and no problem
You mean, to change the cmem.firstname to user.firstname, and that's it? It returns a wrong user... I need to connect them somehow in the WHERE clause, that's where I got stucked...
I've tweaked it a little more, replace the table references with your own
I still get a wrong value. Now it's returning the current logged in user.
|
1

You need to join with the "conversation_member" table again, this time, selecting the other user's id where the same conversation_id and message_id applies:

SELECT
    c.conversation_id,
    c.conversation_subject, 
    MAX(cmes.message_date) AS conversation_last_reply,
    cmem2.user_id AS other_user_id
    CONCAT(usr_tbl.firstname, ' ', usr_tbl.lastname) AS other_user_name
FROM conversation c
    JOIN conversation_member cmem
    JOIN conversation_message cmes
    JOIN conversation_member cmem2
    JOIN users_table usr_tbl

ON  c.conversation_id = cmes.conversation_id 
AND c.conversation_id = cmem.conversation_id                          
AND cmem.user_id = {$_SESSION['user_id']}
AND cmem.conversation_deleted = 0

AND c.conversation_id = cmem2.conversation_id                          
AND cmem2.user_id = {$_SESSION['other_user_id']}
AND cmem2.conversation_deleted = 0

GROUP BY c.conversation_id
ORDER BY conversation_last_reply DESC

1 Comment

Well, I actually don't know who is the other participant in the conversation. I don't store it in a session. I need to find a way to take it somehow else...
1

I don't see why you're having trouble getting the user's first and last name if you were able to construct the rest of that query by yourself?

Try something along the lines of, :

SELECT cmem.user_id, u.first_name,  u.last_name, c.conversation_id, c.conversation_subject, MAX(cmes.message_date) AS conversation_last_reply  
FROM conversation c  
INNER JOIN conversation_member cmem on c.conversation_id=cmem.conversation_id  
INNER JOIN conversation_message cmes on c.conversation_id=cmes.conversation_id  
INNER JOIN users u on u.user_id = cmem.user_id
WHERE cmem.user_id = {$_SESSION['user_id']} AND cmem.conversation_deleted = 0  
GROUP BY cmem.user_id, u.first_name, u.last_name, c.conversation_id, c.conversation_subject

Now, I think you should also be reconsidering your database structure so that all these joins are not necessary. I see several problems. One, your database seems to be over-normalized. Why do you have a separate "Conversations" table that has only two fields, conversation id and subject? In any message system I've ever seen, the subject is always visible so you would always have to join the conversation table just to get the subject field. The conversation_id is in every other table anyway. Just add the subject field to the message table and eliminate the conversation table if that's all it's holding, normalization isn't always a good thing.

Second, why do you set a flag for deleted messages instead of just deleting them? I've also never seen a message system that lets me restore messages I've deleted. At the very least, if you want to retain them for whatever reason you should move them to an archive table so that the primary table you're running selects off of doesn't have to deal with the performance hit of parsing through meaningless "deleted" entries.

Lastly, what is the conversation_member table anyway? Based on my interpretation, it's supposed to represent a member of the conversation since it has a user_id. Why would the conversation delete flag be present for a single member of a conversation? If anything it should be in the conversation table. With that improvement, the only field left in it is conversation_last_view, which really no one cares about. The more important thing is conversation_last_post, which can be easily derived from the timestamp of the last message posted in the thread.

Ultimately, if you just want to see the first and last names appended to your query it's as simple as joining the users table and displaying those two entries. The SQL query I provided should get you close if it doesn't work straight out, I'm too lazy to copy your database and try it myself. However, I think you should really consider the overall design of your database as well so you don't run into needless performance issues down the road.

6 Comments

With your suggestion I will actually get the logged in user's first and last name. That's what I'm trying to explain to everyone that joined this question. To answer to your sugesstions, I will take a look about removing the conversation table and doing something else, but about the flag for deleted messages, I cannot just delete the conversation. Because if one of the users deletes the conversation, then the conversation will be deleted in the other profile also. I delete the conversation completely when both of the user's set the conversation as deleted.
Yes, but I think you're over-thinking it. Sometimes it's important to think about the why before considering the how. Have you ever used a messaging system where you "delete" your thread? Would you yourself even bother using this functionality if it wouldn't be deleted anyway without the other user's verification? What's the likelihood that one person would want it deleted and the other person would bother deleting it as well? Also, why would the deletion be at the conversation level and not the message level?
Well, the deletion part :) I would really like to get a help on the actual question.. The user won't know that the conversation is not actually deleted, because after I set his "deleted flag" to 1 let's say, it won't appear in his conversations window. After the other user sets it also to 1, then I delete it completely. I'd rather appreciate if you actually give me some suggestion (on the whole system if you want) instead of criticizing me :)
There's nothing wrong with constructive criticism, that's what this place is for :). I apologize if I came across as insulting, that wasn't my intent. In any case, my suggestion would be to get rid of the delete functionality altogether. Generally, there's no reason to allow someone to delete a message. In the context of a message system, if they don't want it to be there they just don't read it. The only time that you would actually want to delete a message, is if you don't want anyone to see it. If you want to enable that, you'll have to let them delete it for everyone.
So in short, my recommendation is to remove the functionality of deleting conversations because it's unintuitive and unnecessary. If you really want users to be able to delete their messages, I would change it so that they can delete individual messages (only if they own the message obviously) instead of the entire conversation, and that it would take effect for all users.
|
0

To answer the question of: Finding all users in a conversation, MINUS the current user:

    SELECT (users.forename, users.surname)
      FROM conversation_members AS members
INNER JOIN users_table AS users
        ON members.user_id = users.user_id
     WHERE members.conversation_id = :conversationid
       AND NOT users.user_id = :userid

Where :userid is the current user and :conversationid is the conversation in question.

6 Comments

I guess this way I will need to create separate queries. Because it will be contradictory otherwise. Maybe I should try again by calling a separate function or something I have no idea.
Have a look here to see how I joined many tables together to get its info: stackoverflow.com/questions/10154466/…. Also see my answer here about an email system structure: stackoverflow.com/questions/10175076/… @Darko
Wow... nice :D Do you mind giving me the database schema for this query? Thank you for the second post also. I really appreciate.
@Darko You can tell by the table/col names! But i'll edit the answer to post the schema
@Darko Okay its updated, check the link again. I have to warn you though, my code is untested and I've updated the schema since then (Which i've posted for you), but the query in the link isn't updated.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.