【问题标题】:Why does adding OrderBy to LINQ to EF query improve its performance?为什么将 OrderBy 添加到 LINQ to EF 查询会提高其性能?
【发布时间】:2018-09-01 21:57:49
【问题描述】:

请参阅下面的查询。为了不泄露机密/敏感信息,对象和属性名称经过了一定程度的混淆,但查询结构是相同的。

当添加.OrderBy(p => "") 时,这对我来说完全是无意义的,查询运行得更快。执行查询所需的时间从大约。 2000ms 下降到大约。 400 毫秒。我已经测试了几次,只添加和删除了 OrderBy 语句。

我完全不解,这怎么可能?查询在 Azure 环境中的 SQL 数据库上执行。

我可以理解,在属性 A 上排序数据,然后选择属性 A 等于某个值的记录可能会加快查询速度。但是在一个空字符串上排序!?这是怎么回事?

我还想指出,没有 OrderBy 的查询使用表达式(如 suggested in this post to circumvent SQL parameter sniffing)将执行时间也降低到大约。 400 毫秒。然后添加 .OrderBy(p => "") 不会产生任何明显的差异。

        var query = (from p in Context.Punders.Where(p => p.A == A)
                    .Where(p => null != p.SomeNumber)
                    .Where(p => p.StatusCode == Default ||
                                   p.StatusCode == Cancelled)
                    .Where(p => p.DatePosted >= startDate && p.DatePosted <= endDate)
                join f in Context.Founders.Where(f => f.A == A) on p.Code equals f.Code
                join r in Context.Rounders.Where(r => r.A == A) on p.Code equals r.Code
                    into rg
                from r in rg.DefaultIfEmpty()
                join pt in Context.FishTypes.Where(ft => ft.A ==A) on p.Code equals pt.Code
                where r == null
                select new
                {
                    p.Code,
                    f.B,
                    f.C,
                    p.D,
                    p.E,
                    pt.F,
                    pt.G,
                    p.H
                })
            .OrderBy(p => "");

