简体   繁体   English

LINQ 用于获取每个不同名称的 5 条最新记录

[英]LINQ for getting the 5 latest records for each distinct name

I have been trying to get the 5 most recent records from a database with distinct names from the "Name" Column.我一直在尝试从数据库中获取 5 条最新记录,这些记录的名称与“名称”列不同。 Meaning I want the 5 latest records for each of the unique names in the table.意思是我想要表中每个唯一名称的 5 个最新记录。

Here is a sample table:这是一个示例表:

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 />

I would expect the latest 5 Bob records (out of the 6 ), the 3 Chris records, and the one James record to be returned.我希望返回最新的 5 个 Bob 记录(共 6 个)、3 个 Chris 记录和一个 James 记录。

I have tried to methods so far:到目前为止,我已经尝试过方法:

  1. Breaking this into two different operations:Querying for the Distinct names, then query based on the name, get the latest 5 records by endDate and append to a list.将其分为两个不同的操作:查询不同的名称,然后根据名称进行查询,将 endDate 和 append 的最新 5 条记录获取到列表中。 With this method I was able to do the first query correctly.使用这种方法,我能够正确地执行第一个查询。 I was printing out the three distinct name (Bob, Chris, James).我正在打印出三个不同的名字(Bob、Chris、James)。 However, everytime I did a query with those names and getting the 5 most recent records, and it would come back empty for all three names.但是,每次我对这些名称进行查询并获取 5 个最新记录时,所有三个名称都会返回空值。 Any printing I tried to do said the variable name was void according to Visual Studio 2019...SO I tried method 2我尝试做的任何打印都说根据 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();*/
}
  1. Using a single LINQ query.使用单个 LINQ 查询。 With this method I was able to get 3 distinct records based on name, but I could not get more records than that.使用这种方法,我能够根据名称获得 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;
}

I would love it if method 2 would work, but I feel like I might be stepping on my own toes trying to do it all in one single call.如果方法 2 可行,我会很高兴,但我觉得我可能会踩到自己的脚趾,试图在一次调用中完成所有操作。 If anyone has some advice, it would be greatly appreciated.如果有人有任何建议,将不胜感激。

Edit:编辑:

As @NetMerge pointed out this won't work in EF Core, so I'll leave the answer here for now in case the OP wants to take a look at it anyway.正如@NetMerge 指出的那样,这在 EF Core 中不起作用,所以我暂时将答案留在这里,以防 OP 无论如何都想查看它。

Here's a sample class with just the two properties we're focusing, and your sample list.这是一个示例 class,其中仅包含我们关注的两个属性以及您的示例列表。

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

You can do a GroupBy() and then OrderByDescending() the End property, and take 5 from each.您可以执行GroupBy() ,然后OrderByDescending()End属性,并从每个属性中取 5 个。

var recentFiveForEachName = students
            .GroupBy(x => x.Name, (key, g) => g.OrderByDescending(y => y.End).Take(5));

Since you are using EF Core, you are limited in what you can do with GroupBy .由于您使用的是 EF Core,因此您可以使用GroupBy执行的操作受到限制。 Instead, you can replace GroupBy with a sub-query that returns which records you want for each name :相反,您可以将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;

Or, if you prefer the fluent version:或者,如果您更喜欢流利的版本:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM