SQL没有优化我的参数化查询

时间:2012-09-09 12:04:16

标签: sql-server sql-server-2008

我有这样的查询:

select top(10) * from dbo.myTable where DisplayName like 'farm%'

这会导致对DisplayName进行索引搜索,因为通配符正在跟踪。但是当我这样做时情况并非如此:

declare @val varchar(200) = 'farm'
select top(10) * from dbo.myTable where DisplayName like @val + '%'

或者这个:

declare @val varchar(200) = 'farm%'
select top(10) * from dbo.myTable where DisplayName like @val

在这些情况下,SQL会依赖于速度慢得多的索引扫描操作。找到的值是一个在运行时提供的参数,所以显然我不能只为我的目的使用第一个查询。

我有什么办法可以:

  1. 确保SQL索引寻找值,
  2. 在预编译的存储过程中使用此逻辑。查询需要很快,所以我不想采用一种方法来强制每次执行重新编译(即使编译开销+索引搜索仍然比索引扫描更快)
  3. 我意识到通配符的存在与否会影响执行计划,但SQL似乎并不理解通配符总是被使用,即使你将它连接到值,就像在查询#2中一样。

2 个答案:

答案 0 :(得分:3)

SQL Server can use a range seek for these queries(如果变量包含一个前导通配符,则该范围最终会成为整个索引。)

如果在这种情况下不选择这样做,很可能是因为使用变量意味着它不能准确估计选择性。您可以尝试添加OPTION (RECOMPILE)提示,以便考虑实际的变量值。

答案 1 :(得分:1)

我认为Martin是正确的,因为当我用主键和DisplayName列替换select *时,SQL实际上做了索引搜索,从而消除了键查找。

此外,LIKE'farm%'正在实现为DisplayName> ='farm'AND DisplayName< 'farN',所以我决定完全放弃LIKE算子。

select top(10) * from dbo.EPFSuppliers with(forceseek) 
where DisplayName >= @str and DisplayName < left(@str,len(@str)-1) + char(ascii(right(@str,1)) + 1)

forceseek提示是必要的,因为SQL估计在729匹配的行数,这在我的情况下是完全不切实际的数字。

我相信这种方法优于使用通配符,通配​​符需要预处理字符串以删除转义字符。