Получить только четыре объекта по типу и дате LINQ

Я новичок в программировании, прошу совета.

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

Задача получить из списка пациентов только четырех пациентов, по одному пациенту каждого типа и иметь последнюю дату последнего визита к врачу.

Я могу сделать это четырьмя отдельными запросами к db.

var childPatient = await db.patientsOfClinic.Where(x => x.TypeOfPatient == "child").OrderByDescending(x => x.UploadedDate).FirstOrDefaultAsync();

Но нужно обрабатывать это одним запросом к БД.

Пробовал такой запрос, но он неверный.

var patientsOfClinic = await db.patientsOfClinic.Where(x => x.ClinicId == clinicId && x.TypeOfPatient == "child" && x.TypeOfPatient == "teenager" && x.TypeOfPatient == "adult" && x.TypeOfPatient == "oldage").OrderByDescending(y => y.UploadedDate).ToListAsync();

Спасибо за любой совет.

вы используете и для TypeOfPatient, поэтому он никогда не будет возвращать какое-либо значение, пациент не может использоваться как ребенком, так и подростком или '||' вместо.

coder_b 13.12.2020 14:04

@coder_b, если я буду использовать или ||, я не получу все типы пациентов

Alex Kubin 13.12.2020 14:10

ну я думал только у тебя 4 типа. в таком случае, какие свойства вы хотели бы видеть в коллекцииpatientsOfClinic. так можно ли группировать и выбирать свойства, которые вы хотите отправить как анонимную коллекцию?

coder_b 13.12.2020 14:27
Стоит ли изучать 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
3
72
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Этот запрос требует группировки и присоединения к этой группе.

// make reusable filtered set
var filtered = db.patientsOfClinic.Where(p => x.ClinicId == clinicId);

var grouped = 
   from p in filtered
   group p by p.TypeOfPatient into g
   select new
   {
      TypeOfPatient = g.Key,
      UploadedDate = g.Max(x.UploadedDate)
   };

var result = 
   from p in filtered
   join g in grouped on new { p.TypeOfPatient, p.UploadedDate } 
                 equals new { g.TypeOfPatient, g.UploadedDate }
   select p;
   

в любом случае, сначала я должен получить весь список из базы данных, а затем сделать, как вы показываете? если, например, у меня есть пятьдесят тысяч пациентов и я сначала получу весь список из пятидесяти тысяч, а затем, чтобы получить только четырех пациентов по типам,

Alex Kubin 13.12.2020 14:08

Это единственный запрос к базе данных. просто объединены из переменных.

Svyatoslav Danyliv 13.12.2020 14:11

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

Alex Kubin 13.12.2020 14:15

Если не звонить ToList, ToArray и т.д. - из базы данных ничего не берется. Это декомпозиция запроса - вы создаете части запроса и объединяете их.

Svyatoslav Danyliv 13.12.2020 14:17

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

Alex Kubin 13.12.2020 14:21

@AlexKubin, вместо того, чтобы начинать обсуждение, просто запустите запрос. Я не новичок в SQL и особенно в LINQ. И я знаю любые варианты этого запроса. Здесь быстрее всего использовать ROW_NUMBER, но EF не позволит этого сделать.

Svyatoslav Danyliv 13.12.2020 14:23
var result =  from p in db.patientsOfClinic 
    group p by p.TypeOfPatient into grp 
    select grp.OrderByDescending(o => o.UploadedDate).FirstOrDefault();

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

Alex Kubin 13.12.2020 14:17

@AlexKubin, ты пробовал? Теоретически это правильный запрос, но он не будет переведен в SQL.

Svyatoslav Danyliv 13.12.2020 14:20

@Alex Kubin: Вы получите по одному клиенту каждого типа с последними UploadedDate!

Jackdaw 13.12.2020 14:20

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

Alex Kubin 13.12.2020 14:23

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

Alex Kubin 13.12.2020 14:24

@Alex Kubin: Вы правы, я исправил, сортировка изменена на .OrderByDescending(). Сейчас будет последний. Если вы предоставите MCVE можно будет помочь точнее.

