鉴于IQueryable< Product>在数据库中使用以下值(值是假的,以及实体和字段的名称):
Id | CategoryName | Price
------ | -------------|-------
1 | car | 10000
2 | boat | 15000
3 | boat | 20000
4 | car | 5000
5 | boat | 30000
6 | food | 100
7 | car | 15000
8 | food | 200
我需要转换此IQueryable< Product>到IOrderedQueryable< Product>与其类别中最昂贵的产品的价值,按价格降序排序。所以它看起来像:
Id | CategoryName | Price
------ | -------------|-------
5 | boat | 30000
7 | car | 15000
8 | food | 200
由于IQueryable提供了在底层SQL查询中执行分组和排序的能力(并且真实表包含大约10k行,其中有关于如何对值进行分组的更复杂的标准),我想要的关键是< EM>性能
我已经有了一个工作解决方案,如下所示:
IQueryable<Product> queryable = ...;
var result = queryable
.GroupBy(
x => x.CategoryName,
(_, productsInGroup) => productsInGroup
.FirstOrDefault(x => x.Price == productsInGroup.Max(p => p.Price))
.OrderByDescending(x => x.Price);
此解决方案导致使用GROUP BY和嵌套选择进行长查询(计算MAX(p.Price),选择组中的正确行等等)
没有GroupBy有没有能力做到这一点? 我想要像:
var result = queryable
.OrderByDescending(x => x.Price)
.ExcludeRowsWithDuplicateCategoryName(); // Distinct by predicate
以便此类查询首先按降序对值进行排序:
Id | CategoryName | Price
------ | -------------|-------
5 | boat | 30000
3 | boat | 20000
7 | car | 15000
2 | boat | 15000
1 | car | 10000
4 | car | 5000
8 | food | 200
6 | food | 100
然后只是以某种方式排除第3,2,1,4,6行,因为它们已经在上面的行中出现了CategoryName。
我该怎么做?
答案 0 :(得分:2)
我想到的唯一选择是使用 self anti join 和比较条件(或基于NOT EXISTS
的查询),如下所示:
var result = queryable
.Where(x => !queryable.Any(y => y.CategoryName == x.CategoryName && y.Price > x.Price))
.OrderByDescending(x => x.Price)
.ToList();
它是否具有更高的性能取决于具体的可查询和数据库表索引。
值得一提的是,上述内容并非完全等效,因为如果每组有两个或更多元素,每个类别会返回多个记录,因此确切的等价物需要额外的标准,如:
y => y.CategoryName == x.CategoryName &&
(y.Price > x.Price || (y.Price == x.Price && y.Id > x.Id))
答案 1 :(得分:1)
从我可以在帖子中告诉查询: 首先找到组中的最高价格
productsInGroup.Max(p => p.Price)
然后找到具有匹配价格的第一个元素。
FirstOrDefault(x => x.Price == productsInGroup.Max(p => p.Price))
不确定您是否可以实际跳过该组,但在按价格订购后从组中取出第一个元素可能更简单。
var result = Products.GroupBy(f => f.CategoryName).Select(gr => gr.OrderByDescending(p => p.Price).First() );