【问题标题】:What is the most efficient way to optimize a filter based on parameters on a SQL Server stored procedure?根据 SQL Server 存储过程的参数优化筛选器的最有效方法是什么?
【发布时间】:2021-11-25 21:03:28
【问题描述】:

现在我有类似的东西:

CREATE PROCEDURE [dbo].[sp_GetFilteredInformation]
    @pItem nvarchar(max) = NULL, 
    @pCity nvarchar(max) = NULL,
    @pSerialN nvarchar(max) = NULL, 
    @pPromise datetime = NULL,
    @pSalesOrder nvarchar(max) = NULL, 
    @pLineNumber int = NULL

    DECLARE @vQuery nvarchar(max)

    IF (@pItem IS NOT NULL) 
    BEGIN 
        SET @vQuery += 'AND ITEM LIKE '' + @pItem + ''' 
    END

    IF (@pCity IS NOT NULL) 
    BEGIN 
        SET @vQuery += 'AND CITY LIKE '' + @pCity + ''' 
    END

...等等,所以最后我会有

SELECT * 
FROM TABLE 
WHERE 1 = 1 + @vQuery

我认为这会奏效,但对我来说似乎效率不高。有没有办法优化此过程并使用多个参数过滤信息,其中一些参数为空?

【问题讨论】:

  • 警告:您的代码危险。您正在将参数注入到动态语句中,而不是参数化它们。这尤其是更糟糕,因为你正在给恶意的人提供 8GB(或 40 亿个字符)来玩。
  • 这种厨房水槽查询的特殊方法实际上是最有效的,只是你需要确保将实际数据参数一路传递,使用sp_executesql,不要注入它们跨度>
  • 旁注:您应该为您的存储过程使用sp_ 前缀。微软有reserved that prefix for its own use (see Naming Stored Procedures),你确实会在未来某个时候冒着名称冲突的风险。 It's also bad for your stored procedure performance。最好只是简单地避免 sp_ 并使用其他东西作为前缀 - 或者根本不使用前缀!
  • “关于点,这是一个错字”但是sp_前缀也是一个问题,不应该使用。跨度>

标签: sql sql-server database tsql


【解决方案1】:

执行这种kitchen-sink query 的最有效方法实际上就是您现在正在执行的方式,只是您应该正确地参数化每个过滤器。

这是因为将为每个可能的过滤器组合缓存一个计划。这意味着无论何时再次使用该过滤器组合,即使使用不同的值,也会使用缓存计划。

而如果您使用OPTION(RECOMPILE),则每次运行都会生成一个新计划。而OPTION(OPTIMIZE FOR UNKNOWN) 通常只会为您提供一个总体上不太好的计划。

所以你用sp_executesql参数化它,像这样

CREATE PROCEDURE [dbo].[sp_GetFilteredInformation]
    @pItem nvarchar(max) = NULL, 
    @pCity nvarchar(max) = NULL,
    @pSerialN nvarchar(max) = NULL, 
    @pPromise datetime = NULL,
    @pSalesOrder nvarchar(max) = NULL, 
    @pLineNumber int = NULL

DECLARE @vQuery nvarchar(max) = '
SELECT *
FROM YourTable
WHERE 1=1
';

IF (@pItem IS NOT NULL) 
    SET @vQuery += 'AND ITEM LIKE @pItem
';

IF (@pCity IS NOT NULL) 
    SET @vQuery += 'AND CITY LIKE @pCity
';

-- etc

-- for testing you can use PRINT @vQuery

EXEC sp_executesql
    @vQuery,

  N'@pItem nvarchar(max), 
    @pCity nvarchar(max),
    @pSerialN nvarchar(max), 
    @pPromise datetime,
    @pSalesOrder nvarchar(max),
    @pLineNumber int',

    @pItem = @pItem,
    @pCity = @pCity,
    @pSerialN = @pSerialN,
    @pPromise = @pPromise,
    @pSalesOrder = @pSalesOrder,
    @pLineNumber = @pLineNumber;

【讨论】:

    【解决方案2】:

    优化通用过滤器的唯一方法是在 SQL 动态查询中解析过滤器的所有项目和条件。

    为此,您需要有 2 个 XML 参数:

    • 列出要到达的列,格式为: date_begintown
    • 另一个具有数据类型和值的,例如:2021-09-31巴黎

    然后使用这两个仅包含搜索值列的字符串,您可以构建一个具有特定且可优化的 WHERE 谓词的查询...

    【讨论】:

    • 除了使用 2 个 XML 参数之外,还有很多其他方法。特别是当 XML 不允许强类型值时。
    • 我没有说 XML 是唯一的方法...我确实说过优化它的唯一方法是解析...
    • 您说“您需要有 2 个 XML 参数”,这意味着没有其他选择。但是,有些选项您根本不需要“需要”XML 参数。
    猜你喜欢
    • 2016-03-09
    • 1970-01-01
    • 2021-12-06
    • 2017-09-07
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多