【问题标题】:Move the constant to variable in the where clause dramatically change the execution plan?将 where 子句中的常量移动到变量中会显着改变执行计划?
【发布时间】:2018-04-18 23:36:27
【问题描述】:

我为复杂视图创建了一个索引。在 Sql Server 管理工作室中运行以下查询需要 0 到几秒钟。查询计划显示 99% 的成本在我为主大表创建的索引上的 Index Seek 上。 (总子树成本为 7.5)

select * from ComplexView where id = 10000 and theDate = '1/1/2018'

但是,下面的查询

declare @id int = 10000, @theDate datetime = '1/1/2018'
select * from ComplexView where id = @id and theDate = @theDate

需要很长时间(3 到 10 分钟),查询计划捕获显示 57% 的成本在主大表上的 Table Scan 上(加上 10% 的过滤器和 14% 的哈希匹配和 17% 的排序, 总子树成本为 260)。

【问题讨论】:

    标签: sql-server parameters sql-execution-plan


    【解决方案1】:

    第一个查询将根据您的文字 10000'1/1/2018' 的值以及所涉及表的直方图统计信息编译一个查询计划。

    第二个本质上是“优化未知”,因为参数的值在编译时被认为是未知的。当您使用局部变量时,SQL Server 无法再使用直方图。相反,它使用有关统计对象的密度向量的信息。

    这是已知和预期的行为(尽管如果您不知道它可能会令人惊讶)。

    您为两个查询打开了显示实际执行计划,发现它们有不同的计划。

    如果您碰巧首先使用“典型”值(基于所涉及的列的分布)运行查询,您将获得适用于大多数过滤器值的缓存计划。

    假设您的统计数据是最新的,试试这个:

    declare @id int = 10000, @theDate datetime = '1/1/2018'
    select * from ComplexView 
    where id = @id and theDate = @theDate
    option(recompile)
    

    你有更合理的计划吗?

    使用option(recompile) 是“视情况而定”的答案之一!如果查询是报告查询(我的意思是它运行时间相对较长且不经常运行),则考虑添加它。它告诉 SQL Server 不要缓存计划并在每次执行时重新编译一个新计划。它的缺点是您不会在缓存中看到计划,并且您在每次执行时支付计划编译的成本(与长时间运行的查询相比相对较小)。

    如果这是一个重要的报告查询,那么我会添加 option(recompile) 以确保您永远不会得到不合适的查询计划。

    【讨论】:

    • 添加option(recompile) 使其必须更快,并且它在另一个索引上使用索引搜索。统计信息已更新。为什么需要重新编译查询?我应该将它添加到生产中吗?
    • 奇怪的是,为什么大多数情况下我不需要option (recompile) 来获得正确的计划,即使有参数?
    • 如果您碰巧首先使用“典型”值运行查询(基于所涉及列的分布),您将获得适用于大多数过滤器值的缓存计划。
    • 那么问题可以通过清除缓存并使用参数运行创建新缓存来解决?
    • 会,但是清除缓存比简单地重新编译单个查询要重得多
    猜你喜欢
    • 2013-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-31
    • 1970-01-01
    • 2019-07-17
    • 2018-06-20
    • 2014-04-11
    相关资源
    最近更新 更多