【问题标题】:Linq performance queryLinq 性能查询
【发布时间】:2013-08-04 04:00:12
【问题描述】:

我有这个查询可以提供正确的结果,但运行大约需要 15 秒

int Count= P.Pets.Where(c => !P.Pets.Where(a => a.IsOwned == true)
.Select(a => a.OwnerName).Contains(c.OwnerName) && c.CreatedDate >= 
EntityFunctions.AddDays(DateTime.Now, -8)).GroupBy(b=>b.OwnerName).Count();

如果我删除这部分 linq

 '&& c.CreatedDate >= EntityFunctions.AddHours(DateTime.Now, -8)'

运行只需大约 3 秒。我怎样才能保持相同的情况发生但要快得多? 我需要该日期标准,因为我不希望将任何 8 天前创建的 Classeses 包含在计数中

编辑

我有一个名为 People 的表,在此查询中称为 P,我想返回没有所有者的 Pets 总数,并从查询中删除没有所有者的 Pets ' 没有所有者,即使它们存在于另一个 Pet 引用中也没有该 Pet 的所有者。这意味着如果一个人在 Pets 表中至少有一条记录被视为宠物的所有者,那么我想删除该人存在于返回查询中的所有情况,一旦完成,只返回已创建的 Pets超过 8 天

【问题讨论】:

  • 如果您能从查询中解释您想要什么,那就太好了。它看起来过于嵌套和复杂。
  • 查看正在执行的查询会很有帮助。你能用探查器拦截它吗?
  • @Keith 不知道该怎么做?
  • 请解释查询需要返回什么。看起来它可以比这简单得多。
  • @jake 在您的数据库上放置一个分析器并拦截针对您的 LINQ 执行的查询

标签: c# sql linq-to-sql query-performance


【解决方案1】:

您应该缓存日期并将评估放在首位(因为DateTime 评估应该比Contains 评估更快)。还要避免多次重新计算同一个查询。

DateTime eightDaysOld = EntityFunctions.AddHours(DateTime.Now, -8);

//calculate these independently from the rest of the query
var ownedPetOwnerNames = P.Pets.Where(a => a.IsOwned == true)
                               .Select(a => a.OwnerName);

                              //Evaluate the dates first, it should be 
                              //faster than Contains()
int Count = P.Pets.Where(c => c.CreatedDate >= eightDaysOld &&

                              //Using the cached result should speed this up
                              ownedPetOwnerNames.Contains(c.OwnerName))
                  .GroupBy(b=>b.OwnerName).Count();

这应该返回相同的结果。 (希望)

【讨论】:

    【解决方案2】:

    您将失去任何使用该 sn-p 索引的能力,因为它会计算每一行的静态日期。在查询之前声明一个 DateTime 变量并将其设置为 DateTime.Now.AddHours(-8) 并在 where 子句中使用该变量而不是您的 sn-p。

    【讨论】:

    • 很可能是SQL Server选择了非索引查询计划,但不一定是函数调用的原因。
    • 我将代码更改为 Date 变量而不是 EntityFunctions 并且速度仍然相同
    • 查看为该查询生成的 sql 并查看查询计划。它会告诉你任何缺失的索引或瓶颈。
    【解决方案3】:

    通过分离查询并在其上调用 ToList() 并将其插入主查询中,使其运行速度提高 4 倍

     var ownedPetOwnerNames = P.Pets.Where(a => a.IsOwned == true)
                               .Select(a => a.OwnerName).ToList();
    
    
    int Count = P.Pets.Where(c => c.CreatedDate >= Date&&
    
      ownedPetOwnerNames.Contains(c.OwnerName)).GroupBy(b=>b.OwnerName).Count();
    

    【讨论】:

    • 可能只有少量数据。如果ownedPetOwnerNames 包含 10,000 个名称怎么办?查询甚至不会运行。
    • @GertArnold - 你的意思是查询甚至不会运行?
    • 在 linq-to-sql 中,ownedPetOwnerNames 中的每个字符串都将被放入一个变量中。对于大约 2000 个变量,您将得到一个异常,即过程调用中有太多变量。除此之外,它变得非常缓慢。
    • @GertArnold 那么有什么更好的方法让它更快呢?这是我看到的唯一真正有用的方法
    • 请参阅我提出的解决方案。
    【解决方案4】:

    您可以使用(并且可能首先创建)导航属性Pet.Owner

    var refDate = DateTime.Today.AddDays(-8);
    
    int Count= P.Pets
                .Where(p => !p.Owner.Pets.Any(p1 => p1.IsOwned)
                            && p.CreatedDate >= refDate)
                .GroupBy(b => b.OwnerName).Count();
    

    这可能会提高性能,因为 Contains 已消失。至少它比你的两阶段查询具有更好的可扩展性,Contains 涉及不可预测的字符串数量。

    当然,您还需要确保CreatedDate 上有索引。

    【讨论】:

    • 导航属性是什么意思。在需要一些指导之前,我从未尝试过。
    • 导航属性只是从一个实体“导航”到其他实体的属性,例如 Owner.Pets 或 Pet.Owner。它们相当于数据库中的外键。当您创建 linq-to-sql 图时,您会看到这些属性显示为实体之间的关联(如果您的数据库有外键)。
    • 他们已经在使用外键关系,这是否意味着它已经在使用 Navigation 属性?
    • 嗯,你有Owner.Pets之类的东西吗?
    • @GertArnold- 我对如何设置它感到困惑。我从来没有真正透露过我的桌子的真实细节,因为我想尽可能地简化它;但不知道如何让你的设计在我的桌子上工作。我有一张有数千只宠物的桌子,有些是拥有的“owned=true”,我在这张桌子上有 PetSourceName。PetSourceName 是将这个宠物添加到 Pet 表中的人的来源。我没有从 PetSource 跟踪任何其他内容。但是一旦 PetSourceName 在表中拥有一只宠物,我就不会在我的列表中返回它的任何其他宠物。
    猜你喜欢
    • 2012-05-16
    • 2015-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多