简体   繁体   中英

Get max version within a date period query

I am trying to write a LINQ query that gets all the records and groups them by Period ie Sep-18 and then returns the record with the highest Version number within the periods. For example if I have three periods contained within my periodNames list the output list should return:

  1. Sep-18 Versions: 1, 2, 3 (Returns record with version 3)

  2. Oct-18 Versions: 1, 2 (Returns record with version 2)

  3. Nov-18 Versions: 1, 2, 3, 4 (Returns record with version 4)

This is the query I have written so far:

var previousStatements = _context.Statements.Where(x => periodNames.Contains(x.Period) &&
                          x.Version == _context.Statement.Max(y => y.Version)).toList();

How can I adapt this to the above specification? Thanks

You can use GroupBy in order to group the statements and Max in order to find the maximum value, eg

var previousStatements = _context.Statements.Where(x => periodNames.Contains(x.Period))
   .GroupBy(x => x.Period)
   .Select(x => new { Period = x.Key, MaxVersion = x.Max(y => y.Version))
   .ToList();

The code above returns the Period and the maximum version number only. If you need the record with the highest version number for each period, you can use this:

var previousStatements = (ctx.Items.Where(x => periodNames.Contains(x.Period))
                .GroupBy(x => x.Period)
                .ToArray())
                .Select(x => x.OrderByDescending(y => y.Version).First())
                .ToList();

Please note that the code above first uses a call to ToArray to send the GroupBy-query to the database. From the returned groups, the row with the highest version number for each period is then retrieved in memory.

尝试使用GroupBy,然后使用orderbydescending获取最大版本号:

_context.GroupBy(f => f.Period).Select(f=>f.OrderByDescending(r=>r.Version).First()).ToList();

I think you would have known your solution if you would have written a proper requirement

You wrote:

...groups them by Period ie Sep-18 and then returns the highest Version number within the periods

Your examples don't return the highest version number but the row with the highest version number , so let's assume that is what you want:

From a sequence of Statements, group these statements into groups of statements with equal Period , and return from every group, the statement with the largest VersionNumber .

You haven't defined what you want if two statements within the same Period have the same VersionNumber. Let's assume you think that this will not occur, so you don't care which one is returned in that case.

So you have sequence of Statements , where every Statement has a Period and a VersionNumber .

Officially you haven't defined the class of Period and VersionNumber , the only thing we know about them is that you have some code that can decide whether two Periods are equal, and you have something where you can decide which VersionNumber is larger.

IEqualityComparer<Period> periodComparer = ...
IComparer<VersionNumber> versionComparer = ...

If Period is similar to a DateTime and VersionNumber is similar to an int, then these comparers are easy, otherwise you'll need to write comparers.

From your requirement the code is simple:

  • Take all input statements
  • Make groups of statements with equal Period
  • From every group of statements with this Period keep only the one with the highest VersionNumber

     IEnumerable<Statement> statements = ... var latestStatementsWithinAPeriod = statements .GroupBy(statement => statement.Period, // group by same value for Period (period, statementsWithThisPeriod) => // From every group of statements keep only the one with the highest VersionNumber // = order by VersionNumber and take the first statementWithThisPeriod .OrderByDescending(statement => statement.VersionNumber, versionComparer) .FirstOrDefault(), periodComparer); 

Once again: if default comparers can be used to decide when two Periods are equal and which VersionNumber is larger, you don't need to add the comparers.

The disadvantage of the SorBy is that the 3rd and 4rd element etc are also sorted, while you only need the first element, which is the one with the largest VersionNumber.

This can be optimized by using the less commonly used Aggregate :

(period, statementsWithThisPeriod) => statementWithThisPeriod.Aggregate(
    (newestStatement, nextStatement) => 
    (versionComparer.Compare(newestStatement.VersionNumber,  nextStatement.VersionNumber) >=0 ) ? 
        newestStatement :
        nextStatement)

This will put the first statement as the newestStatement (= until now this was the one with the highest version number). The 2nd element will be put in nextStatement. both statements will be compared, and if nextStatement has a VersionNumber larger than newestStatement, then nextStatement will be considered to be newer, and thus will replace newestStatement. The end of the Aggregate will return newestStatement

You can try with GroupBy and OrderByDescending and then take first one.

var statements = _context.Statements 
        .Where(x => periodNames.Contains(x.Period))
        .GroupBy(g => g.Period)
        .Select(s => s.OrderByDescending(o => o.Version)
        .FirstOrDefault()).ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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