i have table t_message, its collect conversation beetween user and user.
here's the table strcture.
id_message sender_user_id receiver_user_id message
1 2 1 test 1
2 1 2 test 2
3 2 1 test 3
4 1 2 test 4
with that table, i have an objective to collect group of conversation for user, and as example i want to colletct groups of conversation beetween user 1 and user 2. using this query
select * from ( select * from t_message where receiver_user_id = 1 order by id_message DESC ) group by sender
and the result is
id_message sender_user_id receiver_user_id message
3 2 1 test 3
it's showing group of conversation beetwen user 1 and user 2, but not showing the conversation beetwen user 1 and user 2. its just showing the last conversation from user 2 or sender.
what query i need to collect a group of conversation beetwen user, and retrieve the last record of that conversation.
any suggestion?
oh by the way, i am very sory for my bad english, i hope you got the idea.
You can concat the sender and receiver to form a conversation
select a.id, a.conversation, b.sender_user_id, b.receiver_user_id, b.message
from (
select max(id_message) id,
if(sender_user_id>receiver_user_id,
concat(receiver_user_id,',',sender_user_id),
concat(sender_user_id,',',receiver_user_id)) conversation
from t_message
group by conversation) a
join t_message b on a.id = b.id_message;
fiddle
Related
I have a game_players table like this (other columns omitted for brevity):
game_id user_id
1 1
1 3
2 1
2 2
2 4
My intention is to show the user that's logged in only the games they're involved in (e.g. user 2 should only see game 2).
The "where 2 in(select game_players.user_id from game_players)" bit doesn't appear to be working, I get a list of all the games - including the ones user 2 isn't involved in.
select games.game_id as 'game_id',
games.date_game_started as 'date_started',
users.username as 'username',
users.permanent_id as 'permanent_id',
game_players.user_id as 'user_id'
from games
inner join game_players on games.game_id = game_players.game_id
inner join users on game_players.user_id = users.user_id
where 2 in(select game_players.user_id from game_players)
and games.game_active = 1
and game_players.current_turn = 1
group by(games.game_id)
order by field(game_players.user_id, 2) desc,
games.date_game_started asc
Given my test data, I get this result set:
game_id date_started username permanent_id user_id
1 2021-12-15 13:33:17 userc userc 3
2 2021-12-15 13:35:20 Admin admin 1
I should only be getting the second row, because user 2 is only involved in game 2.
I'll admit that my SQL is a bit rusty, please can you help?
I simplified/broke it down and found the answer. I think... so far so good on my testing of it.
Needed to change:
where 2 in(select game_players.user_id from game_players)
to...
where game_players.game_id in(select game_id from game_players where user_id = 2)
I want the last row in each and every group(Group By user_id).
Structure of Table is like follows:
Table: user_messages
id(PK) | user_id(FK) | read_admin_status | created_at
1 5 0 date
2 5 0 date
3 5 0 date
4 5 1 date
5 6 1 date
6 6 1 date
7 7 0 date
8 7 0 date
Table: users
id | username
Now I want username from users tables and I want other details from user_messages.
Now I want data of user_id 5 and want it's last row only.
likewise for other groups I want last row of each group and from users table I want username by joining the tables.
Please help me out with this if you can. Thank you.
You might try something like this:
SELECT user_messages.id, users.username
FROM (
SELECT MAX(id) AS max_id
FROM user_messages
GROUP BY user_id
) AS ids
INNER JOIN user_messages ON ids.max_id = user_messages.id
INNER JOIN users ON user_messages.user_id = users.id
This query will choose the largest message ID in each user's group of messages, which is the same as getting the last ID when ordering by ID, and then use the associated user ID to get the username. I only selected the message ID and username, but you could get whatever information you wanted out of those tables.
Table structure
Id User_From User_To Time_sent Message Message_read
1 1 2 ~TimeLast ~Message 0
2 3 2 ~Time ... 0
3 3 2 ~TimeLast ... 0
How would I create a query that filters out all the unread messages but only shows the last one of them if more than 1 unread mes are in the table?
So Id get this as a result
Id User_From User_To Time_sent Message Mesage_read
1 1 2 LastTime ~~ 0
3 3 2 LastTime ~~ 0
Edit : this worked fine
select p.id,user_from,username,message,time_sent,message_read
from private_messages p join users u on p.user_from = u.id where p.id in (select max(id) as id from private_messages where user_to = :u1 group by user_from
So your result should only always give back one message? And this one message should be the last one sent? then this query would work
SELECT * FROM table WHERE Message_read 0
ORDER BY Time_sent LIMIT 1
it is simple using Mysql LIMIT, "Mysql Limit" can define how many results you will get, for example if you use "LIMIT 1" in your mysql query then you will get only 1 result.
SELECT * FROM table_name WHERE Message_read=0
ORDER BY Time_sent LIMIT 1
This should only return 1 result from table where Message_read=0 and that is latest in your table.
I have user1 who exchanged messages with user2 and user4 (these parameters are known). I now want to select the latest sent or received message for each conversation (i.e. LIMIT 1 for each conversation).
SQLFiddle
Currently my query returns all messages for all conversations:
SELECT *
FROM message
WHERE (toUserID IN (2,4) AND userID = 1)
OR (userID IN (2,4) AND toUserID = 1)
ORDER BY message.time DESC
The returned rows should be messageID 3 and 6.
Assuming that higher id values indicate more recent messages, you can do this:
Find all messages that involve user 1
Group the results by the other user id
Get the maximum message id per group
SELECT *
FROM message
WHERE messageID IN (
SELECT MAX(messageID)
FROM message
WHERE userID = 1 -- optionally filter by the other user
OR toUserID = 1 -- optionally filter by the other user
GROUP BY CASE WHEN userID = 1 THEN toUserID ELSE userID END
)
ORDER BY messageID DESC
Updated SQLFiddle
You can do this easily by separating it into two queries with ORDER BY and LIMIT then joining them with UNION:
(SELECT *
FROM message
WHERE (toUserID IN (2,4) AND userID = 1)
ORDER BY message.time DESC
LIMIT 1)
UNION
(SELECT *
FROM message
WHERE (userID IN (2,4) AND toUserID = 1)
ORDER BY message.time DESC
LIMIT 1)
The parenthesis are important here, and this returns messages 2 and 6, which seems correct, not 3 and 6.
It also seems like you could use UNION ALL for performance instead of UNION because there won't be duplicates between the two queries, but it's better if you decide that.
Here's your data:
MESSAGEID USERID TOUSERID MESSAGE TIME
1 1 2 nachricht 1 123
2 1 2 nachricht 2 124
3 2 1 nachricht 3 125
4 3 2 nachricht wrong 1263
5 2 4 nachricht wrong 1261
6 4 1 nachricht sandra 126
The below works as required:
SELECT m1.*
FROM Message m1
LEFT JOIN Message m2
ON LEAST(m1.toUserID, m1.userID) = LEAST(m2.toUserID, m2.userID)
AND GREATEST(m1.toUserID, m1.userID) = GREATEST(m2.toUserID, m2.userID)
AND m2.time > m1.Time
WHERE m2.MessageID IS NULL
AND ( (m1.toUserID IN (2,4) AND m1.userID = 1)
OR (m1.userID IN (2,4) AND m1.toUserID = 1)
);
To simplify how this works, imagine you just wanted the latest message sent by userid 1, rather than having to match the to/from tuples as this adds clutter to the query that doesn't help. To get this I would use:
SELECT m1.*
FROM Message AS m1
LEFT JOIN Message AS m2
ON m2.UserID = m1.UserID
AND m2.time > m1.time
WHERE m1.UserID = 1
AND m2.MessageID IS NULL;
So, we are joining similar messages, stipulating that the second message (m2) has a greater time than the first, where m2 is null it means there is not a similar message with a later time, therefore m2 is the latest message.
Exactly the principal has been applied in the solution, but we have a more complicated join to link conversations.
I have used LEAST and GREATEST in the join, the theory being that since you have 2 members in your tuple (UserID, ToUserID), then in any combination the greatest and the least will be the same, e.g.:
From/To | Greatest | Least |
--------+-----------+-------+
1, 2 | 2 | 1 |
2, 1 | 2 | 1 |
1, 4 | 4 | 1 |
4, 1 | 4 | 1 |
4, 2 | 4 | 2 |
2, 4 | 4 | 2 |
As you can see, in similar From/To the greatest and the least will be the same, so you can use this to join the table to itself.
There are two parts of your query in the following order:
You want the latest outgoing or incoming message for a conversation between two users
You want these latest messages for two different pairs of users, i.e. conversations.
So, lets get the latest message for a conversation between UserID a and UserID b:
SELECT *
FROM message
WHERE (toUserID, userID) IN ((a, b), (b, a))
ORDER BY message.time DESC
LIMIT 1
Then you want these to be combined for the two conversations between UserIDs 1 and 2 and UserIDs 1 and 4. This is where the union comes into play (we do not need to check for duplicates, thus we use UNION ALL, thanks to Marcus Adams, who brought that up first).
So a complete and straightforward solution would be:
(SELECT *
FROM message
WHERE (toUserID, userID) IN ((2, 1), (1, 2))
ORDER BY message.time DESC
LIMIT 1)
UNION ALL
(SELECT *
FROM message
WHERE (toUserID, userID) IN ((4, 1), (1, 4))
ORDER BY message.time DESC
LIMIT 1)
And as expected, you get message 3 and 6 in your SQLFiddle.
I have a table that manage conversations of a chat between users, the structure is the following.
id | user_id | conversation_id
let's say that on the conversation with ID 1 there are 3 people to chat and the conversation with ID 2, 2 people as well
Conversations_users table will look like this
id | user_id | conversation_id
1 1 1
2 2 1
3 4 1
4 3 2
5 4 2
Now having only the id of the users 3 and 4 and Not Conversation ID I would like select the conversation that belongs to that users so a verbal query should be:
Select from conversations_users, where in user_id = 3 and 4 and conversation_id is equals to conversation id of user 3 and 4
how can I build this "verbal query" in Mysql?
to get all the users in the conversations that user 3 and 4 are part of you could use this:
select distinct(user_id) from conversation_table where conversation_id in (select distinct(conversation_id) from conversation_table where user_id in (3,4));
it won't be very fast though
to get their actual conversations, I'm assuming you have a different table with the text in it:
you probably want something like this
select distinct(u.user_id), c.text from conversation_table u left join conversations c on c.id=u.conversation_id where u.conversation_id in (select distinct(conversation_id) from conversation_table where user_id in (3,4));
here is an sqlfiddle
Here is one method:
select uc.conversation_id
from UserConversions uc
where uc.user_id in (3, 4)
group by uc.conversation_id
having count(*) = 2;
If the table could have duplicates, you'll want: having count(distinct user_id) = 2.
EDIT:
If you want a specific list, just move the where condition to the having clause:
select cu.conversation_id
from conversations_users cu
group by cu.conversation_id
having sum(cu.user_id in (3, 4)) = 2 and
sum(cu.user_id not in (3, 4)) = 0;
I assume you have another table called "conversations" which holds the data you really want.
SELECT *
FROM conversations, conversations_users
WHERE conversations_users.user_id in (3,4)
AND conversations.id = conversations_users.conversation_id