sql查询通过对话分组数据

时间:2016-02-07 00:29:32

标签: sql sql-server tsql

我需要帮助完成这项任务: 我在MsSql数据库中有一个表,我存储了用户间的消息。我需要一个查询来为每个用户对话选择最后一条消息(WhatApp类似于用户的会话列表)。

表格结构( x是消息ID,GUID,每条消息都是唯一的,txtMsg是短信):

| messageId | fromUserId | toUserId | Message |     sentDate     |
|     x     |      1     |     2    |  txtMsg | 1.1.2016 1:00:00 |
|     x     |      1     |     2    |  txtMsg | 1.1.2016 1:00:01 |
|     x     |      1     |     2    |  txtMsg | 1.1.2016 1:00:02 |
|     x     |      2     |     1    |  txtMsg | 1.1.2016 1:00:03 |
|     x     |      3     |     1    |  txtMsg | 1.1.2016 1:00:04 |
|     x     |      4     |     1    |  txtMsg | 1.1.2016 1:00:05 |
|     x     |      2     |     3    |  txtMsg | 1.1.2016 1:00:06 |
|     x     |      2     |     4    |  txtMsg | 1.1.2016 1:00:07 |
|     x     |      2     |     3    |  txtMsg | 1.1.2016 1:00:08 |
|     x     |      1     |     5    |  txtMsg | 1.1.2016 1:00:09 |
|     x     |      3     |     1    |  txtMsg | 1.1.2016 1:00:10 |
|     x     |      2     |     4    |  txtMsg | 1.1.2016 1:00:11 |
|     x     |      2     |     5    |  txtMsg | 1.1.2016 1:00:12 |
|     x     |      1     |     2    |  txtMsg | 1.1.2016 1:00:13 |

预期结果示例,对于id = 1的用户(sentDate是最新日期):

|     x     |      1     |     2    |  txtMsg | 1.1.2016 1:00:13 |
|     x     |      3     |     1    |  txtMsg | 1.1.2016 1:00:10 |
|     x     |      4     |     1    |  txtMsg | 1.1.2016 1:00:05 |
|     x     |      1     |     5    |  txtMsg | 1.1.2016 1:00:09 |

什么SQL查询会创建这样的结果?

谢谢!

稍后编辑: 我在这里添加了sqlfiddle示例:

http://sqlfiddle.com/#!3/6ffbe/15

3 个答案:

答案 0 :(得分:5)

您可以使用案例逻辑row_nubmer来获取最小和最大的用户ID:

select m.*
from (select m.*,
             row_number() over (partition by  (case when fromuserid < touserid then fromuserid else touserid end),
                                              (case when fromuserid < touserid then touserid else fromuserid end)
                                order by sentDate desc
                               ) as seqnum
      from messages m
     ) m
where seqnum = 1;

编辑:

此SQL适用于SQL Fiddle

select m.*
from (select m.*,
             row_number() over (partition by  (case when fromuser < touser then fromuser else touser end),
                                              (case when fromuser < touser then touser else fromuser end)
                                order by createdAt desc
                               ) as seqnum
      from messages m
     ) m
where seqnum = 1;

答案 1 :(得分:1)

的内容如何
SELECT * FROM MessageData
WHERE messageID in
  (SELECT messageID FROM
    SELECT Max(sentDate), Max(messageID) messageID FROM
      MessageData m1 
       INNER JOIN
      MessageData m2
       ON
        m1.fromUserId = m2.toUserId
       OR
        m2.fromUserId = m1.toUserId
    GROUP BY sentDate, MessageID
  )

答案 2 :(得分:1)

与Gordon Linoff的回答非常相似。我刚为用户ID = 1添加了一个过滤器(fromto)。我将查询放在您的SQL Fiddle

ROW_NUMBER将所有行划分为对话,然后从最新开始(按createdAt排序)为对话中的每一行分配序号。

WITH
CTE
AS
(
    SELECT
        M.Id
        ,M.fromUser
        ,M.toUser
        ,M.message
        ,M.createdAt
        ,ROW_NUMBER() OVER (
            PARTITION BY
                CASE WHEN M.fromUser < M.toUser THEN M.fromUser ELSE M.toUser END,
                CASE WHEN M.fromUser > M.toUser THEN M.fromUser ELSE M.toUser END
            ORDER BY M.createdAt DESC) AS rn
    FROM Messages AS M
    WHERE 
        M.fromUser = 1
        OR M.toUser = 1
)
SELECT
    CTE.Id
    ,CTE.fromUser
    ,CTE.toUser
    ,CTE.message
    ,CTE.createdAt
FROM CTE
WHERE rn = 1
ORDER BY fromUser, toUser;