【问题标题】:LINQ: Self join query, how to accomplish this?LINQ:自连接查询,如何完成这个?
【发布时间】:2011-02-17 09:14:45
【问题描述】:

谁能帮忙?

我有 1 个类,基本上它包含成员,并且在该类中是一个列表。

我在列表中的成员也是......所以基本上它是这样的,

我有 2 个成员,每个成员都有多个会话。

我希望只为每个成员返回 1 个会话。

我做了一个 LINQ 查询,但是当然不行……

我想我需要做一个自我加入,有什么想法吗?

基本上我的错误是我的子查询自联接中不存在 m。

var sessions =  
from m in this.members                     
join s in
(
    from se in m.Sessions
    group se by se.Name into g
    select new {Name = g.Key, SessioEndTime = g.Max(a=>a.SessioEndTime)}
)   
on m.Name equals s.Name                    
select new { MemberName = m.Name, SessionTime = s.SessioEndTime}

如果有人提供任何反馈,我将不胜感激。

提前致谢。

编辑

好的,我设法做到了,但这是最好的方法吗?

var sessions =  
from m in this.members                     
let sn = m.Sessions.OrderByDescending(a => a.SessionEndTime).FirstOrDefault()                
select new { MemberName = m.Name, SessionTime = sn.SessioEndTime}

这种方式 sn 包含 1 条记录,但我可以访问所有属性...

但这是使用 LET 的最佳方式吗?

谢谢。

【问题讨论】:

    标签: linq linq-to-sql linq-to-objects self-join


    【解决方案1】:

    除非我遗漏了你需要的东西,不是吗?

    var sessions = 
       from m in members
       select new { 
          MemberName = m.Name, 
          SessionTime = m.Sessions.Max(s => s.SessioEndTime)
       };
    

    您必须改变对 LINQ 查询的看法,更多地从对象的角度而不是从 SQL 实现的角度思考。我需要什么?我需要所有成员,每个成员都有其最近的会话结束时间,然后采取行动。

    编辑: 您使用的 let 选项没问题,只要记住 FirstOrDefault 将返回 null 如果成员有一个空的 Session 列表,然后 sn.SessionEndTime 命中 null 引用。另一方面,如果您确定每个成员至少有一个会话,请改用 First 或聚合。 也不要在 let 中使用FirstOrDefault(),它会弄乱 LINQ 并阻止它与主服务器绑定(导致每个主服务器使用单独的 SQL 查询来检测丢失的子集),因此使用 let 的可用查询是:

    from m in Members                     
    let sn = m.Sessions.Max(s => s.SessioEndTime)                
    select new { MemberName = m.Name, SessionTime = sn};
    
    from m in Members                     
    let sn = m.Sessions.OrderByDescending(a => a.SessioEndTime).First()              
    select new { MemberName = m.Name, SessionTime = sn.SessioEndTime};
    

    对于ordering vs Max aggregation,两个查询都会生成一个子查询:

    -- MAX    
    SELECT [t0].[Name] AS [MemberName], (
        SELECT MAX([t1].[SessioEndTime])
        FROM [Session] AS [t1]
        WHERE [t1].[memberId] = [t0].[id]
        ) AS [SessionTime]
    FROM [Member] AS [t0]
    GO
    
    -- ordering
    SELECT [t0].[Name] AS [MemberName], (
        SELECT [t2].[SessioEndTime]
        FROM (
            SELECT TOP (1) [t1].[SessioEndTime]
            FROM [Session] AS [t1]
            WHERE [t1].[memberId] = [t0].[id]
            ORDER BY [t1].[SessioEndTime] DESC
            ) AS [t2]
        ) AS [SessionTime]
    FROM [Member] AS [t0]
    

    在 SessioEndTime 上使用降序索引时,排序脚本的速度大约慢了两倍(您可以获取这些执行计划以自行检查),而没有索引时,它大约慢 5 倍。

    【讨论】:

    • 是的,谢谢,让我们看看我是否正确,我使用了“let”,可以吗?或者,还有更好的方法?所以现在我让 sn = m.Sessions.OrderByDescending(a => a.SessionEndTime).FirstOrDefault(), 现在我有了会话的完整记录,所以我也可以做 sessiontime= sn.SessionEndTIme 和其他属性..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    相关资源
    最近更新 更多