Linq to entites (EF6) return 每个组的最新记录使用 T-SQL 服务器中的行号
Linq to entites (EF6) return latest records of each group using Row number in T-SQL server
我正在使用 Linq to entities 获取每个组的最新更新记录。但实际上当我检查 sql 探查器时,我的 Ling 查询生成了许多子查询,因此完成它真的需要太多时间。为了解决这个性能问题,我已经编写了下面提到的本机 T-Sql,因此我正在寻找使用 Linq 查询的解决方案,entity framework 使用 ( ROW_NUMBER() OVER(PARTITION BY ...) 以下是我的示例数据:
- 父表和子表:
- 父子表示例数据:
- 下面是我的查询结果:
TSQL 查询:
WITH summary AS (
SELECT a.ParentId
,a.Name
,a.Email
,p.Created
,p.[Status],
ROW_NUMBER() OVER(PARTITION BY p.ParentId
ORDER BY p.Created DESC) AS rk
FROM Parent a
LEFT JOIN Child p
ON a.ParentId = P.ParentId
)
SELECT s.*
FROM summary s
WHERE s.rk = 1
我使用 Linq 的示例 C#:
using (DbContext context = new DbContext())
{
return context.Parents.Where(p => p.ParentId == parentId)
.Include(a => a.Childs)
.Select(x => new ObjectDto()
{
ParentId = x.ParentId,
Status = x.Childs.OrderByDescending(a => a.Created).FirstOrDefault(p => p.ParentId).Status,
ChildName = x.Childs.OrderByDescending(a => a.Created).FirstOrDefault(p => p.ParentId).ChildName
})
.ToList();
}
有几件事可以改进您的 C# 查询:
using (DbContext context = new DbContext())
{
return context.Parents.Where(p => p.ParentId == parentId)
.Include(a => a.Childs)
.Select(x => new ObjectDto()
{
ParentId = x.ParentId,
Status = x.Childs.OrderByDescending(a => a.Created).FirstOrDefault(p => p.ParentId).Status,
ChildName = x.Childs.OrderByDescending(a => a.ChildName).FirstOrDefault(p => p.ParentId).ChildName
})
.ToList();
}
首先,Include
调用在这里什么都不做,因为您不只是返回 EF 实体(因此延迟加载语义不适用)。
其次:避免使用let
子句的重复子查询。
(传递给 FirstOrDefault
的 lambda 一定是一个错误,因为它需要一个 Func<T, bool>
而不是。)
因此
using (DbContext context = new DbContext()) {
return await (from p in context.Parents
where p.ParentId == parentId
let cs = p.Childs.OrderByDescending(a => a.Created).FirstOrDefault()
select new ObjectDto {
ParentId = p.ParentId,
Status = cs.Status,
ChildName = cs.ChildName
}).ToListAsync();
}
不然看起来还算合理。您需要查看生成的查询计划,看看您可以在索引方面做些什么。
如果这不起作用,则使用您可以完全控制的存储过程。 (机械生成代码——无需在代码生成器和优化器中做大量工作——总是可以被手写代码打败)。
我正在使用 Linq to entities 获取每个组的最新更新记录。但实际上当我检查 sql 探查器时,我的 Ling 查询生成了许多子查询,因此完成它真的需要太多时间。为了解决这个性能问题,我已经编写了下面提到的本机 T-Sql,因此我正在寻找使用 Linq 查询的解决方案,entity framework 使用 ( ROW_NUMBER() OVER(PARTITION BY ...) 以下是我的示例数据:
- 父表和子表:
- 父子表示例数据:
- 下面是我的查询结果:
TSQL 查询:
WITH summary AS (
SELECT a.ParentId
,a.Name
,a.Email
,p.Created
,p.[Status],
ROW_NUMBER() OVER(PARTITION BY p.ParentId
ORDER BY p.Created DESC) AS rk
FROM Parent a
LEFT JOIN Child p
ON a.ParentId = P.ParentId
)
SELECT s.*
FROM summary s
WHERE s.rk = 1
我使用 Linq 的示例 C#:
using (DbContext context = new DbContext())
{
return context.Parents.Where(p => p.ParentId == parentId)
.Include(a => a.Childs)
.Select(x => new ObjectDto()
{
ParentId = x.ParentId,
Status = x.Childs.OrderByDescending(a => a.Created).FirstOrDefault(p => p.ParentId).Status,
ChildName = x.Childs.OrderByDescending(a => a.Created).FirstOrDefault(p => p.ParentId).ChildName
})
.ToList();
}
有几件事可以改进您的 C# 查询:
using (DbContext context = new DbContext())
{
return context.Parents.Where(p => p.ParentId == parentId)
.Include(a => a.Childs)
.Select(x => new ObjectDto()
{
ParentId = x.ParentId,
Status = x.Childs.OrderByDescending(a => a.Created).FirstOrDefault(p => p.ParentId).Status,
ChildName = x.Childs.OrderByDescending(a => a.ChildName).FirstOrDefault(p => p.ParentId).ChildName
})
.ToList();
}
首先,Include
调用在这里什么都不做,因为您不只是返回 EF 实体(因此延迟加载语义不适用)。
其次:避免使用let
子句的重复子查询。
(传递给 FirstOrDefault
的 lambda 一定是一个错误,因为它需要一个 Func<T, bool>
而不是。)
因此
using (DbContext context = new DbContext()) {
return await (from p in context.Parents
where p.ParentId == parentId
let cs = p.Childs.OrderByDescending(a => a.Created).FirstOrDefault()
select new ObjectDto {
ParentId = p.ParentId,
Status = cs.Status,
ChildName = cs.ChildName
}).ToListAsync();
}
不然看起来还算合理。您需要查看生成的查询计划,看看您可以在索引方面做些什么。
如果这不起作用,则使用您可以完全控制的存储过程。 (机械生成代码——无需在代码生成器和优化器中做大量工作——总是可以被手写代码打败)。