按参数过滤查询的最佳方法是什么?

时间:2009-03-25 19:21:49

标签: sql tsql

我一直在使用此方法来过滤我的查询:

Create PROCEDURE [dbo].[pGetTask]
    @showCompletedTasks bit = 1
    ,@showInProgressTasks bit = 1
    ,@taskID int = null
    ,@projectID int = null
    ,@applicationID int = null
    ,@clientID int = null

... Snip ...

where       
    a.clientID = isnull(@clientID, a.clientID)
    and a.applicationID = isnull(@applicationID, a.applicationID)
    and p.projectID = isnull(@projectID, p.projectID)
    and t.taskID = isnull(@taskID, t.taskID)
    and curr.complete = case @showCompletedTasks when 0 then 0 else curr.complete end
    and curr.complete = case @showInProgressTasks when 0 then 1 else curr.complete end

这实际上会使我的查询在664行结果集上减慢2秒。 SQL调优顾问没有多大帮助,所以我认为这不是正确的方法。除了大量的if语句之外,还有正确的方法吗?

6 个答案:

答案 0 :(得分:6)

假设您已正确索引选择所在的表,并且这些字段是索引的一部分,我的猜测是它将是对isnull的调用。我会改变它们:

(@clientID is null or a.clientID = @clientId) and ...

至于case语句,位字段的索引是没有意义的,因此没有太多可做的事情。

答案 1 :(得分:2)

检查索引&统计。这看起来有点慢。另一种选择是进行动态查询,实质上是构建一个表示你的sql的字符串,并使用sp_ExecuteSql(或exec语句)执行它

修改

您可以尝试将两种情况合并,但我怀疑它会对查询的性能产生影响。虽然看起来会更好......

虽然我不确定你的查询是否正确(如果没有更多信息,这很难说),但是在你试图提供两个状态返回的情况之间不应该存在或者条款,并且我假设我可以要求仅完成,仅完成或两者都有...在这种情况下,您需要一个或

答案 2 :(得分:1)

您最好的选择是使用此存储过程调用一系列更具体的过程。你有两个问题:

  1. 案例陈述的使用 导致表扫描,其中 (显然)忽略你的任何索引 可能有
  2. 即使你打破了 声明分成几个 有条件地叫,你还会 最终得到编译执行 计划特定于第一个 请致电此程序。
  3. 如果您创建特定的过程,例如pGetTask_Completed和pGetTask_InProgress,并在此过程中有条件地调用它们,那么您应该没有任何问题。

答案 3 :(得分:1)

你可能成为“参数嗅探”问题的受害者。 MS-SQL将把您第一次运行SP的参数作为制定查询计划的最佳样本。由于这个原因,您的查询可能会很慢。

要证明,请尝试通过将填充的参数模拟为变量来直接运行查询的内容。如果它快得多,那么你确实遇到了“参数嗅探”问题。

解决方案是欺骗MS-SQL认为您的参数仅用于分配给另一个变量。例如:


create proc ManyParams
(
    @pcol1 int,
    @pcol2 int,
    @pcol3 int
)
as
declare
    @col1 int,
    @col2 int,
    @col3 int

select
    @col1 = @pcol1,
    @col2 = @pcol2,
    @col3 = @pcol3

select 
    col1,
    col2,
    col3
from 
    tbl 
where 
    1 = case when @col1 is null then 1 else case when col1 = @col1 then 1 else 0 end end
and 1 = case when @col2 is null then 1 else case when col2 = @col2 then 1 else 0 end end
and 1 = case when @col3 is null then 1 else case when col3 = @col3 then 1 else 0 end end

答案 4 :(得分:0)

这是一篇关于这个主题的精彩文章:

http://www.sommarskog.se/dyn-search-2005.html

它会给你很多尝试的想法。

我倾向于混合使用这些“搜索”类型的查询。以下是我似乎一直使用的一些内容:

  • 我尝试使某些搜索参数成为必需,因此您可以在这些参数上点击索引。

  • 如果可能(取决于行数),使用临时表拆分查询。如果您只有几百个ClientID值,请创建一个#ClientID临时表。放入用户想要的或全部的。然后,您可以将FROM表和/或内部联接到其他表中,以使其更快。

  • 如果您的日期是可选的,请不要使用(@startDate为null或a.date> = @startDate)。做一些像SET @ startDate = COALESCE(@ startDate,'01 / 01/1970')的事情。这将为您提供一个值并消除使用“OR”并将使用索引。

答案 5 :(得分:0)

casperOne的建议就是我要开始的。

另一种可能性是:

WHERE
     (1 =
     CASE
         WHEN @client_id IS NULL THEN 1
         WHEN a.clientID = @clientID THEN 1
         ELSE 0
     END) AND
...

我发现使用CASE语句的SQL Server(至少2005年)会导致查询计划短路其余的逻辑。在简单比较的情况下,这并不是一个大问题,但如果您的逻辑包含子查询或其他一些昂贵的操作,那么将其短路可能是一个很大的帮助。在你的例子中,我只会考虑casperOne的建议。

另外,如果您使用上面的CASE方法,则需要将RECOMPILE选项添加到SELECT和存储过程中。