Jackdaw 13.12.2020 14:38

@Alex Kubin: Кстати, если это БД с большим объемом данных, то предпочтительнее описать этот запрос в процедуре хранения, которая будет выполняться на сервере и возвращать только конечный результат.

Jackdaw 13.12.2020 14:52

в этом случае вы можете использовать лямбда-выражения для возврата анонимного объекта, который дает вам список типов патентов с патентом, где максимальная дата загрузки верна.

Тестировать:

public class PatientsOfClinic
{
    public int Id { get; set; }
    public int ClinicId { get; set; }
    public string TypeOfPatient { get; set; }
    public DateTime UploadedDate { get; set; }
}


     var listOfPatents = new List<PatientsOfClinic>
           {
               new PatientsOfClinic
               {
                   Id =1 ,  
                   ClinicId=1,
                   TypeOfPatient = "child",
                   UploadedDate = DateTime.Today
               },
               new PatientsOfClinic
               {
                   Id =1 ,
                   ClinicId=1,
                   TypeOfPatient = "teenager",
                   UploadedDate = DateTime.Today.AddDays(1)
               }
            };
            var clinicId = 1;
                //listOfPatents
    //        .Where(c => c.ClinicId == clinicId)
    //        .GroupBy(x => x.TypeOfPatient)
    //        .Select(g => new
    //        {
    //            TypeOfPatient = g.Key,
    //            Patient = g.OrderByDescending(x => x.UploadedDate)
    //                             .FirstOrDefault()
    //        });

    listOfPatents
        .Where(c => c.ClinicId == clinicId)
        .GroupBy(x =>  x.TypeOfPatient)
        .SelectMany(g => g.Where(u=>u.UploadedDate == g.Max(m =>m.UploadedDate)))
}

я думаю, что Patient = g.OrderByDescending(x => x.UploadedDate).FirstOrDefault() будет нормально работать для linq to objects, но не будет переведен в sql с помощью EF.

Mohammed Sajid 13.12.2020 15:00

@Sajid обновил код для перевода запроса EF, надеюсь, это сработает

coder_b 13.12.2020 15:13
Ответ принят как подходящий

Что вы можете сделать, так это следующее:

Сначала отсортируйте пациентов по дате загрузки.

Затем сгруппируйте их по типу. вы получите 4 группы, упорядоченные по дате.

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

Это даст список из 4 пациентов, по одному из каждой группы, с последней датой загрузки.

var patients = db.patientsOfClinic.OrderByDescending(patient => patient.UploadDate)
                                  .GroupBy(patient => patient.TypeOfPatient)
                                  .Select(g => g.FirstOrDefault());

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

Первое, что вам нужно сделать, когда кто-то говорит: мне нужны элементы с определенным значением свойства, вы должны подумать об одном из перегрузок Queryable.GroupBy

С помощью этого метода вы создаете группы пациентов, которые имеют некоторую общую ценность. В вашем случае вы создаете группы пациентов с одинаковым значением свойства TypeOfPatient.

В результате вы получаете группу пациентов со значением TypeOfPatient, равным Child, и группу пациентов со значением TypeOfPatient, равным Adult, и т. д.

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

... каждого типа и имеет последнюю дату последнего визита к врачу.

Для этого вы используете параметр resultSelector. Этот параметр представляет собой метод с двумя входными значениями: общий элемент, в вашем случае TypeOfPatient и все пациенты, входящие в группу. На выходе один объект. В вашем случае: вы хотите вывести пациента из группы, у которой есть самые свежие данные о посещении врача.

IQueryable<Patient> patients = ...
var result = patients.GroupBy(patient => patient.TypeOfPatient,

    // parameter resultSelector: take each found TypeOfPatient,
    // with all Patients that have this value for TypeOfPatient,
    // to make one new:
    (typeOfPatient, patientsOfThisType) =>

        // order all patients in this group by descending value for UploadedDate
        // and take the first
        patientsWithOfType.OrderByDescending(patient => patient.UploadedDate)
                          .FirstOrDefault());

Вы можете быть уверены, что пустых групп не будет. Так что всегда будет первый.

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