LINQ для получения 5 последних записей для каждого отдельного имени - PullRequest
1 голос
/ 04 августа 2020

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

Вот образец таблицы:

id  |   Name   |  Status  | Start      | End     <br />
1   |  Bob     |  Pass    | 2020-01-01 | 2020-01-01<br />
2   |  Chris   |  Pass    | 2020-01-01 | 2020-01-02<br />
3   |  James   |  Fail    | 2020-01-01 | 2020-01-03<br />
4   |  Bob     |  Pass    | 2020-01-01 | 2020-01-04<br />
5   |  Chris   |  Fail    | 2020-01-01 | 2020-01-05<br />
6   |  Bob     |  Pass    | 2020-01-01 | 2020-01-06<br />
7   |  Bob     |  Fail    | 2020-01-01 | 2020-01-07<br />
8   |  Bob     |  Fail    | 2020-01-01 | 2020-01-08<br />
9   |  Chris   |  Pass    | 2020-01-01 | 2020-01-09<br />
10  |  Bob     |  Pass    | 2020-01-01 | 2020-01-10<br />

Я ожидал бы последних 5 записей Боба (из 6), 3 записи Криса и одна запись Джеймса, которая должна быть возвращена.

До сих пор я пытался использовать методы: Четкие имена, затем запрос на основе имени, получение последних 5 записей по endDate и добавление в список. С помощью этого метода я смог правильно выполнить первый запрос. Я печатал три разных имени (Боб, Крис, Джеймс). Однако каждый раз, когда я выполнял запрос с этими именами и получал 5 самых последних записей, он возвращался пустым для всех трех имен. Любая печать, которую я пытался сделать, говорила, что имя переменной было недействительным в соответствии с Visual Studio 2019 ... Итак, я попробовал метод 2

// GET: api/Student/latestRecords
[HttpGet("latestRecords")]
public async Task<ActionResult<IEnumerable<Student>>> GetLatestRecordsOnAllStudent()
{
    var distinctStudentNames = _context.Students.Select(x => x.name).Distinct();

    IQueryable<Student> allRecords = new Student[] { }.AsQueryable();

    foreach (string studentName in distinctStudentNames)
    {
        var newList = _context.Students.OrderByDescending(x => x.endTime).DistinctBy(y => y.name).Select( z => z).Take(5);

        allRecords.Concat(newList);
    }
            
    return allRecords.ToList();*/
}
Использование одного запроса LINQ. С помощью этого метода я смог получить 3 отдельные записи на основе имени, но я не мог получить больше записей, чем это.
// GET: api/Student/latestRecords
[HttpGet("latestRecords")]
public async Task<ActionResult<IEnumerable<Student>>> GetLatestRecordsOnAllStudents()
{
    var distinctStudentsNames = _context.Students.DistinctBy(x => x.name).OrderByDescending(x => x.endTime).Select(z => z).Take(5).ToList();

    return distinctStudentsNames;
}

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

Ответы [ 2 ]

3 голосов
/ 04 августа 2020

Изменить:

Как отметил @NetMerge, это не будет работать в EF Core, поэтому я оставлю ответ здесь на тот случай, если OP все равно захочет взглянуть на него.

Вот образец класса только с двумя свойствами, на которые мы обращаем внимание, и ваш примерный список.

public class Student
{
    public string Name { get; set; }
    public DateTime End { get; set; }
}

var students = new List<Student>
{
    new Student() { Name = "Bob", End = new DateTime(2020, 1, 1) },
    new Student() { Name = "Chris", End = new DateTime(2020, 1, 2) },
    new Student() { Name = "James", End = new DateTime(2020, 1, 3) },
    new Student() { Name = "Bob", End = new DateTime(2020, 1, 4) },
    new Student() { Name = "Chris", End = new DateTime(2020, 1, 5) },
    new Student() { Name = "Bob", End = new DateTime(2020, 1, 6) },
    new Student() { Name = "Bob", End = new DateTime(2020, 1, 7) },
    new Student() { Name = "Bob", End = new DateTime(2020, 1, 8) },
    new Student() { Name = "Chris", End = new DateTime(2020, 1, 9) },
    new Student() { Name = "Bob", End = new DateTime(2020, 1, 10) }
};

Вы можете сделать GroupBy(), а затем OrderByDescending() свойство End, и возьмите по 5 с каждой.

var recentFiveForEachName = students
            .GroupBy(x => x.Name, (key, g) => g.OrderByDescending(y => y.End).Take(5));
0 голосов
/ 04 августа 2020

Поскольку вы используете EF Core, вы ограничены в возможностях GroupBy. Вместо этого вы можете заменить GroupBy на подзапрос, который возвращает нужные вам записи для каждого name:

var allRecords = from s in _context.Students
                 where (from s2 in _context.Students
                        where s2.name == s.name
                        orderby s2.endTime descending
                        select s2.id).Take(5).Contains(s.id)
                 orderby s.name, s.endTime descending
                 select s;

Или, если вы предпочитаете свободную версию:

var allRecords2 = _context.Students.Where(s => _context.Students.Where(s2 => s2.name == s.name)
                                                       .OrderByDescending(s2 => s2.endTime)
                                                       .Select(s2 => s2.id)
                                                       .Take(5)
                                                       .Contains(s.id))
                                    .OrderBy(s => s.name)
                                    .ThenByDescending(s => s.endTime);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...