【发布时间】:2020-05-14 05:41:58
【问题描述】:
我有表 ChatMessages、ChatGroups 和 ChatGroupMemberships。一个用户可以在 0..N 个群组中,而在群组中可以有 1..N 条聊天消息。第一条消息是在组启动后创建的,它有点像“活着”的 ping。
我正在优化重建用户对话列表的方式。该列表非常标准,您可能从任何社交网站都知道:
| Chat with User X -> [last message in that chat group]
| Group chat named ABC -> [same]
到目前为止,我所做的是我只是查询了ChatGroupMemberships 的列表,其中userId = X(x 正在登录用户),然后对于此集合中的每个条目,我选择了该组中的最新消息,然后订购服务器上的整个列表(建议在 DB 上执行此操作)。
我在此项目中尽可能使用 NHibernate 创建查询,因此上述功能如下:
public ChatMessage GetLastMessageInGroup(int groupId)
{
return session.CreateCriteria<ChatMessage>()
.AddOrder(Order.Desc("Date"))
.Add(Restrictions.Eq("RecipientId", groupId))
.SetMaxResults(1)
.List<ChatMessage>().FirstOrDefault();
}
现在我很确定这是我在那里所做的一个非常丑陋的解决方案,并且随着用户创建越来越多的聊天组,该对话列表的重建时间开始花费越来越多的时间(我通过 AJAX 这样做,但是仍然)。现在一个用户通常是大约 50 个组的成员。
因此需要对此进行优化,我现在正在做的是我想将该列表的加载拆分为更小的批次。当用户滚动列表时,会动态加载条目。
我正在处理的函数如下所示:
public List<ChatGroupMembershipTimestamp> GetMembershipsWhereUserId(int userId, int take, int skip)
{
}
我花了好几个小时学习 CTE 并提出以下几点:
WITH ChatGroupMemberships AS
(
SELECT p.Date, p.RecipientId, p.RecipientType, p.Id, p.userId
ROW_NUMBER() OVER(PARTITION BY p.RecipientId, p.userId ORDER BY p.Id DESC)
AS rk FROM ChatMessages p
)
SELECT s.date, s.recipientId as groupId, s.recipientType as groupType, s.userId
FROM ChatGroupMemberships s
WHERE s.rk = 1
Order by s.date desc;
这将返回每个组中每个用户的最后(最新)消息。问题是我的用户不是所有这些组的成员,所以我需要以某种方式加入? ChatGroupMemberships 表上并检查是否存在该用户 ID 为 @987654342 的行@
上面的查询说明如下:
我可能过于复杂了,我最初的计划是执行:
select m.id as messageId, groupId, date
from ChatGroupMemberships g
join ChatMessages m on g.groupId = m.recipientId
where g.userId = XXX <-- user's id
order by m.date desc
产量:
但在这里我只需要我尝试对上面的查询执行的每个 groupId 的最上面一行。 抱歉,这可能会造成混淆(由于我缺乏适当的术语/知识),这是一个相当长的问题。
如果需要任何澄清,我非常乐意合作。
最后,我附上表格的设计方案
【问题讨论】:
-
如果我理解这一点,您可以在 CTE 中添加连接,问题是您如何传递用户 ID(如果您能够将值从应用程序传递给 SQL)
-
我可以将值传递给 sql,这没问题。你介意用那个加入发布答案吗?真的很有帮助,谢谢
-
此时,我的建议是创建一个Minimal Reproducible Example,其中包含可消耗形式的示例数据。
标签: sql sql-server common-table-expression