为啥 EF Core 最后要添加一个额外的 ORDER BY
Posted
技术标签:
【中文标题】为啥 EF Core 最后要添加一个额外的 ORDER BY【英文标题】:Why is EF Core adding an extra ORDER BY on the end为什么 EF Core 最后要添加一个额外的 ORDER BY 【发布时间】:2021-08-04 18:18:50 【问题描述】:我有一个问题:
public async Task<IEnumerable<CategoryDto>> GetCategoriesAsync()
var dto = await _context.Categories.FromSqlRaw(
@"SELECT * FROM [cp].[GetCategoryTree]")
.ProjectTo<CategoryDto>(_mapper.ConfigurationProvider)
.AsNoTrackingWithIdentityResolution()
.ToListAsync();
return dto;
它产生这个 sql:
SELECT [c].[banner_group_id], [c].[category_id], [c].[description], [c].[inactive], [c].[issitecategory], [c].[keywords], [c].[category_name], [c].[page_content], [c].[parent_category_id], [c].[sort_order], [c].[title], [b].[banner_group_id], [b].[category_id], [b].[description], [b].[inactive], [b].[issitecategory], [b].[keywords], [b].[category_name], [b].[page_content], [b].[parent_category_id], [b].[sort_order], [b].[title]
FROM (
SELECT * FROM [cp].[GetCategoryTree]
) AS [c]
LEFT JOIN [bma_ec_categories] AS [b] ON [c].[category_id] = [b].[parent_category_id]
ORDER BY [c].[category_id], [b].[category_id]
[cp].[GetCategoryTree]
的输出已在该视图中排序。为什么 EF Core 最后要添加一个额外的ORDER BY
?我可以告诉 EF Core 不排序吗?
【问题讨论】:
奇怪...是 Automapper 的 ProjectTo 吗?您可以删除它并记录您的查询吗? @GHDevOps 是的,就是这么做的。 映射到CategoryDto
导致加入,我看到当 EF-core 加入时它总是按 PK 值添加排序,可能是为了使后处理更容易(在内存中构建对象)。
视图没有排序,什么意思它已经排序了?
不,他们不能,这只是一个能够将单词ORDER BY
放在视图中的黑客,这仅意味着TOP
在排序后被评估,它没有 我重复 DOES NOT 对外部查询强制执行任何排序,这是一个完整的神话。见***.com/a/15188437/14868997和mssqltips.com/sqlservertip/4488/…和bengribaudo.com/blog/2015/05/01/3430/…和dba.stackexchange.com/a/21437/220697
【参考方案1】:
递归CTE返回结果是普通的记录集,也就是说如果你在客户端需要树,你必须自己重构它。
让我们重用原始查询:
public async Task<IEnumerable<CategoryDto>> GetCategoriesAsync()
var plainResult = await _context.Categories.FromSqlRaw(@"SELECT * FROM [cp].[GetCategoryTree]")
.Select(c => new CategoryDto
CategoryId = c.CategoryId,
ParentId = c.ParentId,
Name = c.Name,
SortOrder = c.SortOrder
)
.ToListAsync();
var lookup = plainResult.Where(x => x.ParentId != 0).ToLookup(x => x.ParentId);
foreach (c in plainResult)
if (lookup.ContainsKey(c.CategoryId))
c.Children = lookup[c.CategoryId]
.OrderBy(x => x.SortOrder)
.ThenBy(x => x.Name)
.ToList();
var dto = plainResult.Where(x => x.ParentId == 0).ToList();
return dto;
【讨论】:
其实我在这里看不到为什么需要递归 CTE。您可以直接从_context.Categories
进行原始查询。如果要选择树的叶子,则需要 CTE。【参考方案2】:
我从一个平面 Category
查询开始。我需要它在树结构中。所以我为Children
添加了一个Include
。但它只包括根元素上的子元素。我问了 SO here 并被指向 Recursive CTE
。这就是[cp].[GetCategoryTree]
。我现在已经放弃了所有这些并提出了我的问题:
public async Task<IEnumerable<CategoryDto>> GetCategoriesAsync()
var dto = await _context.Categories
.Where(x => x.ParentId == 0)
.OrderBy(x=> x.SortOrder)
.ThenBy(x => x.Name)
.ProjectTo<CategoryDto>(_mapper.ConfigurationProvider)
.AsNoTracking()
.ToListAsync();
foreach (var categoryDto in dto)
foreach (var categoryDtoChild in categoryDto.Children)
categoryDtoChild.Children = await _context.Categories
.Where(x => x.ParentId == categoryDtoChild.CategoryId)
.OrderBy(x => x.SortOrder)
.ThenBy(x => x.Name)
.ProjectTo<CategoryDto>(_mapper.ConfigurationProvider)
.AsNoTracking()
.ToListAsync();
return dto;
这很有效,慢慢确定,但一直填充到曾孙。如果有人有更好的解决方案,我会全力以赴。
基准测试结果(我的和@SvyatoslavDanyliv):
Method | IterationCount | Mean | Error | StdDev | Median | Min | Max | Ratio | RatiosD | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
GetCategories | 1 | 142.82 ms | 5.260 ms | 14.921 ms | 142.65 ms | 85.572 ms | 170.29 ms | 1.00 | 0.00 | 1000.0000 | - | - | 5,845 KB |
GetCategoriesSingleQuery | 1 | 11.17 ms | 0.501 ms | 1.396 ms | 10.87 ms | 8.057 ms | 14.76 ms | 0.08 | 0.01 | - | - | - | 914 KB |
GetCategories | 2 | 290.03 ms | 7.703 ms | 21.473 ms | 288.38 ms | 239.879 ms | 350.48 ms | 1.00 | 0.00 | 2000.0000 | - | - | 11,637 KB |
GetCategoriesSingleQuery | 2 | 23.21 ms | 1.815 ms | 5.207 ms | 21.58 ms | 17.005 ms | 38.73 ms | 0.08 | 0.02 | - | - | - | 1,745 KB |
我想我会接受@SvyatoslavDanyliv 的回答。
【讨论】:
为什么不单查询? @SvyatoslavDanyliv 请给我一个单个查询的示例。 我已经添加了另一个答案。以上是关于为啥 EF Core 最后要添加一个额外的 ORDER BY的主要内容,如果未能解决你的问题,请参考以下文章
为啥 SelectMany 不适用于 EF Core Cosmos DB?