【发布时间】:2012-08-01 12:07:15
【问题描述】:
最近关于使用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 秒内完成(每个查询平均 0.3331),平均 0.0228 找到 500 个结果。
%STREET% 在 149.4500 秒内完成(每个查询平均 0.2989),平均在 0.0177 中找到 500 个结果。 (每个结果的时间更快,因为它在相似的时间内找到比其他结果更多的结果。)
reverse(ADDRESS) like reverse('%STREET') 在 134.0115 秒内完成(每个查询平均 0.2680 个),平均在 0.0183 秒内找到 500 个结果。
reverse('TREETS%') 在 167.6960 秒内完成(每个查询平均 0.3354),平均 0.0229 找到 500 个结果。
我们预计此测试将显示 %STREET% 总体上是最慢的,而实际上它是运行最快的,并且返回 500 个结果的平均时间最好。虽然建议的 reverse('%STREET') 总体上运行速度最快,但返回 500 个结果的时间稍慢。
额外的乐趣:一位同事在我们运行测试时在服务器上运行分析器,发现使用双通配符会显着增加 CPU 使用率,而其他测试之间的差异在 1-2% 以内。
是否有任何 SQL 效率专家可以解释为什么在搜索字符串的末尾使用通配符比在开头更好的做法,也许为什么在字符串的开头和结尾使用通配符比搜索更快只是在开头使用通配符?
【问题讨论】:
-
每次测试前你是否清除了缓冲区和缓存?
-
是的,在测试每个查询之前,我们都会重新启动服务器以确保它是一个公平的测试。
-
reverse() 方法将强制进行表扫描,因为必须反转每一行,它通常与前缀通配符 + 预先计算的反向列一起使用
-
即使模式以通配符开头,索引也可以减少 I/O,因为不需要扫描表行。覆盖索引也可以提高性能。
标签: sql sql-server sql-server-2008 query-optimization processing-efficiency