使用 CTE 编写 SQL 请求并计算其行数
Writing a SQL request with a CTE and counting its rows
我有两个 table:Contacts
和 Messages
。我想获取一个不属于任何 table 的聊天结构(换句话说:没有 Chats
table 我只想构建一个查询)此查询应包含:
- A
contact
我在那个聊天中指的是
- 我和那个联系人之间的
lastMessage
(如果我没有收到那个联系人的最后一条消息 - 我应该不会从那个联系人那里得到具体的结果)
unreadCount
表明该对话中有多少消息尚未阅读。
我的 table 是:
- 联系人
uniqueId
(斑点)
username
(文字)
- 消息
isRead
(布尔)
sender
(斑点)
receiver
(斑点)
timestamp
(整数)
我最远的是这个:
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
如果您有任何问题,请给我留言谢谢。
我有两个 table:Contacts
和 Messages
。我想获取一个不属于任何 table 的聊天结构(换句话说:没有 Chats
table 我只想构建一个查询)此查询应包含:
- A
contact
我在那个聊天中指的是 - 我和那个联系人之间的
lastMessage
(如果我没有收到那个联系人的最后一条消息 - 我应该不会从那个联系人那里得到具体的结果) unreadCount
表明该对话中有多少消息尚未阅读。
我的 table 是:
- 联系人
uniqueId
(斑点)username
(文字)
- 消息
isRead
(布尔)sender
(斑点)receiver
(斑点)timestamp
(整数)
我最远的是这个:
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
如果您有任何问题,请给我留言谢谢。