C# IQueryable Union操作后排序丢失,如何保留排序?
解决Union操作后排序丢失的问题
为什么排序会丢失
Union是集合去重合并操作,EF Core会将其转换为SQL的UNION语句,而SQL标准中UNION的结果是无序的——无论子查询有没有排序,最终结果都会忽略这些排序规则,只有在整个查询的末尾添加ORDER BY才能确保最终结果的顺序。另外,你之前在子查询中添加的排序完全多余,既不影响Union结果,还会增加不必要的查询开销。
方案一:统一最终排序(不区分匹配优先级)
如果不需要按“匹配字段的重要性”排序,只需要统一的排序规则,可去掉所有子查询的排序,先合并去重,再在最后添加全局排序:
var expressions = new List<IQueryable<Container>>(); var containers1 = containers .Where(x => EF.Functions.Like(x.ContainerReference1, $"%{message.SearchValue}%") || EF.Functions.Like(x.ContainerReference2, $"%{message.SearchValue}%")); expressions.Add(containers1); var containers2 = containers .Where(x => EF.Functions.Like(x.Description, $"%{message.SearchValue}%")); expressions.Add(containers2); var containers3 = containers.Where(x => x.ContactEmails .Any(y => EF.Functions.Like(y.Email, $"%{message.SearchValue}%") || EF.Functions.Like(y.DisplayName, $"%{message.SearchValue}%"))); expressions.Add(containers3); var containers4 = containers .Where(x => EF.Functions.Like(x.Keywords, $"%{message.SearchValue}%")); expressions.Add(containers4); // 先合并去重,再执行全局排序 containers = expressions.Aggregate((acc, i) => acc.Union(i)) .OrderBy(x => x.ContainerReference1) .ThenBy(x => x.ContainerReference2) .ThenByDescending(x => x.DateUpdated);
方案二:按匹配优先级排序(区分匹配字段的重要性)
如果希望优先展示匹配容器编号的结果,再展示匹配描述的,以此类推,可以给每个匹配结果添加优先级权重,合并后按优先级排序,再补充其他排序规则:
// 每个子查询添加优先级标识,数字越小优先级越高 var containers1 = containers .Where(x => EF.Functions.Like(x.ContainerReference1, $"%{message.SearchValue}%") || EF.Functions.Like(x.ContainerReference2, $"%{message.SearchValue}%")) .Select(x => new { Container = x, Priority = 1 }); var containers2 = containers .Where(x => EF.Functions.Like(x.Description, $"%{message.SearchValue}%")) .Select(x => new { Container = x, Priority = 2 }); var containers3 = containers.Where(x => x.ContactEmails .Any(y => EF.Functions.Like(y.Email, $"%{message.SearchValue}%") || EF.Functions.Like(y.DisplayName, $"%{message.SearchValue}%"))) .Select(x => new { Container = x, Priority = 3 }); var containers4 = containers .Where(x => EF.Functions.Like(x.Keywords, $"%{message.SearchValue}%")) .Select(x => new { Container = x, Priority = 4 }); // 合并所有结果,按主键去重(避免同一个容器匹配多个条件),再排序 var combined = containers1 .Union(containers2) .Union(containers3) .Union(containers4) .GroupBy(item => item.Container.Id) // 假设Id是Container的主键,用于去重 .Select(g => g.First()) // 保留优先级最高的匹配项 .OrderBy(item => item.Priority) // 先按优先级排序 .ThenBy(item => item.Container.ContainerReference1) .ThenBy(item => item.Container.ContainerReference2) .ThenByDescending(item => item.Container.DateUpdated) .Select(item => item.Container); containers = combined;
额外优化点
子查询containers3里对ContactEmails的OrderBy完全多余,因为Any只需要判断是否存在匹配项,排序不会影响结果,去掉后能提升查询性能。
内容的提问来源于stack exchange,提问作者A. Gladkiy
相关产品推荐
相关产品推荐

