强制SQL Server执行所需的执行计划

时间:2017-07-27 08:56:41

标签: sql sql-server sql-server-2012

SQL Server版本:2012

存储过程接受参数@personID

程序是这样写的:

select [some columns] 
from 
T1 join T2
on T1.id=T2.id
where
[condition 1]
and 
[condition 2]
and 
(
  @personid=10
  or 
  @personid<>10 and T1.addressID in 
                    ( select T3.addressID from 
                      T3 join T4 on T3.uid=T4.uid 
                      where [some conditions]
                    )
)

问题是:
SQL Server正在执行下半部分,即@personid<>10和T1。*,即使是personid=10,这会导致不必要的读取和CPU时间。

是否有任何方式像if / else,case语句将其限制为在第一个条件满足时不检查后半部分。

  

编辑:

我喜欢VojtěchDohnal的答案(即使用选项(重新编译))以及Rigerta Demiri的答案(即使用if / else或单独的存储过程)。我会说,这是一个打破平局。

对所有寻求解决类似问题的人:
我的建议是,如果查询的编译时间非常短并且也不是生产中经常执行的语句/程序之一,那么一定应该使用选项(重新编译)

如果不符合上述条件,则应使用if / else方法或使用两个单独的存储过程。

考虑到我的case中的select语句有如此多的嵌套查询这一事实,它的编译时间不短,而且它也是生产中最常用的程序之一,所以我已经创建了两个独立的SP,一个用于参数匹配,另一个用于其他方式。

3 个答案:

答案 0 :(得分:3)

添加

OPTION (RECOMPILE)

查询。

请参阅this article

  

所有@x IS NULL子句的效果是如果是输入参数   为NULL,则相应的AND条件始终为true。从而,   唯一有效的条件是搜索的条件   参数具有非NULL值。听起来很简单,但有一个   在有或没有最后一行的情况下,性能差异很大   本:

     

选项(重建)

     

提示指示SQL Server每次都重新编译查询。   没有这个提示,SQL Server会生成一个将被缓存的计划   重复使用。这有一个非常重要的含义:该计划必须与之合作   参数的所有可能输入值。由于参数   嗅探,可以针对参数组合优化计划   第一次搜索。该计划可能完全表现不佳   不同的参数,虽然它仍然不是完美的   初始参数组合。为了获得最佳响应时间   用户提供单个订单ID,我们希望优化器使用   订单和订单明细中的OrderID上的索引并忽略所有内容   其他。但是,如果用户搜索产品ID或产品   name,我们想在Order Details中使用ProductID上的索引,依此类推   其他搜索标准。

     

这正是我们通过提示OPTION(RECOMPILE)实现的目标。   由于指示SQL Server每次都重新编译查询,   没有必要缓存计划,为什么SQL Server 可以处理所有   变量作为常量。

请注意,此行为肯定适用于SQL Server 2012及更高版本的低版本may have other older behaviors implemented

要查看是否有效,请检查查询的执行计划 - 将其放在存储过程之外,并将其作为一个单独的查询进行检查。

答案 1 :(得分:1)

对于Kimberly L. Tripp schema在本文中完美讨论和解释的内容,这是一个简单的案例场景。

其他方式是您可以简单地将查询放在IF-ELSE块中。

if @personId = 10
    begin

        select [some columns] 
        from 
        T1 join T2
        on T1.id=T2.id
        where
        [condition 1]
        and 
        [condition 2]
    end
else
    begin

        select [some columns] 
        from 
        T1 join T2
        on T1.id=T2.id
        where
        [condition 1]
        and 
        [condition 2]
        and 
         @personid<>10 and T1.addressID in 
                            ( select T3.addressID from 
                              T3 join T4 on T3.uid=T4.uid 
                              where [some conditions]
                            )
    end

或者,您可以动态构建整个查询并按照链接中的说明执行它。

希望它有所帮助!

  

编辑:

如果您真正想要的是在执行此分支逻辑时只评估一个查询执行计划,而不是两者(正如查询优化器在执行过程时将执行的那样),那么您必须将其分成两个不同的子过程并从父包装程序中调用它们。

这样,对于每个调用,您只能获得一个针对参数进行优化的计划。

我在这里做了类似的事情(使用一个名为category的不同样本表)并获得了以下结果:

create table category (category_no int identity(1,1) not null, category_desc varchar(100))
alter table [dbo].[category] add constraint [category_ident] primary key clustered ( [category_no] asc ) 

insert into category (category_desc) values ('test1', 'test2', 'test3')



create procedure testing_noparams
as
begin
    select * from category 
end

create procedure testing_withparams @categoryNo int 
as
begin
    select * from category where category_no = @categoryNo
end

create procedure wrapper @categoryNo int = null
as
begin

    if @categoryNo is null 
        begin
            exec testing_noparams
        end

    else
        begin
            exec testing_withparams @categoryNo
        end
end

执行:

exec wrapper null
exec wrapper 2

计划:

Building High Performance Stored Procedures

这种方法的问题是引入一个新程序,但是:)

答案 2 :(得分:0)

此时,执行计划无关紧要,因为逻辑不正确。也许更好的解决方案是与Erland关于动态搜索的讨论的链接 - 这需要额外的复杂性,但此时可能超出您的需求/能力。

如果您正确地填补了您的条件,您将解决您的直接逻辑问题。 where子句中有3个条件。您的最后一个条件包括两个不同的子条件。从逻辑上讲,你有&#34;和(x或y)&#34;其中y也是一组2个条件。所以我们可以将它扩展到&#34;和(x或(y和z))&#34;。但没有括号表明z&#34;与#34;年。简而言之,了解logical operator precedence