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]%'