9

最近在使用LIKE和通配符搜索MS SQL数据库的最有效方式上,存在一场争论。我们正在使用%abc%,%abcabc%进行比较。一个人说过,在任期结束时你应该始终使用通配符(abc%)。所以,据他们说,如果我们想找到以“abc”结尾的内容,那么使用`reverse(column)LIKE reverse('%abc')会是最有效的。SQL通配符搜索 - 效率?

我建立了一个测试使用SQL Server 2008(R2)来比较每个以下语句:

select * from CLMASTER where ADDRESS like '%STREET' 
select * from CLMASTER where ADDRESS like '%STREET%' 
select * from CLMASTER where ADDRESS like reverse('TEERTS%') 
select * from CLMASTER where reverse(ADDRESS) like reverse('%STREET') 

CLMASTER持有约500000条记录,大约有7400的地址是结束“街”,以及有关8,500个地址中有“街道”,但不一定在最后。每次测试运行需要2秒钟,除了%STREET%之外,他们都返回了相同数量的行,结果找到了额外的900个结果,因为它找到了结尾有公寓号码的地址。

由于SQL Server测试并没有表现出在执行时间,我用下面的代码,在每个语句切换,快速运行多个测试,我搬进了PHP任何区别:

<?php 

    require_once("config.php"); 
    $connection = odbc_connect($connection_string, $U, $P); 

    for ($i = 0; $i < 500; $i++) { 
    $m_time = explode(" ",microtime()); 
    $m_time = $m_time[0] + $m_time[1]; 

    $starttime = $m_time; 

    $Message=odbc_exec($connection,"select * from CLMASTER where ADDRESS like '%STREET%'"); 
    $Message=odbc_result($Message,1); 

    $m_time = explode(" ",microtime()); 
    $m_time = $m_time[0] + $m_time[1]; 

    $endtime = $m_time; 

    $totaltime[] = ($endtime - $starttime); 

} 

odbc_close($connection); 

echo "<b>Test took and average of:</b> ".round(array_sum($totaltime)/count($totaltime),8)." seconds per run.<br>"; 
echo "<b>Test took a total of:</b> ".round(array_sum($totaltime),8)." seconds to run.<br>"; 

?> 

结果这个测试与SQL Server中的测试结果一样模糊。

%STREET在166.5823秒内完成(每个查询的平均值为.3331),并在0.0228中找到平均值500的结果。

%STREET%在149.4500秒内完成(每个查询平均值为.2989),平均值为0.0177。 (每结果更快的时间,因为它找到比其它的词,在类似的时间。)

reverse(ADDRESS) like reverse('%STREET')在134.0115秒(每个查询0.2680平均)完成,并且平均在0.0183秒发现500个结果。

reverse('TREETS%')在167.6960秒(每个查询的平均值为.3354)中完成,并且在0.0229中找到平均500个结果。

我们预计该测试显示%STREET%将是整体上最慢的,而它实际上是运行速度最快的,并且具有最好的平均返回500次结果的时间。虽然建议的reverse('%STREET')是整体运行速度最快的,但返回500个结果的时间稍慢。

额外的乐趣:当我们运行测试时,一位同事在服务器上运行了分析器,发现使用双通配符会显着增加CPU使用率,而其他测试则相差1-2%。

是否有任何SQL效率专家可以解释为什么在搜索字符串末尾使用通配符会比开始时更好练习,也许为什么在字符串开头和末尾使用通配符搜索更快比刚开始使用通配符还要多?

+0

DID名称您在每次测试之前清除缓冲区和缓存? – 2012-08-03 12:33:30

+0

是的,在每次查询测试之前,我们重新启动服务器以确保它是一个公平测试。 – Jeremy1026 2012-08-03 12:35:16

+1

reverse()方法将强制进行表扫描,因为每行必须颠倒过来,它通常与前缀通配符+预计算的反向列一起使用 – 2012-08-03 12:35:24

回答

16

具有通配符在字符串的结尾处,就像'abc%',将有助于如果该列被索引,因为这将是能够直接寻求与'abc'开始,而忽略其他一切记录。在开始时使用通配符意味着无论索引如何,都必须查看每一行。

好文章here有更多解释。

+2

这意味着,像“abc%”这样的'reverse(col)'这样的事情是一个坏主意。 – 2012-08-03 12:38:14

+0

是的,'REVERSE'或其他任何改变索引列的计算意味着你失去了sargability。 – Bort 2012-08-03 12:41:14

+0

感谢您提供的答复/评论 – Jeremy1026 2012-08-03 15:38:38

1

Microsoft离开结束通配符更有效率,因为它可以(如果存在)使用索引而不是执行扫描。想一想如何搜索可能的工作,如果你不知道什么是它之前,你必须扫描一切,但如果你只是搜索尾端,那么你可以订购行,甚至可能(根据你要找的东西)做一个准二进制搜索。

联接或谓词中的某些运算符倾向于产生资源密集型操作。带有通配符(“%a value%”)的LIKE运算符几乎总是会导致表扫描。由于前面的通配符,这种类型的表扫描是非常昂贵的操作。只有关闭通配符的LIKE操作符可以使用索引,因为索引是B +树的一部分,并且通过从左向右匹配字符串值来遍历索引。

所以,以上报价也解释了为什么有运行两个通配符时是一个巨大的处理器秒杀。只有偶然才能完成,因为有足够的马力来掩盖低效率。当试图确定查询的性能时,您希望查看查询的执行情况而不是服务器的资源,因为这可能会引起误解。如果我拥有足够的马力来服务天气的服务器,并且我正在小到500,000行的表上运行查询,那么结果将会产生误导。

减去微软引用您的答案的事实,在进行性能分析时,考虑深入了解如何阅读执行计划。这是一项投资,并且非常干,但从长远来看这将是值得的。

总而言之,无论谁指出尾随通配符的效率更高,都是正确的。

+0

@ Jeremy1026 - 我已经更新了我的答案,并对服务器性能使用结果进行了更多的说明。 – 2012-08-03 12:46:34

+0

感谢您提供的答案。 – Jeremy1026 2012-08-03 15:38:59

+0

@ Jeremy1026 - 不是问题。 – 2012-08-03 15:45:25

2

只有Like字符串末尾的通配符才会使用索引。

如果您想提高字符串前后通配符的速度,您应该查看使用FTS Contains。还有see this related SO post regarding Contains versus Like

+0

感谢您提供的答案,不幸的是,切换到Contains对我们来说不是一个可行的解决方案,因为我们需要将全文索引列入不少(数百个)表格中,以使其成为可行的解决方案。我们经常搜索特定的子串和其他项目。 – Jeremy1026 2012-08-03 15:40:21

-2

在MS SQL,如果你想拥有那些与“ABC”结尾的名字,那么在哪里有查询像下面(假设表名是student

select * from student where student_name like'%[ABC]' 

所以它会给那些以'A','B','C'结尾的名字。

2)如果u想拥有它开始与名称 'ABC' 指 -

select * from student where student_name like '[ABC]%' 

3)如果u希望有这中间有 'ABC'

select * from student where student_name like '%[ABC]%'