查询优化不使用索引

时间:2014-01-28 21:21:48

标签: sql-server sql-server-2008-r2 query-optimization

我遇到了Sql Server 2008 R2的性能问题,我已经缩小到查询优化器(我想!)。我正在寻找一个明确的“为什么会发生这种情况,还是一个错误?”。

为了便于讨论,我将使用这个例子,但是在具有相同场景的多个sproc中也看到了同样的问题。 我们有一张包含付款方式的表格;关键字段是PaymentMethodId和UserId。 PaymentMethodId是一个int,而PK; UserId是一个带有非聚集索引的nvarchar(255)。

该查询类似于以下内容:

值得一提的Sproc params: @id int = null @userId nvarchar(255)= null 在sproc的开头有一个if语句,禁止两个参数都为空。

select * from PaymentMethods (nolock) pm
where (@userId is null or @userId = pm.UserId)
  and (@id is null or @id = pm.PaymentMethodId)

在@userId为null的情况下,我希望优化器检测第一个where子句始终为true;如果@userId为非null,我希望它在UserId上使用索引。 我和@id有同样的期望。

我们所看到的是,无论输入值如何,数据库都选择进行全表扫描。 虽然这本身就令人担忧,但它会变得更有趣。

将查询where子句更新为下面的等效项时,它正确地使用了indecies。

select * from PaymentMethods (nolock) pm
where ((@userId is null and pm.UserId is null) OR @userId = pm.UserId)
  and (@id is null or @id = pm.PaymentMethodId) 

发生了什么事?为什么每个记录都考虑“@userId为空”(或者是吗?)或者是坐在键盘前的真正问题?!

1 个答案:

答案 0 :(得分:1)

为什么你的sp很慢,可能有很多原因。例如,存储过程根据第一次运行sp时参数的值创建计划。这意味着即使新值可能返回完全不同的结果集,也可以从另一个计划中受益,您也可以获得相同的计划。您可以尝试使用动态SQL或使用OPTION(RECOMPILE)运行sp,以便优化器可以创建另一个执行计划。这是一个例子:

CREATE STORED PROCEDURE dbo.Test @userid INT, @id INT
AS

DECLARE @sql NVARCHAR(4000)

SET @sql = N'SELECT * 
             FROM PaymentMethods pm
             WHERE 1 = 1'

SET @sql =  @sql + 
            CASE 
                WHEN @userid IS NOT NULL THEN N' AND pm.UserId = @userid '
                ELSE N'' 
            END +
            CASE 
                WHEN @id IS NOT NULL THEN N' AND pm.PaymentMethodId = @id '
                ELSE N'' 
            END 

EXEC sp_executesql @sql, N'@userid INT, @id INT', @userid, @id;