Группировать по кратному, включая детей, а затем суммировать

Используя приведенные ниже таблицы, я хотел бы суммировать дозы для каждого участка, который проходит один и тот же курс. Я хочу сначала сгруппировать по курсу, затем по участкам, а затем суммировать дозы.

Радиация

Идентификатор радиации Ид курса Доза 1 1 10 2 1 20 3 2 5

Радиационные сайты

Идентификатор радиации идентификатор сайта 1 1 1 2 1 3 2 3 3 1

Выход

Ид курса идентификатор сайта Общая доза Название курса Сайт 1 1 10 1 курс Селезенка 1 2 10 1 курс Позвоночник 1 3 30 1 курс Глаз 2 1 5 Курс 2 Селезенка

Я попробовал что-то вроде следующего, но не могу уложиться в этом.

    var totals =
    dbContext
        .Radiations
        .Include(r => r.RadiationSites)
        .ThenInclude(s => s.Site)
        .Include(r => r.RadiationCourse)
        .Where(r => r.Nhi == NHI)
        .SelectMany(s => s.RadiationSites, (Radiation, RadiationSites) => new { Radiation, RadiationSites } )
        .GroupBy(r => r.Radiation.RadiationCourseId, r => r.RadiationSites.SiteId);

Возможно ли это вообще? Пожалуйста помоги!

Как вы могли понять из полученных ответов, выполнять соединения с использованием синтаксиса понимания запросов зачастую проще, чем использовать функциональный синтаксис. Каждый синтаксис имеет свои преимущества. Не волнуйтесь, что у вас есть преимущество во времени выполнения.

Flydog57 22.03.2024 05:24

Было бы полезно иметь код классов в вопросе. Кроме того, задавая вопросы об EF, важно знать точную версию, поскольку версии различаются по поддерживаемым формам запросов.

Gert Arnold 22.03.2024 09:59
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
58
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Если у вас есть таблица Sites со столбцами SiteId и SiteName и таблица Courses со столбцами CourseId и CourseName, ваш запрос будет выглядеть примерно так:

var totals = from rad in dbContext.Radiations
             join radSite in dbContext.RadiationSites on rad.RadiationId equals radSite.RadiationId
             join site in dbContext.Sites on radSite.SiteId equals site.SiteId
             join course in dbContext.Courses on rad.CourseId equals course.CourseId
             group new { rad, site.SiteName, course.CourseName } by new { rad.CourseId, radSite.SiteId } into grouped
             select new {
                 CourseId = grouped.Key.CourseId,
                 SiteId = grouped.Key.SiteId,
                 TotalDose = grouped.Sum(x => x.rad.Dose),
                 CourseName = grouped.Select(x => x.CourseName).FirstOrDefault(),
                 SiteName = grouped.Select(x => x.SiteName).FirstOrDefault()
             };

var result = totals.ToList();

Здесь

  • Синтаксис from...join... используется для объединения таблиц на основе их отношения.
  • Синтаксис group... by... to grouped используется для группировки результатов. как по CourseId, так и по SiteId.
  • Синтаксис select new используется для прогнозирования конечного результата, где мы просуммируйте дозы для каждой группы, а также выберите CourseName и SiteName на основе первого вхождения в каждой группе (при условии, что имена согласованы внутри групп).
  • Замените dbContext.Sites и dbContext.Courses фактическим DBSet. свойства из вашего DbContext, если это имена ваших сущности, содержащие сведения о сайте и курсе.

Не используйте соединения явно, если есть свойства навигации. Например, join radSite... можно заменить на from radSite in rad.RadiationSites, а также на другие соединения.

Svyatoslav Danyliv 22.03.2024 10:43

Вот ваши данные как действительный код C#:

var sites = new[]
{
    new { SiteId = 1, Site = "Spleen", },
    new { SiteId = 2, Site = "Spine", },
    new { SiteId = 3, Site = "Eye", },
};

var courses = new[]
{
    new { CourseId = 1, Course = "Course 1", },
    new { CourseId = 2, Course = "Course 2", },
};

var radiations = new[]
{
    new { RadiationId = 1, CourseId = 1, Dose = 10, },
    new { RadiationId = 2, CourseId = 1, Dose = 20, },
    new { RadiationId = 3, CourseId = 2, Dose = 5, },
};

var radiationSites = new[]
{
    new { RadiationId = 1, SiteId = 1, },
    new { RadiationId = 1, SiteId = 2, },
    new { RadiationId = 1, SiteId = 3, },
    new { RadiationId = 2, SiteId = 3, },
    new { RadiationId = 3, SiteId = 1, },
};

Теперь ваш запрос выглядит так:

var query =
    from course in courses
    join radiation in radiations on course.CourseId equals radiation.CourseId
    join radiationSite in radiationSites on radiation.RadiationId equals radiationSite.RadiationId
    join site in sites on radiationSite.SiteId equals site.SiteId
    group radiation.Dose by new
    {
        course.CourseId,
        site.SiteId,
        course.Course,
        site.Site,
    } into gs
    select new
    {
        gs.Key.CourseId,
        gs.Key.SiteId,
        TotalDose = gs.Sum(),
        gs.Key.Course,
        gs.Key.Site,
    };

