最近在使用LIKE
和通配符搜索MS SQL数据库的最有效方法上进行了辩论。我们正在使用%abc%
,%abc
和abc%
进行比较。有人说你应该在学期结束时(abc%
)使用通配符。因此,根据他们的说法,如果我们想要找到以“abc”结尾的东西,那么使用`reverse(column)LIKE reverse('%abc')是最有效的。
我使用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拥有大约500,000条记录,大约有7,400个地址以“Street”结尾,大约8,500个地址包含“Street”,但不一定在最后。每次测试运行花了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),在.0228中找到平均500个结果。
%STREET%
在149.4500秒内完成(每个查询平均值为.2989),平均在.0177中找到500个结果。 (每个结果的更快时间,因为它在相似的时间内找到比其他结果更多的结果。)
reverse(ADDRESS) like reverse('%STREET')
在134.0115秒内完成(每个查询平均值为.2680),平均在.0183秒内找到500个结果。
reverse('TREETS%')
在167.6960秒内完成(每个查询平均值为.3354),平均在.0229中找到500个结果。
我们预计此测试会显示%STREET%
总体上最慢,而它实际上是最快的,并且有最佳平均时间来返回500个结果。虽然建议的reverse('%STREET')
总体上运行速度最快,但返回500个结果的速度要慢一点。
额外的乐趣:当我们运行测试时,同事在服务器上运行了分析器,发现使用双通配符会显着增加CPU使用率,而其他测试则相互之间的比例在1-2%之内。 / p>
是否有任何SQL效率专家可以解释为什么在搜索字符串末尾使用通配符比开头更好的做法,也许为什么在字符串的开头和结尾用通配符搜索比刚刚开始使用通配符?
答案 0 :(得分:19)
在字符串末尾加上通配符,如'abc%'
,将帮助,如果该列被编入索引,因为它可以直接查找以{开头的{ {1}}并忽略其他一切。在开头使用外卡意味着它必须查看每一行,无论索引如何。
好文章here有更多解释。
答案 1 :(得分:2)
只有Like
字符串末尾的通配符才会使用索引。
如果要提高字符串正面和背面的通配符速度,应该使用FTS Contains
。还see this related SO post regarding Contains versus Like。
答案 2 :(得分:1)
从Microsoft开始,离开结束通配符会更有效,因为它可以(如果存在)使用索引而不是执行扫描。想想搜索可能如何工作,如果你不知道它之前是什么,那么你必须扫描所有内容,但是如果你只搜索尾部那么你可以订购行甚至可能(取决于你要找的东西) )进行准二分搜索。
连接或谓词中的某些运算符往往会产生资源密集型操作。带有通配符(“%a value%”)的值的LIKE运算符几乎总是会导致表扫描。由于前面的通配符,这种类型的表扫描是非常昂贵的操作。只有关闭通配符的LIKE运算符可以使用索引,因为索引是B +树的一部分,并且通过从左到右匹配字符串值来遍历索引。
所以,上面的引用也解释了为什么在运行两个通配符时出现了巨大的处理器峰值。它仅通过偶然事件更快地完成,因为有足够的马力来掩盖低效率。在尝试确定查询的性能时,您希望查看查询的执行而不是服务器的资源,因为这些可能会产生误导。如果我的服务器具有足够的马力来满足天气的需求,并且我在小到500,000行的桌面上运行查询,那么结果将会产生误导。
在进行性能分析时,微软引用您的答案时,请考虑更深入地学习如何阅读执行计划。这是一项投资, 非常 干,但从长远来看,这是值得的。
简而言之,无论谁表明尾随通配符只是效率更高,都是正确的。
答案 3 :(得分:-4)
在MS SQL中,如果您想要以'ABC'结尾的名称,那么您可以使用如下所示的查询(假设表名为student
)
select * from student where student_name like'%[ABC]'
所以它会给那些以'A','B','C'结尾的名字。
2)如果你想要以'ABC'开头的名字意味着 -
select * from student where student_name like '[ABC]%'
3)如果你想让名字在中间有'ABC'
select * from student where student_name like '%[ABC]%'