参数导致 SQL Server 中的表扫描

Posted

技术标签:

【中文标题】参数导致 SQL Server 中的表扫描【英文标题】:Parameter cause a table scan in SQL Server 【发布时间】:2016-03-22 05:31:35 【问题描述】:

我有两个表a,b,主键是它们的索引。

要求:

如果@filter为空,则选择a、b的所有记录,否则将@filter按任意特定分隔符拆分,查找b.PKey在过滤器中的记录。

当前实施:

declare @filter nvarchar(max)= ''

SELECT * 
FROM a
JOIN b ON a.PKey = b.aPKey
       AND (@filter = '' OR b.PKey IN (SELECT item FROM splitFunction(@filter))

我发现最后一条语句and (@filter = '' or b.PKey in (select item from splitFunction(@filter))总是会导致对表b的表扫描,只有当我删除@filter=''时,它才会变为索引查找。

有什么方法可以实现我的要求并且不损害性能?

【问题讨论】:

请查看***.com/questions/31500/… @AbdulRasheed,谢谢,它有帮助,但似乎不是同一个问题。我的问题是连接条件@filter='' 导致表扫描。如果我删除它,将使用索引。 相关:Dynamic Search Conditions 首先我建议您的AND 部分不是连接条件,而是WHERE 条件。从功能上讲,将其置于 ON 或 WHERE 之间没有任何区别,但由于表达式未评估两个表之间的匹配,因此对我来说这是一个 where 条件。如果它是外连接会有所不同,但由于它是内连接,所以不会 您的实际问题可能是缓存的查询计划不正确或参数嗅探。如果您按照此处使用OPTIMIZE FOR UNKNOWN,性能是否会提高:blogs.msdn.microsoft.com/sqlprogrammability/2008/11/26/… 【参考方案1】:

对于常量,优化器可以根据给定值的统计数据制定最佳计划。

当您使用变量时,您是在强制参数化,并且该计划将被设计为可重复用于各种值。所以优化器使用扫描而不是搜索。为了克服这个问题,请停止在查询中使用局部变量并将其用作存储过程中的参数

create procedure p1
@filter = ''
as
begin
SELECT * 
FROM a
JOIN b ON a.PKey = b.aPKey
       AND (@filter = '' OR b.PKey IN (SELECT item FROM splitFunction(@filter))
option (recompile)
end

这将为您提供正确的计划,就像在存储过程中我已将您的局部变量转换为参数一样。

【讨论】:

是的,它有效!但是我仍然对此表示怀疑,如果这是由优化器引起的,为什么该语句不起作用? OPTION(OPTIMIZE FOR (@filter='')) 选项优化不起作用,因为@filter 是一个局部变量,并且在制定执行计划时优化器不知道它的值并进行粗略估计。这就是您的选项优化不起作用的原因。如果您在存储过程中使用相同的内容,它将起作用 我实际上是在存储过程中这样做的,它没有工作:(。一旦更改为option(recompile),它就会再次工作。 Now Optimize for 意味着优化器将创建带有选项 filter ='' 的计划,这意味着它必须返回所有值。即使 filter '' 仍然优化器会为 filter = '' 制定计划。所以你总是会得到扫描。当行数是静态的并且不会改变时,优化是很有帮助的。在您的情况下,作为输出的行数取决于过滤器的值。当 filter = '' 时扫描并在 filter '' 时搜索。有关更多信息,请参阅链接 - msdn.microsoft.com/en-us/library/ms181714%28SQL.100%29.aspx【参考方案2】:

专门针对这种情况,你也可以试试这个:

DECLARE @filter nvarchar(max)= ''

IF @filter = ''
BEGIN
    SELECT * 
    FROM a
    JOIN b ON a.PKey = b.aPKey
END
ELSE 
BEGIN
    SELECT * 
    FROM a
    JOIN b ON a.PKey = b.aPKey
    WHERE b.PKey IN (SELECT item FROM splitFunction(@filter))
END

【讨论】:

是的,这行得通。但这里我的问题是一个可以解释清楚的例子。如果我们有多个表和多个过滤器。这种方式似乎行不通。 是的,如果您有多个表,那么这不会产生最佳解决方案。这就是我专门针对这个案例提到的原因。

以上是关于参数导致 SQL Server 中的表扫描的主要内容,如果未能解决你的问题,请参考以下文章

更新 SQL Server 2005 中的表列

SQL Server - 如何根据将插入数据半小时的表中的几个参数来计算持续时间?

SQL中的表扫描和索引扫描

登录到 SQL Server 触发器中的表

SQL SERVER 性能优化

Sql Server之旅——第二站 理解万恶的表扫描