【发布时间】:2021-05-14 22:43:12
【问题描述】:
我有两个模型 Parent 和 Child,我想检索所有具有 unitid = 5 并按家庭排序的 Distinct parent
public class Parent
{
public int Id { get; set; }
public string Family { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public int UnitId { get; set; }
public Parent parent { get; set; }
}
父表有 500000 条记录,子表有 2000000 条记录。我使用 SelectMany And Distinct 但检索记录需要 8 秒
var parents= Context.Set<Parent>()
.SelectMany(x => x.Children.DefaultIfEmpty(),
(u, r) => new {
Parent = u,
Child = r })
.Where(x=>x.Child.UnitId == 5)
.Select(m => new Parent{
Id = m.Parent.Id,
Family= m.Parent.Family
})
.Distinct()
.OrderBy(x=>x.Family)
.Take(30).Skip(0).ToList();
翻译成:
SELECT [t].[Id], [t].[Family] FROM (
SELECT distinct [a].[Id], [a].[Family]
FROM [Parent] AS [a]
LEFT JOIN [Children] AS [r] ON [a].[Id] = [r].[ParentId]
WHERE ([r].[unitid] = 5) AS [t] ORDER BY [t].[family] OFFSET 0 ROWS FETCH next 30 ROWS ONLY
或者
var parents = Context.Set<Children>()
.Include(x=>x.Parent)
.Where(x=>x.UnitId ==5)
.Select(m => new Parent{
Id = m.ParentId ,
Family = m.Parent.Family})
.Distinct()
.OrderBy(x=>x.Family)
.Take(30).Skip(0).ToList();
翻译成:
SELECT [t0].[Id],[t0].[Family] FROM ( SELECT DISTINCT [t].[Id], [t].[Family]
FROM [Children] AS [r]
INNER JOIN (
SELECT [a].[Id] , [a].[Family]
FROM [Parent] AS [a]
) AS [t] ON [r].[ParentId] = [t].[Id]
WHERE ([r].[UnitId] = 5) AS [t0] ORDER BY [t0].[Family] OFFSET 0 ROWS FETCH next 30 ROWS ONLY
1 .当使用按 id 排序时,速度非常快
2 。用家人点单拿的时候,跳过很慢
3 .家庭被索引
4 . Ef Core 3.1
哪种解决方案是正确的或更好的?
【问题讨论】:
-
首先如果你使用
EFCore < 3.0,它有一个奇怪的特性叫做客户端查询自动评估,所以要确保数据通过查看Output窗口以查看是否有关于该自动评估的任何警告,不会无意中将所有内容拉到客户端并在客户端上进行处理。其次,您可以直接尝试生成的查询,看看您是否真的可以通过编写原始查询来优化它。如果不是,那么它受您的数据库(而不是 LINQ)的限制。在这种情况下,我怀疑Family没有被索引,对该列的排序将非常慢。尝试索引它 -
索引和 EfCore 3.1 中的系列,在我写从子到父结果的左连接时,在 sql server 中很快。但在 EF 中,我不能离开 join from child to parent 。它转化为内部连接
-
您的查询中有一个
Distinct,恐怕您的左连接查询中没有包含它(可能只是为了测试速度)。如果要区分的复合列在一个表中,我们可以轻松地对其应用索引,但这里它们在 2 个表上。我可以看到它会导致一个相当复杂的查询。如果可能,您应该尝试共享该查询以供其他人查看。 -
SelectMany将使用内部连接。要显式使用左连接,您可能必须使用GroupJoin。但是我们仍然需要确定它是否真的被内部连接减慢了。我的意思是在慢版本和快版本之间编写查询时应该是公平的。 -
Distinct 用于防止重复父行。是的,当我删除不同的结果时,结果是快速但来自父级的重复记录。不同列中没有索引。
标签: sql asp.net entity-framework asp.net-core entity-framework-core