问题
I have this table on my database:

sentBy
and sentTo
are FK to User
table.
On this table I have messages between users:
sentBy | sentTo | dateSent | body
-------+----------+------------------+-----------------
1 | 2 | 11/21/2010 10:00 | Hey!
-------+----------+------------------+-----------------
2 | 1 | 11/21/2010 10:50 | Hi!
-------+----------+------------------+-----------------
1 | 2 | 11/21/2010 10:51 | msg body 1
-------+----------+------------------+-----------------
2 | 1 | 11/21/2010 11:05 | msg body 2
-------+----------+------------------+-----------------
1 | 3 | 11/21/2010 11:51 | msg body 3
-------+----------+------------------+-----------------
3 | 1 | 11/21/2010 12:05 | msg body 4
-------+----------+------------------+-----------------
1 | 3 | 11/21/2010 12:16 | msg body 5
-------+----------+------------------+-----------------
4 | 1 | 11/21/2010 12:25 | msg body 6
-------+----------+------------------+-----------------
I need to know the users with whom user 1 has talked and users that have talked with user 1. In this case, with users 2, 3 and 4 (note that user 4 has sent a message to user 1, but user 1 hasn't sent any message yet).
And the second question is: how can I get the last message with each user? I'm asking about to get the latest message sent to a user.
For example, if I'm asking about user 1, the latest message with user 2 is: msg body 2. And the latest message with user 3 is msg body 5.
How can I get that info in one SQL SELECT statement? Or maybe I will need two selects.
I'm trying to do something like WhatsApp. Where you have a chats screen with a list of users with whom I have talked (my first question), and the last message with them (my second question).
Maybe I can create another table named Conversation
, move sentBy
and sentTo
to that table, and also last message with the date sent to it, but I think this can't be a good design.
The result for my two question is this:
sentBy | sentTo | dateSent | body
-------+----------+------------------+-----------------
2 | 1 | 11/21/2010 11:05 | msg body 2
-------+----------+------------------+-----------------
1 | 3 | 11/21/2010 12:16 | msg body 5
-------+----------+------------------+-----------------
4 | 1 | 11/21/2010 12:25 | msg body 6
-------+----------+------------------+-----------------
回答1:
The following query will give you the expected results for user 1:
select m.* from messages m
join (
select auser,withuser,max(datesent) datesent from (
select sentby as auser,sentto as withuser,datesent from messages
union
select sentto as auser,sentby as withuser,datesent from messages
) as ud
group by auser,withuser
) maxud
on (m.datesent=maxud.datesent and maxud.auser in (m.sentBy,m.sentTo))
where auser=1
Needless to say, you can change the condition in the where
clause to get similar results for any user.
However, my approach would be to create a view and later select from it, like so:
create view conversation_stuff as
select m.sentBy,m.sentTo,m.dateSent,m.body,maxud.auser,maxud.withuser
from messages m
join (
select auser,withuser,max(datesent) datesent from (
select sentby as auser,sentto as withuser,datesent from messages
union
select sentto as auser,sentby as withuser,datesent from messages
) as ud
group by auser,withuser
) maxud
on (m.datesent=maxud.datesent and maxud.auser in (m.sentBy,m.sentTo))
select sentBy,sentTo,dateSent,body from conversation_stuff where auser=1;
This could prove useful for other uses too, I guess.
EDIT: Changed user
to auser
everywhere, for sqlserver to stop complaining and to avoid []
s...
回答2:
Select the maximum dateSent filtered on sentBy and grouped by sentTo, then join this result back to the original table.
edit: I see the problem. You want the maximum message of the messages the user sent and received?
SELECT
Messages.*
FROM
Messages
INNER JOIN
(
SELECT
CombinedKey,
MAX(dateSent) AS dateSent
FROM
(
SELECT
CombinedKey = CASE WHEN sentBy > sentTo
THEN CAST(sentBy AS nvarchar(10)) + '_'
+ CAST(sentTo AS nvarchar(10))
ELSE CAST(sentTo AS nvarchar(10)) + '_'
+ CAST(sentBy AS nvarchar(10))
END,
MAX(dateSent) AS dateSent
FROM
Messages
WHERE
sentBy = @user
OR sentTo = @user
GROUP BY
sentBy,
sentTo
) AS MaxMessagesBoth
GROUP BY
CombinedKey
) AS MaxMessages
ON MaxMessages.CombinedKey = CASE WHEN sentBy > sentTo
THEN CAST(sentBy AS nvarchar(10)) + '_'
+ CAST(sentTo AS nvarchar(10))
ELSE CAST(sentTo AS nvarchar(10)) + '_'
+ CAST(sentBy AS nvarchar(10))
END
AND MaxMessages.dateSent = Messages.dateSent
来源:https://stackoverflow.com/questions/19651811/select-users-with-i-have-talked-and-last-message-with-them-like-whatsapp