3

我有一个这样的查询:

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. 将此逻辑存储在预编译的存储过程中。查询需要快速,所以我不想求助于每次执行都会强制重新编译的方法(即使编译开销 + 索引查找仍然比索引扫描快)

我意识到通配符的存在或不存在会影响执行计划,但 SQL 似乎并不理解通配符总是被使用,即使你将它连接到值,如查询 #2。

4

2 回答 2

3

SQL Server可以对这些查询使用范围搜索(如果变量包含前导通配符,则范围最终会成为整个索引)。

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

于 2012-09-09T12:22:00.763 回答
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)

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

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

于 2012-09-09T13:25:12.037 回答