Я новичок в программировании, прошу совета.
У меня есть список пациентов в клинике, у каждого пациента есть один из этих типов: «ребенок», «подросток», «взрослый», «пожилой», а также дата посещения врача.
Задача получить из списка пациентов только четырех пациентов, по одному пациенту каждого типа и иметь последнюю дату последнего визита к врачу.
Я могу сделать это четырьмя отдельными запросами к 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();
Спасибо за любой совет.
@coder_b, если я буду использовать или ||, я не получу все типы пациентов
ну я думал только у тебя 4 типа. в таком случае, какие свойства вы хотели бы видеть в коллекцииpatientsOfClinic. так можно ли группировать и выбирать свойства, которые вы хотите отправить как анонимную коллекцию?





Этот запрос требует группировки и присоединения к этой группе.
// 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;
в любом случае, сначала я должен получить весь список из базы данных, а затем сделать, как вы показываете? если, например, у меня есть пятьдесят тысяч пациентов и я сначала получу весь список из пятидесяти тысяч, а затем, чтобы получить только четырех пациентов по типам,
Это единственный запрос к базе данных. просто объединены из переменных.
да, я понимаю, но я думаю, что нецелесообразно получать весь список из пятидесяти тысяч пациентов из базы данных, если у меня есть список только из четырех пациентов.
Если не звонить ToList, ToArray и т.д. - из базы данных ничего не берется. Это декомпозиция запроса - вы создаете части запроса и объединяете их.
смотри, опять же зачем мне получать Список всего пациента, могу ли я одним запросом сделать что-то и получить Список только четырех пациентов. Я понимаю, что без ToList я не получу никакого списка
@AlexKubin, вместо того, чтобы начинать обсуждение, просто запустите запрос. Я не новичок в SQL и особенно в LINQ. И я знаю любые варианты этого запроса. Здесь быстрее всего использовать ROW_NUMBER, но EF не позволит этого сделать.
var result = from p in db.patientsOfClinic
group p by p.TypeOfPatient into grp
select grp.OrderByDescending(o => o.UploadedDate).FirstOrDefault();
Мне нужно получить список из четырех пациентов с разными типами и последней датой, в этом примере я получу только одного пациента.
@AlexKubin, ты пробовал? Теоретически это правильный запрос, но он не будет переведен в SQL.
@Alex Kubin: Вы получите по одному клиенту каждого типа с последними UploadedDate!
да верно, но мне нужно получить список четырех пациентов с разным типом к последней дате
@Svyatoslav да, я пытался, это близко, но здесь я получаю только одного пациента, мне нужно получить список из четырех пациентов с разным типом к последней дате
@Alex Kubin: Вы правы, я исправил, сортировка изменена на .OrderByDescending(). Сейчас будет последний. Если вы предоставите MCVE можно будет помочь точнее.
@Alex Kubin: Кстати, если это БД с большим объемом данных, то предпочтительнее описать этот запрос в процедуре хранения, которая будет выполняться на сервере и возвращать только конечный результат.
в этом случае вы можете использовать лямбда-выражения для возврата анонимного объекта, который дает вам список типов патентов с патентом, где максимальная дата загрузки верна.
Тестировать:
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.
@Sajid обновил код для перевода запроса EF, надеюсь, это сработает
Что вы можете сделать, так это следующее:
Сначала отсортируйте пациентов по дате загрузки.
Затем сгруппируйте их по типу. вы получите 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());
Вы можете быть уверены, что пустых групп не будет. Так что всегда будет первый.
вы используете и для TypeOfPatient, поэтому он никогда не будет возвращать какое-либо значение, пациент не может использоваться как ребенком, так и подростком или '||' вместо.