T-SQL查询性能之谜:为什么使用变量会有所不同?

T-SQL查询性能之谜:为什么使用变量会有所不同?,sql,sql-server,tsql,query-optimization,Sql,Sql Server,Tsql,Query Optimization,我正试图优化一个复杂的SQL查询,当我做出看似无关紧要的更改时,会得到截然不同的结果 例如,这需要336毫秒才能运行: Declare @InstanceID int set @InstanceID=1; With myResults as ( Select Row = Row_Number() Over (Order by sv.LastFirst), ContactID From DirectoryContactsByContact(1)

我正试图优化一个复杂的SQL查询,当我做出看似无关紧要的更改时,会得到截然不同的结果

例如,这需要336毫秒才能运行:

Declare @InstanceID int set @InstanceID=1;
With myResults as (
    Select 
        Row = Row_Number() Over (Order by sv.LastFirst), 
        ContactID
    From DirectoryContactsByContact(1) sv 
    Join ContainsTable(_s_Contacts, SearchText, 'john') fulltext on (fulltext.[Key]=ContactID)
    Where IsNull(sv.InstanceID,1) = @InstanceID
    and len(sv.LastFirst)>1
) Select * From myResults Where Row between 1 and 20;  
Declare @InstanceID int set @InstanceID=1;
With myResults as (
    Select 
        Row = Row_Number() Over (Order by sv.LastFirst), 
        ContactID
    From DirectoryContactsByContact(1) sv 
    Join ContainsTable(_s_Contacts, SearchText, 'john') fulltext on (fulltext.[Key]=ContactID)
    Where IsNull(sv.InstanceID,1) = 1
    and len(sv.LastFirst)>1
) Select * From myResults Where Row between 1 and 20;  
如果我用硬编码的数字替换@InstanceID,运行需要13秒(13890毫秒):

在其他情况下,我得到了完全相反的效果:例如,使用变量@s而不是文字“john”会使查询的运行速度慢一个数量级


有人能帮我把这个绑起来吗?变量什么时候使事情变得更快,什么时候使事情变得更慢?

原因可能是
IsNull(sv.InstanceID,1)=@InstanceID
@InstanceID
的某些值具有很强的选择性,但对其他值没有很强的选择性。例如,
InstanceID=null
可能有数百万行,因此对于
@InstanceID=1
,扫描可能会更快

但是,如果显式提供
@InstanceID
的值,SQL Server将根据表统计信息知道它是否是选择性的

首先,确保您的统计数据是最新的:

UPDATE STATISTICS table_or_indexed_view_name 

然后,如果问题仍然存在,则比较这两种方法的查询执行计划。然后,您可以使用带有硬编码值的。

强制执行最快的方法,优化器知道在构建执行计划时基于什么。 当您使用变量时,它试图“猜测”值,在许多情况下,它得到的不是最佳值

您可以通过两种方式帮助it部门选择优化值:

  • “我更清楚”,这将迫使它使用您提供的价值

    选项(针对(@InstanceID=1)进行优化)

  • “看看我在做什么”,这将指示它嗅探您传递的值,并使用随时间推移提供的值的平均值(或某些数据类型中最常用的值)

    选项(针对未知优化)


  • 您意识到使用
    TOP 20
    并将订单从行号移动到行号,意味着您不需要CTE?@OMG:只有在这些数字永远不变的情况下-如果他想要获得行800-820,CTE方法才更有效faster@OMG:@Gabriel是对的,这是用来传递分页结果的,所以它可能是20到40之间的
    等等。我遇到了一个类似的问题,我在Delete语句中有一个用于顶部(n)的变量和一个用于传入dateTime的变量。在测试过程中,如果我不使用变量,并对值进行硬编码,它运行得非常快。一旦我使用了变量,查询就会变得非常复杂,我怀疑这是因为SQL无法确定估计的有效行,因此它将执行页面锁定,而行锁定才是它真正需要的。我怀疑统计信息已经过时。这是否意味着统计信息也有助于预测存储过程中计算的变量可能是什么?
    选项(针对未知优化)
    将导致与使用变量完全相同的“猜测”行为<代码>选项(重新编译)将导致SQL Server在考虑实际变量值的情况下重新编译语句。选项(针对未知值进行优化)将使SQL能够根据您随时间推移而传递的实际值进行猜测,因此这不完全相同,尽管默认情况下您可以为所有SP启用此类行为