Sql server 不使用索引的查询优化
我在SQLServer2008R2上遇到了性能问题,我已经把它缩小到查询优化器(我想!)。我正在寻找一个明确的答案“为什么会发生这种情况,或者它是一个bug?” 为了便于讨论,我将使用此示例,但在具有相同场景的多个存储过程中发现了相同的问题。 我们有一个包含付款方式的表格;关键字段是PaymentMethodId和UserId。 PaymentMethodId是一个int,PK;UserId是具有非聚集索引的nvarchar(255) 查询类似于以下内容: 值得一提的存储过程参数: @id int=null @userId nvarchar(255)=空 存储过程的开头有一个if语句,禁止两个参数都为nullSql server 不使用索引的查询优化,sql-server,sql-server-2008-r2,query-optimization,Sql Server,Sql Server 2008 R2,Query Optimization,我在SQLServer2008R2上遇到了性能问题,我已经把它缩小到查询优化器(我想!)。我正在寻找一个明确的答案“为什么会发生这种情况,或者它是一个bug?” 为了便于讨论,我将使用此示例,但在具有相同场景的多个存储过程中发现了相同的问题。 我们有一个包含付款方式的表格;关键字段是PaymentMethodId和UserId。 PaymentMethodId是一个int,PK;UserId是具有非聚集索引的nvarchar(255) 查询类似于以下内容: 值得一提的存储过程参数: @id in
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子句更新为等同于以下内容时,它正确地使用了索引
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”(或者是吗?),或者真正的问题就摆在他们面前 sp运行缓慢的原因可能有很多。例如,存储过程在您第一次运行该sp时会根据参数值创建计划。这意味着您可以获得相同的计划,即使新值可能返回完全不同的结果集,也可以从另一个计划中受益。您可以尝试使用动态SQL或使用
选项(重新编译)
运行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;
可能是因为(a)您的表很小(行数很少),或者(b)因为您使用的是
SELECT*
索引无法“覆盖”查询,因此需要进行大量的键查找,最终这比只进行完整的表扫描要昂贵。表中有1130万条记录,而实际的查询不会按名称而不是*调用字段。请发布查询计划的快照。你有表格定义和样本数据吗。一些记录。好的,所以这个表不是很小-问题仍然是:(A)WHERE
子句是否过滤了“足够多”的行,或者您是否得到了这些行的一半?和(b)您的SELECT
中的所有列都是索引(索引或包含列)的一部分吗?否则,我的评论仍然适用于必须执行太多关键查找请参见。除非您想使用选项(重新编译)
我不喜欢使用动态sql作为解决方案,否则应该避免这种“一网打尽”的查询,但您关于创建计划的评论正是我想要的。非常感谢。现在我们决定使用if语句和单独的查询。