2

我正在编写一个通用搜索存储过程,以根据用户可以在 UI 中选择的许多过滤器(使用 MS-SQL 2008)在表中搜索。

这是简化版本:

CREATE PROCEDURE SearchAll
    @FirstName NVARCHAR(MAX) = NULL,
    @LastName NVARCHAR(MAX) = NULL,
    @Age INT = NULL
AS
    SELECT * 
    FROM persons 
    WHERE 
        (@FirstName IS NULL OR FirstName = @firstname)
        AND (@LastName IS NULL OR LastName = @LastName)
        AND (@Age IS NULL OR Age = @Age)

似乎如果我将 NULL 传递给 @Age 就不会有性能成本。但是,当我使用大量数据进行测试时,我失去了很大的性能!

这是逻辑上相同但实际上非常不同的查询:

DECLARE @FirstName NVARCHAR(MAX) = NULL
DECLARE @Age INT = 23
------------First slow------------
SELECT * 
FROM persons 
WHERE 
    (@FirstName IS NULL OR FirstName = @firstname)
    AND (@Age IS NULL OR Age = @Age)
------------Very fast------------
SELECT * 
FROM persons 
WHERE 
    Age = @Age

有没有漏掉一点?

我知道 SQL 引擎会找到索引的最佳匹配,并且...(在运行查询之前),但很明显:@FirstName IS NULL并且不需要分析任何内容。

我还在ISNULL查询中测试了函数(结果相同)。

4

2 回答 2

5

包含此构造的查询@variable is null or @variable = column是性能灾难。这是因为创建了 SQL 计划,因此它们适用于变量的任何值。有关该主题的冗长讨论、问题和可能的解决方案,请参阅T-SQL 中的动态搜索条件

于 2012-03-27T07:06:26.853 回答
0

正如已经提到的,问题是查询计划将被构建为使用任何变量值。您可以通过仅使用所需参数构建查询来规避此问题,如下所示:

CREATE PROCEDURE SearchAll
    @FirstName NVARCHAR(MAX) = NULL,
    @LastName NVARCHAR(MAX) = NULL,
    @Age INT = NULL
AS
BEGIN
    DECLARE @sql NVARCHAR(MAX), @has_where BIT
    SELECT @has_where = 0, @sql = 'SELECT * FROM persons '

    IF @FirstName IS NOT NULL
        SELECT @sql = @sql + 'WHERE FirstName = ''' + @FirstName + '''', @has_where = 1
    IF @LastName IS NOT NULL
        SELECT @sql = @sql + CASE WHEN @has_where = 0 THEN 'WHERE ' ELSE 'AND ' END + 'LastName = ''' + @LastName + '''', @has_where = 1
    IF @Age IS NOT NULL
        SELECT @sql = @sql + CASE WHEN @has_where = 0 THEN 'WHERE ' ELSE 'AND ' END + 'Age = ' + CAST(@Age AS VARCHAR), @has_where = 1

    EXEC sp_executesql @sql
END
于 2012-03-28T09:27:10.667 回答