【问题标题】:T-SQL Where Clause Case Statement Optimization (optional parameters to StoredProc)T-SQL Where 子句案例语句优化(StoredProc 的可选参数)
【发布时间】:2010-12-05 02:53:58
【问题描述】:

我已经和这个斗争了一段时间了。我有一个存储过程,它接受 3 个用于过滤的参数。如果传入了特定值,我想对其进行过滤。如果传入-1,全部给我。

我试过以下两种方法:

第一种方式:

SELECT field1, field2...etc  
FROM my_view  
WHERE  
parm1 = CASE WHEN @PARM1= -1 THEN parm1  ELSE @PARM1 END  
AND parm2 = CASE WHEN @PARM2 = -1 THEN parm2  ELSE @PARM2 END  
AND parm3 = CASE WHEN @PARM3 = -1 THEN parm3  ELSE @PARM3 END

第二种方式:

SELECT field1, field2...etc  
FROM my_view  
WHERE  
(@PARM1 = -1 OR parm1 = @PARM1)  
AND (@PARM2 = -1 OR parm2 = @PARM2)  
AND (@PARM3 = -1 OR parm3 = @PARM3)  

我在某处读到第二种方法会短路,如果为真,则永远不会评估第二部分。我的 DBA 说它强制进行表扫描。我尚未对此进行验证,但在某些情况下似乎运行速度较慢。

该视图从中选择的主表有大约 150 万条记录,该视图继续连接大约 15 个其他表以收集大量其他信息。

这两种方法都很慢...从即时到任何地方都需要 2-40 秒,这在我的情况下是完全不可接受的。

是否有更好的方法不涉及将其分解为特定 vs -1 的每个单独案例?

感谢任何帮助。谢谢。

【问题讨论】:

    标签: sql-server tsql query-optimization case where-clause


    【解决方案1】:

    我在某处读到第二种方法会短路,如果为真,则永远不会评估第二部分。我的 DBA 说它会强制进行表扫描。

    你看错了;它不会短路。你的 DBA 是对的;它不能很好地与查询优化器配合使用,并且可能会强制执行表扫描。

    第一个选项差不多好。改进的选项是动态 sql 或具有过滤列的所有可能组合的长存储过程,以便您获得独立的查询计划。您也可以尝试使用“WITH RECOMPILE”选项,但我认为这对您没有帮助。

    【讨论】:

    • 第一个选项不一定会返回与第二个相同的结果。如果表中的行具有 NULL 值,则“选项 1”查询不会返回它们。例如.... Select * From Table Where NullableColumn = NullableColumn
    • 所有三个参数都不为空,因此在这种情况下这不是问题。看起来我可能会被选项 1 卡住。
    • 感谢大家的快速回复和中肯的建议。
    【解决方案2】:

    如果您运行的是 SQL Server 2005 或更高版本,则可以使用 IF 使用适当的 WHERE 生成多个版本的查询,以便可以使用索引。每个查询计划都会放在查询缓存中。

    另外,这里有一篇关于这个主题的非常全面的文章:

    Dynamic Search Conditions in T-SQL by Erland Sommarskog

    它涵盖了尝试编写具有多个可选搜索条件的查询的所有问题和方法

    这是目录:

    介绍 案例研究:搜索订单 北风数据库 动态 SQL 介绍 使用 sp_executesql 使用 CLR 使用 EXEC() 当缓存不是你真正想要的 静态 SQL 介绍 x = @x 或 @x 为空 使用 IF 语句 乌马昌达的诡计袋 使用临时表 x = @x AND @x 不为空 处理复杂条件 混合解决方案——同时使用静态和动态 SQL 使用视图 使用内联表函数 结论 反馈和致谢 修订历史

    【讨论】:

    • 已添加书签...谢谢。我们混合使用了 2000/2005 台服务器,不幸的是这台是 2000 台。
    • 本文介绍了多种处理可变搜索条件的方法,IF 只是其中一种
    【解决方案3】:

    如果你在想要所有东西的时候传入一个空值,那么你可以把你的 where 子句写成

       Where colName = IsNull(@Paramater, ColName)  
    

    这与您的第一种方法基本相同...只要列本身不可为空,它就可以工作...空值 IN 列会稍微弄乱它。

    加速它的唯一方法是在 Where 子句中被过滤的列上添加一个索引。已经有了吗?如果没有,那将带来显着的改善。

    【讨论】:

    • 您认为这会加快速度吗?看起来它基本上是相同的操作,但检查 null 而不是 -1。 IsNull 是否比 CASE 更有效?
    • @IronicMuffin,(我喜欢你的绰号)不,你完全正确,这相当于你的第一种方法......
    【解决方案4】:

    我想不出其他办法:

    在哪里

    (MyCase 为 NULL 或 MyCase = @MyCaseParameter) 和 ....

    如果你问我,第二个对开发者来说更​​简单、更易读。

    【讨论】:

    • 它也像糖蜜一样慢。不要使用它。
    • @Joel 你有什么可以支持的吗?并不是我怀疑你,我只是在寻找一些硬数据。
    • 我有两个指标表明第二种方法可能会强制进行表扫描,这对于 150 万条记录来说不是一件好事。
    • @Joel:这种像糖蜜一样慢的概念是完全错误的。您是说在客户端应用程序中动态构建 WHERE 子句更好吗?我不敢苟同...这是干净的正确代码,应该提示他的 DBA。
    【解决方案5】:

    SQL 2008 及更高版本对 (MyCase IS NULL OR MyCase = @MyCaseParameter) AND .... 之类的优化做了一些改进

    如果您可以升级,并且添加 OPTION (RECOMPILE) 以获得所有可能的参数组合的良好性能(在这种情况下,没有适合所有可能的参数组合的单一计划),您可能会发现这表现良好。

    http://blogs.msdn.com/b/bartd/archive/2009/05/03/sometimes-the-simplest-solution-isn-t-the-best-solution-the-all-in-one-search-query.aspx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-21
      相关资源
      最近更新 更多