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,一个用于参数匹配,另一个用于其他方式。
答案 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