SQL - 使用常量值与参数的任何性能差异?

Posted

技术标签:

【中文标题】SQL - 使用常量值与参数的任何性能差异?【英文标题】:SQL - any performance difference using constant values vs parameters? 【发布时间】:2016-02-25 12:22:02 【问题描述】:

在性能方面有什么区别,当有许多查询在 where 子句中使用(不同的)常量值运行时,与在顶部声明参数的查询相反,参数值在哪里发生变化?

where 子句中具有常量值的示例查询:

select
*
from [table]
where [guid_field] = '00000000-0000-0000-000000000000' --value changes

提出的(改进的?)带有声明参数的查询:

declare @var uniqueidentifier = '00000000-0000-0000-000000000000' --value changes

select
*
from [table]
where [guid_field] = @var

有什么不同吗?我正在查看类似于上述两个查询的执行计划,但没有发现任何区别。但是,我似乎记得,如果您在 SQL 语句中使用常量值,那么 SQL 服务器将不会重用相同的查询执行计划,或者会导致性能下降——但这真的是真的吗?

【问题讨论】:

SQL Server 的编译和存储查询的规则相当复杂。一般来说,我认为它会根据传入的第一个参数编译查询。因此,如果查询计划被缓存并且您更改了参数,那么该计划的后续使用对于新值可能不是最佳的。但是,对于生成查询计划的第一次调用应该没问题。注意:从空表开始然后填充表也会影响最佳计划,即使传入的值相同。 【参考方案1】:

这里区分参数和变量很重要。参数被传递给过程和函数,变量被声明。

寻址变量,即问题中的 SQL 所具有的,在编译临时批处理时,SQL Server 会在它自己的权限内编译每个语句。 因此,当使用变量编译查询时,它不会返回检查任何分配,因此它将编译针对未知变量优化的执行计划。 在第一次运行时,这个执行计划将被添加到计划缓存中,然后以后的执行可以并且会为所有变量值重用这个缓存。

当您传递一个常量时,查询将根据该特定值进行编译,因此可以创建更优化的计划,但会增加重新编译的成本。

所以专门回答你的问题:

但是,我似乎记得,如果您在 SQL 语句中使用常量值,那么 SQL Server 将不会重用相同的查询执行计划,或者会导致性能更差的东西——但这真的是这样吗?

是的,相同的计划不能重复用于不同的常量值,但这并不一定会导致性能下降。可以为该特定常量使用更合适的计划(例如,为稀疏数据选择书签查找而不是索引扫描),并且此查询计划更改可能超过重新编译的成本。 SQL 性能问题几乎总是如此。答案是视情况而定

对于参数,默认行为是根据第一次执行过程或函数时使用的参数的时间来编译执行计划。

我之前已经通过示例更详细地回答了类似的问题,涵盖了上述很多内容,因此我不会重复它的各个方面,而是将问题链接起来:

Does assigning stored procedure input parameters to local variables help optimize the query? Ensure cold cache when running query Why is SQL Server using index scan instead of index seek when WHERE clause contains parameterized values

【讨论】:

【参考方案2】:

你的问题涉及很多东西,都与统计有关..

SQL 甚至为 Adhoc 查询编译执行计划,并将它们存储在计划缓存中以供重用,如果它们被认为是安全的。

select * into test from sys.objects

select schema_id,count(*) from test
group by schema_id


--schema_id 1 has 15
--4 has 44 rows

先问: 我们每次都在尝试不同的文字,因此如果认为安全,则 sql 会保存计划。您可以看到第二个查询估计与 literla 4 相同,因为 SQL 保存了 4 的计划

--lets clear cache first--not for prod
dbcc freeproccache

select * from test
where schema_id=4

输出:

select * from test where 
 schema_id=1

输出:

第二次提问: 将局部变量作为参数传递,让我们使用相同的值 4

--lets pass 4 which we know has 44 rows,estimates are 44 whem we used literals
declare @id int
set @id=4
select * from test

正如您在下面的屏幕截图中看到的,使用局部变量估计少了一些与统计有关的粗略的 29.5 行 ..

输出:

总而言之,统计数据对于选择查询计划(嵌套循环或进行扫描或查找)至关重要 ,从示例中,您可以看到每种方法的估计值有何不同。进一步从计划缓存膨胀的角度来看

您可能还想知道,如果我通过许多即席查询会发生什么,因为即使空间发生变化,SQL 也会为相同的查询生成一个新计划,以下是对您有帮助的链接

进一步阅读:http://www.sqlskills.com/blogs/kimberly/plan-cache-adhoc-workloads-and-clearing-the-single-use-plan-cache-bloat/http://sqlperformance.com/2012/11/t-sql-queries/ten-common-threats-to-execution-plan-quality

【讨论】:

【参考方案3】:

首先,请注意,局部变量与参数不同。

假设列已编入索引或具有统计信息,SQL Server 使用统计信息直方图根据提供的常量值收集估计的合格行数。如果查询很简单(无论值如何,都会产生相同的计划),查询也将被自动参数化和缓存,以便后续执行避免查询编译成本。

参数化查询还使用带有初始提供的参数值的统计直方图生成计划。该计划被缓存并重用于后续执行,无论它是否微不足道。

使用局部变量,SQL Server 使用整体统计基数来生成计划,因为实际值在编译时是未知的。当查询不是微不足道的时候,这个计划可能对某些值有好处,但对其他值来说不是最优的。

【讨论】:

以上是关于SQL - 使用常量值与参数的任何性能差异?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL使用默认值与在查询中设置值是不是有任何性能差异?

Lambda 捕获与参数 - 任何性能差异?

笔记:原始值与引用值执行上下文与作用域垃圾回收

R语言使用caret包对GBM模型参数调优SVM模型自定义参数调优RDF模型自定义参数调优(例如,ROC)重采样对多个模型的性能差异进行统计描述可视化多模型在多指标下的性能对比分析

如何使用 jOOQ 将 Oracle PL/SQL 常量作为参数传递?

使用选择的参数:RavenDB 与 SQL Server - 性能、可靠性和简单性 [关闭]