简体   繁体   中英

How to avoid OrderByDescending in query linq

I have the linq query:

var ed = db.table
.GroupBy(x => x.Sn)
.Select(g => g.OrderByDescending(x => x.Date).FirstOrDefault());

I need to rewrite this query for server-side evaluation.

My table:

Sn    Value      Data     
150   180.3    01/06/2020  
150   195.0    01/05/2020  
149   13.3     01/06/2020  
345   27.5     27/06/2013
....  
.Select(g => g.OrderByDescending(x => x.Date).FirstOrDefault())

is probably just:

.Select(g => g.Max(x => x.Date))

Which the parser probably handles better

You can try using Aggregate

var ed = db.table
           .GroupBy(x => x.Sn)
           .Select(x => x.Aggregate((max, cur) => max.Date > cur.Date ? max : cur));

This might help you to know more How to use LINQ to select object with minimum or maximum property value .

It depends on whether dt.Table is IQueryable or not.

Normally an IQueryable is to be executed by a different process, quite often a database management system. It that is the case, you'll have to use OrderBy followed by a FirstOrDefault.

Luckily proper database management systems are extremely optimized to sort. If you are not satisfied with the efficiency of the sort, and you don't change your table too often, consider adding an extra index in DbContext.OnModelCreating:

modelBuilder.Entity<Customer>()
    .HasIndex(customer => customer.Name)

Your database management system knows this extra index, and can immediately return the element that the last item of the index refers to.

Whenever you change the name, or add a new Customer, the index has to be recreated. So don't do this if you are changing customer names often, like 10 times a second.

If dt.table is not IQueryable, but IEnumerable, OrderBy is relatively slow. Alas there is no Enumerable.Max for you, but you can use on of the overloads of Enumerable.Aggregate .

As you are certain that every group contains at least one element, you can use the overload without Seed:

var result = db.table.GroupBy(x => x.Sn)
   .Aggregate( (maxItem, nextitem) =>(nextItem.Date > maxItem.Date) ?? nextItem : maxItem)

If you use this quite often, consider to create an extension method. Creating an extension method is quite easy. See extension methods demystified

public static T MaxOrDefault<T, TProperty> MaxPropertyOrDefault(
    this IEnumerable<T> source,
    Func<TSource, TProperty> propertySelector)
{
     return MaxPropertyOrDefault(source, propertySelector, null)
}

Overload with comparer: if comparer equals null, use default comparer

public static T MaxOrDefault<T, TProperty> MaxPropertyOrDefault(
    this IEnumerable<T> source,
    Func<TSource, TProperty> propertySelector,
    IComparer<TProperty) comparer)
{
    // TODO: what to do if source == null?
    // TODO: what to do if propertySelector == null?
    if (comparer == null) comparer = Comparer<TProperty>.Default();
    
    var enumerator = source.GetEnumerator();
    if (!enumerator.MoveNext)
    {
        // empty source, return default:
        return default(T);
    }
    else
    {
        TProperty maxPropertyValue = propertySelector(enumerator.Current);
        T maxValue = enumerator.Current();

        while (enumerator.MoveNext())
        {
             TProperty currentPropertyValue = propertySelector(enumerator.Current);
             if (comparer.Compare(currentPropetyValue, maxPropertyValue) > 0)
             {
                 maxPropertyValue = currentPropertyValue;
                 maxValue = enumerator.Current;
             } 
        }
        return maxValue;
    }
}

Usage:

var ed = db.table.GroupBy(x => x.Sn)
           .Select(group => group.MaxOrDefault(groupElement => groupElement.Date);

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