2014-01-28 176 views
1

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

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

查询类似于以下内容:

存储过程PARAMS值得一提: @id INT = NULL @userId为nvarchar(255)= NULL 有一个if语句在存储过程的开始禁止两个参数都是空的。

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

在@userId为空的情况下,我希望优化器检测到第一个where子句始终为true;如果@userId不为空,我期望它使用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为null”,或者是真正的问题坐在他们的键盘前面?

+1

可能是因为(a)你的表很小(行数很少),或者(b)因为你使用'SELECT *',索引无法“覆盖”该查询,所以很多密钥查找将是必要的,并且最终这将比仅执行全表扫描更昂贵。 –

+0

表中有1130万条记录,并且实际查询通过名称而不是*调用字段。 – Jon

+1

请发布查询计划的快照。你有表格定义和样本数据吗?几条记录。 –

回答

1

可能有很多原因导致你的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; 
+0

我不喜欢动态SQL作为解决方案,但是你的关于计划创建的意见就是我所期待的。谢谢。 我们现在决定使用if语句和单独的查询。 – Jon