首页
/ EF Core 中 SplitQuery 排序不一致问题解析

EF Core 中 SplitQuery 排序不一致问题解析

2025-05-16 17:46:25作者:袁立春Spencer

问题背景

在 EF Core 8.0.11 版本中,当使用 SplitQuery 查询策略进行分页查询时,如果查询中包含 OrderBy 子句但不包含主键排序,可能会导致查询结果不一致的问题。这个问题在使用 MySQL 数据库时尤为明显。

问题现象

考虑以下实体模型:

public class EntityA
{
    public string KeyB { get; set; }
    public string KeyA { get; set; }
    public string Name { get; set; }
    public DateTime Time { get; set; }
    public ICollection<EntityB> B { get; set; }
}

public class EntityB
{
    public string KeyA { get; set; }
    public string KeyB { get; set; }
    public int Id { get; set; }
    [ForeignKey("KeyA,KeyB")]
    public EntityA A { get; set; }
}

当执行如下查询时:

dataContext.A.Include(t => t.B)
    .OrderByDescending(t => t.Name)
    .Skip(4)
    .Take(4)
    .ToList();

EF Core 会生成两个 SQL 查询:

  1. 主查询:
SELECT `a`.`KeyA`, `a`.`KeyB`, `a`.`Name`
FROM `A` AS `a`
ORDER BY `a`.`Name` DESC, `a`.`KeyA`, `a`.`KeyB`
LIMIT 4 OFFSET 4
  1. 关联查询:
SELECT `b`.`Id`, `b`.`KeyA`, `b`.`KeyB`, `t`.`KeyA`, `t`.`KeyB`
FROM (
    SELECT `a`.`KeyA`, `a`.`KeyB`
    FROM `A` AS `a`
    ORDER BY `a`.`Name` DESC
    LIMIT 4 OFFSET 4
) AS `t`
INNER JOIN `B` AS `b` ON (`t`.`KeyA` = `b`.`KeyA`) AND (`t`.`KeyB` = `b`.`KeyB`)
ORDER BY `t`.`KeyA`, `t`.`KeyB`

问题分析

关键问题在于两个查询的排序条件不一致:

  1. 主查询中,EF Core 自动添加了主键(KeyA 和 KeyB)作为额外的排序条件
  2. 但在关联查询的子查询中,却没有添加主键排序条件

这种不一致会导致:

  1. 当表中有大量 Name 值相同的记录时,MySQL 在没有明确排序条件的情况下可能返回不同的结果顺序
  2. 主查询和关联查询可能返回不同的记录集
  3. 最终可能导致 Include 加载的导航属性 B 与主实体 A 不匹配

技术原理

在 EF Core 中,SplitQuery 策略会将单个查询拆分为多个查询以提高性能。对于包含 Include 的查询:

  1. 第一个查询获取主实体
  2. 第二个查询获取关联实体

为了确保两个查询返回的记录能够正确匹配,EF Core 需要在两个查询中保持一致的排序顺序。特别是在分页查询(Skip/Take)中,排序的一致性尤为重要。

解决方案

这个问题已经在 EF Core 10 中修复。修复的核心是确保在 SplitQuery 的所有相关查询中保持一致的排序条件,特别是:

  1. 在所有子查询中添加与主查询相同的排序条件
  2. 确保主键总是包含在排序条件中,即使查询中没有显式指定

对于使用 EF Core 8 或 9 的用户,可以采取以下临时解决方案:

  1. 在查询中显式添加主键排序:
.OrderByDescending(t => t.Name)
.ThenBy(t => t.KeyA)
.ThenBy(t => t.KeyB)
  1. 避免在 Name 值大量重复的情况下使用分页查询
  2. 考虑使用 SingleQuery 策略代替 SplitQuery

最佳实践

  1. 对于包含分页的 Include 查询,总是显式指定完整的排序条件
  2. 在升级到 EF Core 10 之前,仔细测试 SplitQuery 行为
  3. 对于关键业务逻辑,考虑添加额外的断言来验证数据一致性

这个问题提醒我们,在使用 ORM 框架的高级功能时,理解底层生成的 SQL 查询非常重要,特别是在处理分页和关联数据加载时。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
ohos_react_nativeohos_react_native
React Native鸿蒙化仓库
C++
177
262
RuoYi-Vue3RuoYi-Vue3
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
864
512
ShopXO开源商城ShopXO开源商城
🔥🔥🔥ShopXO企业级免费开源商城系统,可视化DIY拖拽装修、包含PC、H5、多端小程序(微信+支付宝+百度+头条&抖音+QQ+快手)、APP、多仓库、多商户、多门店、IM客服、进销存,遵循MIT开源协议发布、基于ThinkPHP8框架研发
JavaScript
93
15
openGauss-serveropenGauss-server
openGauss kernel ~ openGauss is an open source relational database management system
C++
129
182
openHiTLSopenHiTLS
旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
C
261
302
kernelkernel
deepin linux kernel
C
22
5
cherry-studiocherry-studio
🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端
TypeScript
596
57
CangjieCommunityCangjieCommunity
为仓颉编程语言开发者打造活跃、开放、高质量的社区环境
Markdown
1.07 K
0
HarmonyOS-ExamplesHarmonyOS-Examples
本仓将收集和展示仓颉鸿蒙应用示例代码,欢迎大家投稿,在仓颉鸿蒙社区展现你的妙趣设计!
Cangjie
398
371
Cangjie-ExamplesCangjie-Examples
本仓将收集和展示高质量的仓颉示例代码,欢迎大家投稿,让全世界看到您的妙趣设计,也让更多人通过您的编码理解和喜爱仓颉语言。
Cangjie
332
1.08 K