我遇到了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为空”(或者是吗?)或者是坐在键盘前的真正问题?!
答案 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;