Это производит:

Ответ принят как подходящий

Что мне не нравится в других ответах, так это то, что:

  • Они используют локальные объекты, которые могут вести себя иначе, чем запросы EF; не каждый запрос, который вы можете написать в локальном стиле LINQ, можно перевести в SQL с помощью EF и/или
  • Они не используют тот факт, что EF знает, как объединять таблицы, поскольку знает связи между ними; все, что вам нужно сделать, это перемещаться по свойствам соединения, которые предоставляет EF, и EF выполняет соединение.
    • Когда у вас есть EF, писать объединяется с самим собой — это все равно, что купить машину и отвезти ее в магазин вместо того, чтобы завести двигатель. Конечно, если двигатель сломается, вам, возможно, придется его толкать, но в основном вы будете стараться ездить на нем.

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

Я воспроизвел вашу БД настолько, насколько смог понять ее из вашего поста:

Не имеет значения, если это не совпадает точно, но при написании кода полезно иметь диаграмму, чтобы облегчить навигацию. Не беспокойтесь о незначительных различиях, например, у вас нет RadiationSiteId.

Главный момент: ваша жизнь станет значительно проще, если вы начнете с RadiationSites; это конец двух отношений, и у него есть только родители. Находиться «в» дочернем объекте и переходить к родительскому объекту обычно намного проще, чем находиться в родительском объекте и выбирать одного из его многочисленных дочерних объектов.

Вам не нужно включать; Включить — это не JOIN. Вы можете прочитать об этом более объемные тома, но Include предназначен для тех случаев, когда вы загружаете целые объекты и хотите сообщить EF, какие еще целые связанные объекты следует добавить. Группирующие запросы, вероятно, не приводят к получению выходного объекта, который полностью является объектом схемы, а вместо этого используют какую-то другую проекцию, например. анонимный тип, поэтому пропустите Include. Вам не требуется Include, чтобы EF мог «видеть» связанные данные, к которым вы обращаетесь — когда вы используете свойства навигации в разделах «Where», «GroupBy», «Select» и т. д., EF увидит, что вы перемещаетесь от одного объекта к другому, и сформирует необходимые соединения для ты на заднем плане

        var totals = await db.RadiationSites
            .Where(rs => rs.Radiation.Nhi == nhi)
            .GroupBy(rs => new { rs.Radiation.CourseId, rs.SiteId, rs.Radiation.Course.CourseName, rs.Site.SiteName })
            .Select(g => new { g.Key.CourseId, g.Key.SiteId, TotalDose = g.Sum(rs => rs.Radiation.Dose), g.Key.CourseName, g.Key.SiteName })
            .ToArrayAsync();

Мы начинаем с RadiationSites и Where, переходя к Radiation, чтобы настаивать на том, что NHI представляет собой некоторую ценность. EF увидит это и присоединится, как бы ему хотелось это сделать.

Поскольку мы находимся внизу в иерархии отношений (представляя это как генеалогическое древо, где дети находятся ниже родителей), группировка столь же проста; мы поднимаемся по дереву ровно настолько, насколько нам нужно. В некоторых базах данных вам может сойти с рук просто группировка по первичному ключу, и БД знает, что неявно все остальные столбцы из этой таблицы должны быть уникальными, но SQLS предпочитает группировать все столбцы, даже если, например, для идентификатора курса, равного 1, имя курса никогда не будет иметь другого значения. Поскольку нам нужно название курса, мы извлекаем его в ключ группировки, потому что позже это упростит жизнь. Мы можем добраться до CourseId, только поднявшись до Radiation.CourseId, но нам придется идти дальше, чтобы узнать имя - Radiation.Course.CourseName

В конечном счете, чтобы не сломать себе голову, нужно посмотреть на желаемый результат; вам нужны CourseId, SiteId, CourseName и SiteName в качестве не сгруппированных (ключевых) столбцов, а сумма дозы — в виде агрегированного столбца. Поместите все эти ключевые столбцы в объект, который вы группируете: new { rs.Radiation.CourseId, rs.SiteId, rs.Radiation.Course.CourseName, rs.Site.SiteName }. Если вы хотите упростить некоторые вещи, вы всегда можете сгруппировать их только по идентификаторам, т. е. { rs.Radiation.CourseId, rs.SiteId }, и получить соответствующие данные позже, возможно, даже с помощью отдельного запроса.

Затем есть Select, где вы снова извлекаете значения ключей (повторяющиеся побитовые значения), но помогаете себе, называя входную переменную Select g или аналогичной, чтобы отразить тот факт, что это группа (набор коллекции RadiationSites) с .Key, указывающим, к чему были сгруппированы все сайты в нем, и сам является коллекцией RadationSites. Вот почему я использую g внутри Select, а rs внутри Sum, потому что сумма пропускает «группу» RadiationSites и суммирует Dose.

И вот оно:

Идеальный ответ и фантастические учения, сопровождающие его. Я подумаю об отправной точке в дальнейшем и дам отличный совет по включению. Большое спасибо!

Tim Long 22.03.2024 22:02

Другие вопросы по теме