我正在编写一般搜索存储过程,以便根据用户可以在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
函数(结果相同)。
答案 0 :(得分:5)
包含此@variable is null or @variable = column
构造的查询是性能灾难。这是因为SQL计划已创建,因此它们适用于变量的任何值。有关该主题的冗长讨论,问题和可能的解决方案请参见Dynamic Search Conditions in T-SQL
答案 1 :(得分: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