【问题标题】:Avoiding branching in stored procedures that return search results?避免在返回搜索结果的存储过程中分支?
【发布时间】:2010-06-22 14:56:21
【问题描述】:

我有一个应用程序需要从 SQL Server 2008 数据库返回搜索结果。我想使用单个存储过程来返回结果,但我发现当我构建存储过程时,它充满了许多 Else .. Else If 语句,查询一遍又一遍地重复,根据用户搜索略有不同标准。

有没有更好的方法来解决这个问题?我试图避免编写动态 SQL,因为我想要执行计划的好处,但我认为必须有更好的方法。有没有人有任何建议或示例如何最好地设计一个必须处理许多搜索参数的存储过程,其中许多可能是 NULL?谢谢。

【问题讨论】:

    标签: sql sql-server stored-procedures dynamic-sql


    【解决方案1】:

    不是。

    使用 SQL Server 2005 及更高版本进行语句级重新编译,OR 子句的惩罚更少,只是维护复杂性。

    使用 Richard Harrison 的方法会使情况变得更糟,因为 OR 不可分割,运行缓慢,很可能不会使用索引。

    动态 SQL 会引发 SQL 注入、引用和缓存问题。

    根据 CountZero 的回答,sp_executesql 仍然需要构建字符串。

    解决方案可能不是基于代码的...您真的需要在任何时候搜索所有字段吗?我会尝试将搜索分为简单搜索和高级搜索,或者找出最常见的搜索并尝试涵盖这些查询。

    【讨论】:

    • 应用需要搜索员工。用于搜索员工的数字变量很大。从部分匹配的名字和姓氏到该人被雇用的日期范围和公司的当前状态。然后我需要添加逻辑以返回具有不同 order 子句的结果,以支持 UI 中的排序。我不确定如何将其分解为单独的存储过程。
    • 在 UI 中排序会有所帮助,在一些古怪的网格中,并且只在 SQL 中进行初始排序。但同样,每一列是否都被平等地搜索和使用?我应该说最常见的有一些 ELSE 子句,其余的有一个缓慢而可怕的。
    • 我认为你对我的方法是错误的,因为我认为常量表达式会导致整个子句被优化掉,从而使情况变得更糟。我检查了执行计划,在与直接字段 = 值选择进行比较时,我没有发现任何不妥之处。但是,如果我错了,我很乐意了解原因。
    • @Richard Harrison:除非您使用 CASE,否则不能保证 SQL Server 可以使用快捷方式(即按您期望的顺序计算)。领先的 % 也使索引使用无效。你的计划都是表和索引扫描,不是吗?
    • 你是对的——它会进行索引扫描——所以我想出了一个允许索引搜索(非聚集)的版本。在计划中看起来好多了。谢谢。
    【解决方案2】:

    我总是通过使用默认值和条件来做到这一点;例如

    CREATE PROCEDURE [dbo].[searchForElement]
    (
        @Town     nvarchar(100) = '',
        @County   nvarchar(100) = '',
        @postcode nvarchar(100) = ''
    )
    AS
    BEGIN
        SET NOCOUNT ON;
    
        SELECT <fields> 
    
        FROM table 
        WHERE 
            (@Town = ''             OR Town     LIKE '%'+@Town+'%')
            AND (@County = ''       OR County   LIKE '%'+@County+'%')
            AND (@postcode = ''     OR postcode LIKE '%'+@PostCode +'%')
    END
    

    编辑:

    正如@gbn 正确建议的那样,上述将导致索引扫描,这对于大型表可能是一个问题。如果这是一个问题,解决方案是在下面使用 ISNULL 并且将 NULL 添加到任何结果都会导致 NULL 的事实将允许索引搜索因为优化器可以理解“%”(在 SQL2008 上测试)。这可能不太可读,但它可以更好地利用索引。

    CREATE PROCEDURE [dbo].[searchForElement]
    (
        @Town     nvarchar(100) = NULL,
        @County   nvarchar(100) = NULL,
        @postcode nvarchar(100) = NULL
    )
    AS
    BEGIN
        SET NOCOUNT ON;
    
        SELECT <fields> 
    
        FROM table 
        WHERE   Town     LIKE ISNULL('%'+@Town+'%', '%')
           AND  County   LIKE ISNULL('%'+@County+'%', '%')
           AND  Postcode LIKE ISNULL('%'+@PostCode +'%', '%')
    END
    

    【讨论】:

    • 恐怕我很难理解 WHERE 子句中的逻辑。如果@Town 参数为“空白”,LIKE 子句是否仍使用 LIKE 查找匹配项?
    • @Town='' 的原因是允许优化器将整个子句短路,因为它总是正确的,因此不需要索引扫描。当然,这是我一直相信的,并且已经对照执行计划进行了检查。这种技术我只用过数万条记录,性能没有问题。
    【解决方案3】:

    我自己也经常遇到这个问题。倾向于使用动态 SQL,只要你使用 sp_executesql 那么优化器就会尝试使用相同的执行计划。

    http://ayyanar.blogspot.com/2007/11/performance-difference-between-exec-and.html

    【讨论】:

    • 感谢您的文章。我想我总是可以选择在存储过程中使用动态 SQL。
    • 如果标准数量增加、表大小增加并且您关心性能,则唯一的选择。
    猜你喜欢
    • 2011-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-22
    • 2015-06-03
    • 2013-06-21
    • 2020-05-03
    相关资源
    最近更新 更多