如何编写一个 LINQ 查询来将子表过滤到特定时间段并对子表的结果求和?

发布于 2024-11-26 10:53:29 字数 1379 浏览 2 评论 0原文

如果我必须执行以下操作,那么这是在 LINQ 中执行左外连接的最有效方法...

  • 按开始日期和结束日期过滤表 2。
  • 即使表 2 的过滤未返回任何行,表 1 中的所有行也必须保留。
  • 必须对结果进行分组,以便对表 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))
    };

That is the most efficient way to perform a left outer join in LINQ if I must do the following...

  • Filter Table 2 by a beginning and ending date.
  • All rows in Table 1 must remain, even if the filtering of Table 2 returns no rows.
  • The result must be grouped so that the columns from Table 2 get summed.

For example (example code variable names changed for propietary reasons), supposed I have a database with two tables. Table 1 has a list of doors with a building code, door ID and current status (open or closed) - the building code and door ID are the primary key. Table 2 has a list of events for all doors (an event is an opening or closing) plus a timestamp. So the columns are building code, door ID, timestamp, opening, closing. Opening and closing are integers with a 1 in the column for the appropriate event. There is a foreign key relationship between the two tables on the building code and door ID.

For my query I need to return a list of all the unique doors with the current door status and a sum of all the opening and closing events for a selected time period. An entry must be returned for each door, even if no events occured during the selected time period.

Below is the best LINQ code I could come up with. It works, but it seems really inefficient and hard to understand. How would you make it more efficient and easier to understand?

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))
    };

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

厌味 2024-12-03 10:53:29

我认为这更容易阅读

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))
    };

I think this is slightly easier to read

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))
    };
一张白纸 2024-12-03 10:53:29

@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))
...

The answer from @adducci helped me come up with a slightly different solution that I think is even more readable, albeit possibly less efficient.

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))
    };

Note that an alternative method for filtering by date would be directly in the summing function, as below, but this is much less efficient since all records would be retrieved from the database and then filtered locally.

...
//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))
...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文