使用 CTE 编写 SQL 请求并计算其行数

Writing a SQL request with a CTE and counting its rows

我有两个 table:ContactsMessages。我想获取一个不属于任何 table 的聊天结构(换句话说:没有 Chats table 我只想构建一个查询)此查询应包含:

我的 table 是:

  1. 联系人
  1. 消息

我最远的是这个:

WITH 
"lastMessage" AS (
    SELECT *, MAX("timestamp") 
    FROM "messages" 
    GROUP BY "sender", "receiver"
),
"unreadCount" AS (
    SELECT COUNT(*)
    FROM "messages" WHERE "isRead" = 0
    GROUP BY "sender"
)

SELECT "contacts".*, "unreadCount".*, "lastMessage".* 
FROM "contacts" 

JOIN "lastMessage" 
ON ("lastMessage"."sender" = "contacts"."uniqueId") 
OR ("lastMessage"."receiver" = "contacts"."uniqueId") 

LEFT JOIN "unreadCount" 
ON "unreadCount"."sender" = "contacts"."uniqueId" 

ORDER BY "lastMessage"."timestamp" DESC

PS:一条消息的sender/receiver等于一个联系人的uniqueId(或者我自己的uniqueId,反之亦然)

当前结果的问题是它为每个联系人获取重复的聊天记录(考虑到每个联系人都有已发送和已接收的消息)并且无法获取未读内容

编辑 1:这就是我要找的...

我的数据库中有:

Contacts table:

- username: John Doe, 
  uniqueId: (Blob...)

- username: Alice Shrek, 
  uniqueId: (Blob...)

Messages table:

- sender: (Blob from my uniqueId), 
  receiver: (Blob from John Doe), 
  isRead: true, 
  timestamp: ...1

- sender: (Blob from John Doe), 
  receiver: (Blob from my uniqueId), 
  isRead: false, 
  timestamp: ...2

- sender: (Blob from my uniqueId), 
  receiver: (Blob from Alice Shrek), 
  isRead: true, 
  timestamp: ...3

- sender: (Blob from Alice Shrek), 
  receiver: (Blob from my uniqueId), 
  isRead: false, 
  timestamp: ...4

(换句话说,这个假设场景我有两个联系人和两条消息,每个联系人都有。我发送的一条消息是已读消息,我收到的消息是未读消息)

我希望我的查询获取这些:

- username: John Doe, 
  uniqueId: (Blob from John doe), 
  sender: (Blob from John doe)
  receiver: (Blob from my uniqueId)
  timestamp: ...2
  unreadCount: 1

- username: Alice Shrek, 
  uniqueId: (Blob from Alice Shrek), 
  sender: (Blob from Alice Shrek)
  receiver: (Blob from my uniqueId)
  timestamp: ...4
  unreadCount: 1

我想这里面有你想要的一切......编辑:第一遍未读未读计数!

DECLARE @Me int = 104;

with cteConatacts as (
    SELECT * FROM (VALUES (101, 'Bob'), (102, 'Alice')
        , (103, 'Chris'), (104, 'Me')) as Contacts(ContactID, ContactName)
), cteMessages as (
    SELECT * FROM (VALUES (1, 101, 103, 1001, 'Hi Chris')
        , (1, 101, 104, 1002, 'Hi friend')
        , (1, 104, 101, 1003, 'Hi Bob')
        , (0, 104, 102, 1004, 'Hi Alice')
    ) as Mess(IsRead, SenderID, RecvID, Timestmp, MsgBody)
), cteThreadsWithMe as (
    SELECT 'Sent' as Direction, @Me as MyID, M.RecvID as TheirID,  M.* 
    FROM cteMessages as M 
    WHERE M.SenderID = @Me 
    UNION ALL
    SELECT 'Recv' as Direction, @Me as MyID, M.SenderID as TheirID,  M.* 
    FROM cteMessages as M 
    WHERE M.RecvID = @Me 
), cteGrouped as (
    SELECT * 
        , ROW_NUMBER() OVER (PARTITION BY MyID, TheirID ORDER BY Timestmp DESC) as Newness
    FROM cteThreadsWithMe
), cteReadCounts as (
    SELECT G.MyID, G.TheirID, Count(*) as MessageCount, SUM( IsRead) as ReadCount
    FROM cteGrouped as G
    GROUP BY G.MyID, G.TheirID 
)
SELECT CM.ContactName as MyName, CT.ContactName as TheirName,G.* , C.MessageCount - C.ReadCount as UnreadCount
FROM cteGrouped as G 
    INNER JOIN cteConatacts as CM on CM.ContactID = G.MyID 
    INNER JOIN cteConatacts as CT on CT.ContactID = G.TheirID 
    INNER JOIN cteReadCounts as C on C.MyID = G.MyID AND C.TheirID = G.TheirID 
WHERE Newness = 1

输出:

MyName TheirName Direction MyID TheirID IsRead SenderID RecvID Timestmp MsgBody Newness UnreadCount
Me Bob Sent 104 101 1 104 101 1003 Hi Bob 1 0
Me Alice Sent 104 102 0 104 102 1004 Hi Alice 1 1

使用window函数ROW_NUMBER()识别最后​​一条消息,TOTAL()获取每个联系人的未读消息总数:

WITH
  person(uniqueId) AS (VALUES (?)),
  aggregates AS (
    SELECT *, 
           ROW_NUMBER() OVER (PARTITION BY MIN(m.sender, m.receiver), MAX(m.sender, m.receiver) ORDER BY timestamp DESC) rn,
           TOTAL(NOT m.isRead) OVER (PARTITION BY MIN(m.sender, m.receiver), MAX(m.sender, m.receiver)) unreadCount
    FROM Messages m INNER JOIN person p 
    ON p.uniqueId IN (m.sender, m.receiver)
  )
SELECT c.username, c.uniqueId,
       a.sender, a.receiver, a.timestamp, a.unreadCount 
FROM aggregates a CROSS JOIN person p 
INNER JOIN Contacts c 
ON c.uniqueId = CASE WHEN a.sender = p.uniqueId THEN a.receiver ELSE a.sender END
WHERE a.rn = 1;

将 cte person 中的 ? 替换为您的 uniqueId

参见demo

以下查询对您有所帮助。

Declare @Myid int 
set @Myid = 3

select Sender.username [Sender] , receiver.username [Receiver],
Last_msg.isRead,
Last_msg.timestamp,
Last_msg.UnReadCount
From contact
cross apply  (select  top(1) * , sum(case when Messages.isRead = true then 1 else 0 end) over(order by @Myid) UnReadCount
                                            from Messages 
                                                where (contact.uniqueId = Messages.receiver and @Myid = Messages.sender) or 
                                                (contact.uniqueId = Messages.sender and @Myid = Messages.receiver)
                                                order by timestamp desc) Last_msg
join contact Sender on
Sender.uniqueId = Last_msg.sender
join contact receiver on
receiver.uniqueId = Last_msg.receiver

这里只需要确定是Read的数据类型即可。我已经用 0/1 进行了测试,但在你的情况下是真还是假我已经改变了它你只需要确保。 Messages.isRead = true 如果您有任何问题,请给我留言谢谢。