不带.OrderBy(...的查询

SELECT [Filter1].[q]              AS [q], 
       [Filter1].[c1]                 AS [edoc], 
       [Filter1].[oc1]            AS [wnrdc], 
       [Filter1].[otc1]        AS [weener], 
       [Filter1].[ptc1]      AS [pmtpdc], 
       [Extent4].[isr]          AS [isr], 
       [Extent4].[rac] AS [rac], 
       [Filter1].[arn]              AS [arn] 
FROM   (SELECT [Extent1].[pcid]  AS [pcid1], 
               [Extent1].[edoc]            AS [c1], 
               [Extent1].[pmtpdc] AS [ptc1], 
               [Extent1].[q]        AS [q], 
               [Extent1].[arn]        AS [arn], 
               [Extent1].[dateposted]      AS [DatePosted], 
               [Extent2].[pcid]  AS [pcid2], 
               [Extent2].[wnrdc]       AS [oc1], 
               [Extent2].[weener]   AS [otc1] 
        FROM   [fnish].[post] AS [Extent1] 
               INNER JOIN [fnish].[olik] AS [Extent2] 
                       ON [Extent1].[olikedoc] = [Extent2].[edoc] 
               LEFT OUTER JOIN [fnish].[receivable] AS [Extent3] 
                            ON ( [Extent3].[pcid] = @p__linq__4 ) 
                               AND ( [Extent1].[edoc] = 
                                     [Extent3].[pepstedoc] ) 
        WHERE  ( [Extent1].[arn] IS NOT NULL ) 
               AND ( [Extent1].[posttedoc] IN ( N'D', N'X' ) ) 
               AND ( [Extent3].[id] IS NULL )) AS [Filter1] 
       INNER JOIN [fnish].[paymenttype] AS [Extent4] 
               ON [Filter1].[ptc1] = [Extent4].[edoc] 
WHERE  ( [Filter1].[pcid1] = @p__linq__0 ) 
       AND ( [Filter1].[dateposted] >= @p__linq__1 ) 
       AND ( [Filter1].[dateposted] <= @p__linq__2 ) 
       AND ( [Filter1].[pcid2] = @p__linq__3 ) 
       AND ( [Extent4].[pcid] = @p__linq__5 ) 

使用.OrderBy(...查询

SELECT [Project1].[q]              AS [q], 
       [Project1].[edoc]                  AS [edoc], 
       [Project1].[wnrdc]             AS [wnrdc], 
       [Project1].[weener]         AS [weener], 
       [Project1].[pmtpdc]       AS [pmtpdc], 
       [Project1].[isr]          AS [isr], 
       [Project1].[rac] AS [rac], 
       [Project1].[arn]              AS [arn] 
FROM   (SELECT N''                               AS [C1], 
               [Filter1].[c1]                 AS [edoc], 
               [Filter1].[ptc1]      AS [pmtpdc], 
               [Filter1].[q]              AS [q], 
               [Filter1].[arn]              AS [arn], 
               [Filter1].[oc1]            AS [wnrdc], 
               [Filter1].[otc1]        AS [weener], 
               [Extent4].[isr]          AS [isr], 
               [Extent4].[rac] AS [rac] 
        FROM   (SELECT [Extent1].[pcid]  AS [pcid1], 
                       [Extent1].[edoc]            AS [c1], 
                       [Extent1].[pmtpdc] AS [ptc1], 
                       [Extent1].[q]        AS [q], 
                       [Extent1].[arn]        AS [arn], 
                       [Extent1].[dateposted]      AS [DatePosted], 
                       [Extent2].[pcid]  AS [pcid2], 
                       [Extent2].[wnrdc]       AS [oc1], 
                       [Extent2].[weener]   AS [otc1] 
                FROM   [fnish].[post] AS [Extent1] 
                       INNER JOIN [fnish].[olik] AS [Extent2] 
                               ON [Extent1].[olikedoc] = [Extent2].[edoc] 
                       LEFT OUTER JOIN [fnish].[receivable] AS [Extent3] 
                                    ON ( [Extent3].[pcid] = 
                                         @p__linq__4 ) 
                                       AND ( [Extent1].[edoc] = 
                                             [Extent3].[pepstedoc] ) 
                WHERE  ( [Extent1].[arn] IS NOT NULL ) 
                       AND ( [Extent1].[posttedoc] IN ( N'D', N'X' ) ) 
                       AND ( [Extent3].[id] IS NULL )) AS [Filter1] 
               INNER JOIN [fnish].[paymenttype] AS [Extent4] 
                       ON [Filter1].[ptc1] = [Extent4].[edoc] 
        WHERE  ( [Filter1].[pcid1] = @p__linq__0 ) 
               AND ( [Filter1].[dateposted] >= @p__linq__1 ) 
               AND ( [Filter1].[dateposted] <= @p__linq__2 ) 
               AND ( [Filter1].[pcid2] = @p__linq__3 ) 
               AND ( [Extent4].[pcid] = @p__linq__5 )) AS [Project1] 
ORDER  BY [Project1].[c1] ASC 

结论

据我所知,有一点猜测:这是特定于案例的行为。在我的例子中,性能提升可能是由于 SQL 服务器正在构建不同的执行计划,从而产生更好的查询。我已经看到了一个不同的执行计划,其中没有OrderBy 的查询使用SQL 语句OPTION(RECOMIPILE) 显示出类似的性能提升。因此,将OrderBy 添加到 LINQ 查询很可能(我认为)产生不同的执行计划,从而产生更好的查询。

【问题讨论】:

  • 运行 SQL 分析器并检查 EF 生成的 sql 查询
  • 你可以试试 LINQPad。这是一个很棒的工具。你把 linq 写成 sql 或者 ef 就可以立即看到生成的 sql。 (我不隶属,只是一个用户)。这样,您可以看到添加 order by 子句时 SQL 的变化情况。
  • 鉴于您的注释,我认为 sql server 为您的查询缓存了计划,并且对于给定的值,该计划无效。通过添加订单,您可以强制它创建新的执行计划(因为它现在是不同的查询)。
  • 请用两条 SQL 语句更新您的问题。此外,如果您清除计划缓存会发生什么? stackoverflow.com/questions/8495210/resetting-execution-plans
  • @mjwills 感谢您的提醒。我将它们添加到帖子中。我看到使用OrderBy 生成的查询的第三个子查询,所以现在我很清楚性能提升可能是由于不同的执行计划。由于 SQL OPTION(RECOMPILE) 在通过 SQL Management Studio 运行“原始”SQL 查询时显示出类似的性能提升。

标签: c# sql-server performance linq-to-entities azure-sql-database


【解决方案1】:

鉴于你的笔记

我还想指出,没有 OrderBy 的查询使用 表达式(如本文中建议的那样规避 SQL 参数 嗅探)将执行时间也降低到大约。 400 毫秒。添加 .OrderBy(p => "") 然后没有任何明显的区别。

最合理的解释是:OrderBy 与使用显式值而不是参数的效果相同。因此,如果您有给定查询的预缓存计划,并且具有特定的参数值,则该计划不是最佳的(2 秒) - 通过添加无用的OrderBy 来更改此查询将强制 SQL Server 为该查询创建新的执行计划,因此会否定旧的非最优执行计划的影响。当然,应该清楚这不是一个否定计划缓存的好方法。

【讨论】:

    猜你喜欢
    • 2012-01-06
    • 2019-02-04
    • 2012-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    相关资源
    最近更新 更多