Как написать запрос LINQ, который фильтрует вложенную таблицу за определенный период времени и суммирует результаты вложенной таблицы? - PullRequest
2 голосов
/ 26 июля 2011

Это самый эффективный способ выполнить левое внешнее объединение в LINQ, если я должен сделать следующее ...

  • Фильтр таблицы 2 по дате начала и окончания.
  • Все строки в таблице 1 должны остаться, даже если фильтрация таблицы 2 не возвращает строк.
  • Результат должен быть сгруппирован так, чтобы столбцы из таблицы 2 суммировались.

Например (например, имена переменных кода изменены по причинам, связанным с проповедью), предполагается, что у меня есть база данных с двумя таблицами Таблица 1 содержит список дверей с кодом здания, идентификатором двери и текущим состоянием (открыт или закрыт) - основной код - код здания и идентификатор двери. Таблица 2 содержит список событий для всех дверей (событие является открытием или закрытием), а также отметку времени. Таким образом, столбцы - это строительный код, идентификатор двери, отметка времени, открытие, закрытие. Открытие и закрытие - целые числа с 1 в столбце для соответствующего события. Между двумя таблицами существует код внешнего ключа в строительном коде и идентификаторе двери.

Для моего запроса мне нужно вернуть список всех уникальных дверей с текущим состоянием двери и суммой всех событий открытия и закрытия за выбранный период времени. Запись должна быть возвращена для каждой двери, даже если в течение выбранного периода времени не произошло никаких событий.

Ниже приведен лучший код 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 ]

2 голосов
/ 27 июля 2011

Ответ @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))
...
2 голосов
/ 26 июля 2011

Я думаю, что это немного легче читать

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