Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 不使用索引的查询优化_Sql Server_Sql Server 2008 R2_Query Optimization - Fatal编程技术网

Sql server 不使用索引的查询优化

Sql 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

我在SQLServer2008R2上遇到了性能问题,我已经把它缩小到查询优化器(我想!)。我正在寻找一个明确的答案“为什么会发生这种情况,或者它是一个bug?”

为了便于讨论,我将使用此示例,但在具有相同场景的多个存储过程中发现了相同的问题。 我们有一个包含付款方式的表格;关键字段是PaymentMethodId和UserId。 PaymentMethodId是一个int,PK;UserId是具有非聚集索引的nvarchar(255)

查询类似于以下内容:

值得一提的存储过程参数: @id int=null @userId nvarchar(255)=空 存储过程的开头有一个if语句,禁止两个参数都为null

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语句和单独的查询。