背景
几个月前其实我已经注意到了一篇帖子《从30秒到30毫秒:EF Core查询性能优化实战全记录》,感兴趣的小伙伴可以自行网上收一下,讲的是如何通过优化提高efcore的性能。
我们对其进行简化,简化部分需求,来讲解本次主题,为什么easy-query这么强,为什么可以这么智能
本次使用的是MYSQL 8本地环境 java8和efcore9
实体关系如下:
用户:每个用户有多篇帖子和多条评论
评论:每条评论属于一个用户并关联一篇帖子
分类:帖子按分类组织
帖子:每个帖子所属一个类目 并且有多个评论
挑战需求:查询过去7天在".NET"分类下评论最多的5位用户,
每位用户返回:
用户ID
用户名
隐式Group
一种更加智能的sql生成,完美的融合了表达式的阅读性和性能,在复杂子查询嵌套和多子查询下合并有种非常高的性能提升,且表达式本身不需要任何变化,是一种非常智能的表达式转变。
先说结论
框架耗时 easy-query普通子查询 efcore子查询 easy-query隐式Group
mysql8 11s 11s 2.7s
创建实体
点击查看实体代码
通过easy-query的code-first生成数据库表,并且生成初始化数据,其中初始化数据为 9个类目,16个用户 发布了15w个帖子并且一个评论100w条记录
点击查看初始化代码
查询数据库
普通子查询
LocalDateTime dateTime = LocalDateTime.now().plusDays(-7);
List<User> list = entityQuery.queryable(User.class)
.where(u -> {
u.comments().any();
})
.orderBy(u -> {
u.comments().where(c -> {
c.createAt().isAfter(dateTime);
c.post().category().name().eq(".NET");
}).count().desc();
})
.limit(5).toList();
最终生成的sql为
SELECT
t.`id`,
t.`username`
FROM
`t_user` t
WHERE
EXISTS (SELECT
1 FROM `t_comment` t1
WHERE
t1.`user_id` = t.`id`
LIMIT
1)
ORDER BY
(SELECT
COUNT(*)
FROM
`t_comment` t2
LEFT JOIN
`t_post` t3
ON t3.`id` = t2.`post_id`
LEFT JOIN
`t_category` t4
ON t4.`id` = t3.`category_id`
WHERE
t2.`user_id` = t.`id`
AND t2.`create_at` > '2025-10-19 22:28:18.469'
AND t4.`name` = '.NET') DESC
LIMIT
5
平均耗时11秒左右
efcore
点击查看实体代码
查询数据库
var dateTime = DateTime.Now.AddDays(-7);
var users = context.Set<User>()
.AsNoTracking()
.Where(u =>u.Comments.Any())
.OrderByDescending(u => u.Comments
.Count(c =>
c.CreatedAt >= dateTime &&
c.Post.Category.Name ==".NET")
)
.Take(5)
.ToList();
生成的sql如下
Executed DbCommand (11,016ms) [Parameters=[@__dateTime_0='2025-10-19T22:32:06.7108910+08:00' (DbType = DateTime), @__p_1='5'], CommandType='Text', CommandTimeout='30']
SELECT `t`.`id`, `t`.`username`
FROM `t_user` AS `t`
WHERE EXISTS (
SELECT 1
FROM `t_comment` AS `t0`
WHERE `t`.`id` = `t0`.`user_id`)
ORDER BY (
SELECT COUNT(*)
FROM `t_comment` AS `t1`
INNER JOIN `t_post` AS `t2` ON `t1`.`post_id` = `t2`.`id`
INNER JOIN `t_category` AS `t3` ON `t2`.`category_id` = `t3`.`id`
WHERE (`t`.`id` = `t1`.`user_id`) AND ((`t1`.`create_at` >= @__dateTime_0) AND (`t3`.`name` = '.NET'))) DESC
LIMIT @__p_1
平均耗时11秒
从结果看两者都是性能相当的,那么为什么本次的主题是暴打efcore呢?
接下来我为大家带来easy-query的究极子查询功能,将子查询转成groupJoin也就是隐式查询的隐式Group可以说.net应该没有ORM做到了这个功能
隐式Group
修改easy-query查询添加一行配置,是的你没听错就一行配置
LocalDateTime dateTime = LocalDateTime.now().plusDays(-7);
List<User> list = entityQuery.queryable(User.class)
.configure(s->s.getBehavior().add(EasyBehaviorEnum.ALL_SUB_QUERY_GROUP_JOIN))
.where(u -> {
u.comments().any();
})
.orderBy(u -> {
u.comments().where(c -> {
c.createAt().isAfter(dateTime);
c.post().category().name().eq(".NET");
}).count().desc();
})
.limit(5).toList();
生成的sql如下
SELECT
t.`id`,
t.`username`
FROM
`t_user` t
LEFT JOIN
(SELECT
t1.`user_id` AS `userId`, (COUNT(*) > 0) AS `__any2__`, COUNT((CASE
WHEN t1.`create_at` > '2025-10-19 22:30:12.833'
AND t4.`name` = '.NET'
THEN 1
ELSE NULL
END)) AS `__count3__` FROM `t_comment` t1
LEFT JOIN
`t_post` t3
ON t3.`id` = t1.`post_id`
LEFT JOIN
`t_category` t4
ON t4.`id` = t3.`category_id`
GROUP BY
t1.`user_id`) t2
ON t2.`userId` = t.`id`
WHERE
IFNULL(t2.`__any2__`, false) = true
ORDER BY
IFNULL(t2.`__count3__`, 0) DESC
LIMIT
5
平均耗时2.7秒 是的你妹看错仅仅只需要一行代码即可让子查询性能提升4-5倍这是什么概念,这就是easy-query带来的最强子查询 隐式Group
鱼和熊掌可以兼得
eq真正的做到了将子查询优化到无敌的存在,可以说目前市面上没有任何一款orm做到了如此智能的sql,性能和可维护性真的可以在easy-query上做到兼得。
具体eq的相关文档点击此处
插曲
我在看《从30秒到30毫秒:EF Core查询性能优化实战全记录》这个文章的时候看到
image
这个表达式,我随机在efcore上试了一下结果性能大跌眼镜要足足22秒也不知道原文是怎么测试的???
var dateTime = DateTime.Now.AddDays(-7);
var users = context.Set<User>()
.AsNoTracking()
.Where(u => u.Comments.Any(c =>
c.CreatedAt >= dateTime &&
c.Post.Category.Name ==".NET"))
.Select(u =>new
{
u.Id,
u.Username,
CommentsCount = u.Comments.Count(c =>
c.CreatedAt >= dateTime &&
c.Post.Category.Name ==".NET")