2011-07-26 25 views
2

这是执行左外最有效的方式加入LINQ,如果我必须在开始和结束日期执行下列操作...如何编写一个LINQ查询,将子表过滤到特定时间段并汇总子表的结果?

  • 筛选表2。
  • 表1中的所有行都必须保留,即使表2的过滤没有返回任何行。
  • 结果必须进行分组,以便表2中的列求和。

例如(示例代码变量名称因专有原因而更改),假设我有一个包含两个表的数据库。表1列出了具有建筑物代码,门ID和当前状态(打开或关闭)的门 - 建筑物代码和门ID是主键。表2列出了所有门的事件(一个事件是开放或关闭)加上一个时间戳。所以列是建筑代码,门ID,时间戳,打开,关闭。打开和关闭是相应事件的列中有1的整数。建筑密码和门ID两个表格之间存在外键关系。

对于我的查询,我需要返回所有唯一门的列表,包括当前的门状态以及所选时间段内所有开门和关门事件的总和。 即使在所选时间段内没有发生任何事件,也必须为每扇门返回条目。

下面是我能想到的最好的LINQ代码。它有效,但它看起来效率很低,很难理解。你如何使它更高效,更易于理解?

var query = 
    from doors in Context.Doors 
    join fevents in 
     (
     from events in db.Events 
     where events.TimeStamp >= date1 && events.TimeStamp <= date2 
     select new { events.BuildingCode, events.DoorID, events.TimeStamp, events.Opening, events.Closing } 
     ) 
    on new { doors.BuildingCode, doors.DoorID } equals { fevents.BuildingCode, fevents.DoorID } 
    into g1 
    from c in g1.DefaultIfEmpty() 
    group c by new 
    { 
     doors.BuildingCode, 
     doors.DoorID, 
     doors.DoorStatus 
    } into g2 
    select new 
    { 
     BuildingCode = g2.Key.BuildingCode, 
     DoorID = g2.Key.DoorID, 
     Status = g2.Key.DoorStatus 
     NumOpenings = g2.Sum(i => (i == null ? 0 : i.Opening)), 
     NumClosings = g2.Sum(i => (i == null ? 0 : i.Closing)) 
    }; 

回答

2

我认为这是稍微容易阅读

var query = 
    from doors in Context.Doors 
    from c in db.Events 
       .Where(events => doors.BuildingCode == events.BuildingCode) 
       .Where(events => doors.DoorID == events.DoorID) 
       .Where(events => events.TimeStamp >= date1 && events.TimeStamp <= date2) 
       .Select(events => new { events.BuildingCode, events.DoorID, events.TimeStamp, events.Opening, events.Closing }) 
       .DefaultIfEmpty() 
    group c by new 
    { 
     doors.BuildingCode, 
     doors.DoorID, 
     doors.DoorStatus 
    } into g2 
    select new 
    { 
     BuildingCode = g2.Key.BuildingCode, 
     DoorID = g2.Key.DoorID, 
     Status = g2.Key.DoorStatus 
     NumOpenings = g2.Sum(i => (i == null ? 0 : i.Opening)), 
     NumClosings = g2.Sum(i => (i == null ? 0 : i.Closing)) 
    }; 
+0

感谢您的回答@aducci。很有帮助。这让我以不同的方式思考问题。我已经发布了我的略有不同的答案作为其他人的参考。 –

2

答案从@adducci帮我想出了一个稍微不同的解决方案,我认为是更可读的,尽管可能效率较低。

var query = 
    from doors in Context.Doors 
    from events in doors.Events 
         .Where(i => i.TimeStamp >= date1 && i.TimeStamp <= date2) 
         .DefaultIfEmpty() 
    group new { doors, events } 
    by doors into g 
    select new 
    { 
     BuildingCode = g.Key.BuildingCode, 
     DoorID = g.Key.DoorID, 
     Status = g.Key.DoorStatus, 
     NumOpenings = g.Sum(i => (i.events == null ? 0 : i.events.Opening)), 
     NumClosings = g.Sum(i => (i.events == null ? 0 : i.events.Closing)) 
    }; 

注意,对于由日期滤波的替代方法将是直接在求和函数,如下,但这是效率要低得多因为所有记录将被从数据库中检索到的,然后在本地过滤。

... 
//from events in doors.Events 
//     .Where(i => i.TimeStamp >= date1 && i.TimeStamp <= date2) 
//     .DefaultIfEmpty() 
from events in doors.Events 
        .DefaultIfEmpty() 
... 
NumOpenings = g.Sum(i => (i.events == null ? 0 : (i.events.Timestamp >= date1 && i.events.TimeStamp <= date2) ? i.events.Opening : 0)), 
NumClosings = g.Sum(i => (i.events == null ? 0 : (i.events.Timestamp >= date1 && i.events.TimeStamp <= date2) ? i.events.Closing : 0)) 
...