SQL 查询中的 CTE 范围
SQL CTE scope in query
我有 tables ChatMessages
、ChatGroups
和 ChatGroupMemberships
。一个用户可以在 0..N 个组中,组中可以有 1..N 个聊天消息。第一条消息是在组启动后创建的,它有点像 "alive" ping。
我正在优化重建用户对话列表的方式。该列表非常标准,您可能从任何社交网站都知道它:
| Chat with User X -> [last message in that chat group]
| Group chat named ABC -> [same]
到目前为止我所做的是我简单地查询了 ChatGroupMemberships
的列表,其中 userId = X
(x 正在登录用户)然后对于这个集合中的每个条目我都选择了最新消息在该组中,然后在服务器上订购整个列表(而不是在数据库上这样做)。
我在这个项目中尽可能使用 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;
每个组中每个用户的returns 最后一条(最新)消息。问题是我的用户不是每个这些组的成员所以我需要以某种方式 join? 在 ChatGroupMemberships
table 上检查是否有该用户 ID 为 userId
的行
上面的查询说明如下:
我可能也把这个复杂化了,我原来的计划是执行:
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 的最顶行,我试图用上面的查询来做。
抱歉,这可能会造成混淆(由于我缺乏适当的术语/知识),而且这是一个相当长的问题。
如果需要任何说明,我非常乐意合作。
最后附上tables
的设计方案
聊天消息:
聊天组成员资格:
聊天组:
WITH messages_ranked 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
JOIN ChatGroupMemberships g
on p.recipientId = g.groupId
where g.user_id = XXX
)
SELECT s.date, s.recipientId as groupId, s.recipientType as groupType, s.userId
FROM messages_ranked s
WHERE s.rk = 1
Order by s.date desc;
我看不到你的照片,但如果你的计划只是制作你的 'original plan' 的最新版本,那么它会是这样的:
with chatMessages as (
select m.*,
rk = row_number() over(
partition by m.recipientid, m.userid
order by m.id desc
)
from chatMessages m
)
select m.id as messageId, g.groupId, m.date
from chatGroupMemberships g
join chatMessages m on g.groupId = m.recipientId and m.rk = 1
where g.userId = XXX <-- user's id
order by m.date desc
要点是,您不要试图从 chatMessages
中得到 chatGroupMemberships
。您只需对 chatMessages
进行所需的过滤,然后像以前一样使用它。
话虽这么说。如果您担心的是 'my user is not a member of every of these groups',我不知道您的原始查询在任何意义上都对您有用。
根据您的 C# 代码,.Add(Restrictions.Eq("RecipientId", groupId))
,我想知道您是否可能需要更改
g.userId = XXX
到
m.recipientId = XXX
无论如何,为什么 g.groupId
匹配 m.recipientId
。这似乎是一个错误,如果不是,也很奇怪。
我有 tables ChatMessages
、ChatGroups
和 ChatGroupMemberships
。一个用户可以在 0..N 个组中,组中可以有 1..N 个聊天消息。第一条消息是在组启动后创建的,它有点像 "alive" ping。
我正在优化重建用户对话列表的方式。该列表非常标准,您可能从任何社交网站都知道它:
| Chat with User X -> [last message in that chat group]
| Group chat named ABC -> [same]
到目前为止我所做的是我简单地查询了 ChatGroupMemberships
的列表,其中 userId = X
(x 正在登录用户)然后对于这个集合中的每个条目我都选择了最新消息在该组中,然后在服务器上订购整个列表(而不是在数据库上这样做)。
我在这个项目中尽可能使用 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;
每个组中每个用户的returns 最后一条(最新)消息。问题是我的用户不是每个这些组的成员所以我需要以某种方式 join? 在 ChatGroupMemberships
table 上检查是否有该用户 ID 为 userId
上面的查询说明如下:
我可能也把这个复杂化了,我原来的计划是执行:
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 的最顶行,我试图用上面的查询来做。 抱歉,这可能会造成混淆(由于我缺乏适当的术语/知识),而且这是一个相当长的问题。
如果需要任何说明,我非常乐意合作。
最后附上tables
的设计方案聊天消息:
聊天组成员资格:
聊天组:
WITH messages_ranked 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
JOIN ChatGroupMemberships g
on p.recipientId = g.groupId
where g.user_id = XXX
)
SELECT s.date, s.recipientId as groupId, s.recipientType as groupType, s.userId
FROM messages_ranked s
WHERE s.rk = 1
Order by s.date desc;
我看不到你的照片,但如果你的计划只是制作你的 'original plan' 的最新版本,那么它会是这样的:
with chatMessages as (
select m.*,
rk = row_number() over(
partition by m.recipientid, m.userid
order by m.id desc
)
from chatMessages m
)
select m.id as messageId, g.groupId, m.date
from chatGroupMemberships g
join chatMessages m on g.groupId = m.recipientId and m.rk = 1
where g.userId = XXX <-- user's id
order by m.date desc
要点是,您不要试图从 chatMessages
中得到 chatGroupMemberships
。您只需对 chatMessages
进行所需的过滤,然后像以前一样使用它。
话虽这么说。如果您担心的是 'my user is not a member of every of these groups',我不知道您的原始查询在任何意义上都对您有用。
根据您的 C# 代码,.Add(Restrictions.Eq("RecipientId", groupId))
,我想知道您是否可能需要更改
g.userId = XXX
到
m.recipientId = XXX
无论如何,为什么 g.groupId
匹配 m.recipientId
。这似乎是一个错误,如果不是,也很奇怪。