选择我已经聊过的用户和最后留言的用户,比如whatsapp

时间:2013-10-29 07:08:20

标签: sql sql-server whatsapp

我的数据库中有这个表:

Message table

sentBysentTo是FK到User表。

在这张桌子上,我有用户之间的消息:

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
-------+----------+------------------+-----------------

我需要知道用户1与之交谈的用户以及与用户1交谈的用户。在这种情况下,对于用户2,3和4(请注意,用户4已向用户1发送了消息,但用户1尚未发送任何消息)。

第二个问题是:我如何才能获得每个用户的最后一条消息?我要求将最新消息发送给用户。

例如,如果我询问用户1,则用户2的最新消息是:msg body 2.而用户3的最新消息是msg body 5.

如何在一个SQL SELECT语句中获取该信息?或者我可能需要两个选择。

我正在尝试做一些像 WhatsApp 这样的事情。你有一个聊天屏幕,其中包含我与之交谈的用户列表(我的第一个问题),以及带有它们的最后一条消息(我的第二个问题)。

也许我可以创建另一个名为Conversation的表,将sentBysentTo移动到该表,并且还会发送包含发送日期的消息,但我认为这不可能是一个很好的设计。

我的两个问题的结果是:

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
-------+----------+------------------+-----------------

2 个答案:

答案 0 :(得分:4)

以下查询将为您提供用户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

毋庸置疑,您可以更改where子句中的条件,以便为任何用户获得类似的结果。

但是,我的方法是创建一个视图,然后从中进行选择,如下所示:

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;

我猜这也可能对其他用途有用。

编辑:将user更改为auser,sqlserver停止抱怨以避免[] ...

答案 1 :(得分:0)

选择在sentBy上过滤并按sentTo分组的最大dateSent,然后将此结果连接回原始表。

修改:我看到了问题。您想要收到用户发送的消息的最大消息吗?

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