2009-10-14 77 views
9

我有一個117000左右的記錄表。我需要執行搜索,檢查給定的字符串模式的3個單獨的字段。如何優化/重構TSQL「LIKE」子句?

我的where子句如下:

field1 LIKE '%' + @DESC + '%' 
OR field2 LIKE '%' + @DESC + '%' 
OR field3 LIKE '%' + @DESC + '%' 

這似乎需要大約24秒無論輸入的...

有沒有更好的方式來做到這一點?少於10(或5!)秒將更加可取。

感謝您的任何幫助。

回答

14

使用全文檢索和CONTAINS。 LIKE在搜索字段中間時無法優化,即。當LIKE表達式以'%'開頭時,它總是會執行全表掃描。

+0

你可以使用索引,做一點工作,做LIKE'%'+字符串時,請參閱我的其他評論中的鏈接。 – 2009-10-14 21:19:13

+0

@KM:有趣的把戲,一個顛倒的列。與電話簿類似的還不錯,使得案例生動清晰。 – 2009-10-14 21:24:06

+0

不錯的訣竅,對於類似'%text%'問題沒有任何類似的問題。 – HLGEM 2009-10-14 21:38:47

1

無論何時您使用通配符開始LIKE搜索,您都在進行掃描。除非您可以縮小搜索條件以包含第一個字符(這可能不可行),否則您需要使用全文搜索。

+3

@Stuart Ainsworth說_任何時候你開始一個通配符LIKE搜索,你正在做一個scan_,這不必是真實的,看到這個:http://stackoverflow.com/questions/1388059/sql-server-index -columns-used-like-like/1395881#1395881 – 2009-10-14 21:15:49

+0

現在你只是在玩語義;您將列倒置並對其進行索引,然後反轉類似子句,使其不以通配符開頭。你不是用通配符開始搜索,所以你沒有進行掃描。 我會承認這是一個有趣的解決方案,我必須記住它。 – 2009-10-14 23:33:03

0

怎麼樣

field1 + field2 + field3 LIKE '%' + @DESC + '%' 

CONTAINS(field1 + field2 + field3, @DESC) 
1

你真的需要啓動一個通配符?爲什麼?通常你可以強制用戶至少輸入第一個字符。我提出這個問題,因爲有些開發人員只是使用通配符作爲一種習慣,而不是因爲有需求。在大多數情況下,用戶將能夠輸入第一個字符,除非該字段存儲長字符串(如說官方機場名稱)。否則,你真的需要使用全文索引,儘管如果你最後不需要通配符,KM的逆向技巧非常酷。

如果你可以避免做性能上的事情,那就這樣做。

+0

是的,我確實需要這個查詢的雙面通配符。我需要在「草莓香蕉酸奶」中找到類似「香蕉」的東西。 – IronicMuffin 2009-10-15 15:54:35

1

雖然我同意接受的答案是全文索引是最好的解決方案,我絕不主張使用領先的通配符搜索,如果他們必須執行那麼是可以採取的潛在步驟使他們的表現不那麼糟糕。

卡倫·德萊尼在書中「Microsoft SQL Server 2008 Internals」說:

整理可以產生巨大的變化 當SQL Server必須以看上去幾乎 所有字符的字符串。對於 例如,看看下面:

SELECT COUNT(*) FROM tbl WHERE longcol LIKE '%abc%' 

這可以執行速度快10倍以上用二進制排序比非二進制Windows排序規則。與varchar數據一起使用SQL排序規則比使用Windows排序規則執行速度快七到八倍。

+0

排序規則如何影響性能? – crosenblum 2011-02-15 21:05:28

+0

本書更多內容「如果你有一個varchar列,你可以通過強制排序來加速 ,如下所示: SELECT COUNT(*)FROM tbl WHERE longcol COLLATE SQL_Latin1_General_CP_CI_AS LIKE'%abc%'; – 2012-11-29 17:28:51

+0

@yoelhalb你可能的意思是'_CP1_'不是'_CP_' – jazzcat 2017-02-21 11:13:04

0

我嘗試了一種可能的解決方案。在此解決方案甚至查詢之前未返回結果並導致連接超時錯誤。

我的查詢使用日期過濾器和其他標準。所有其他標準就像搜索。一列的關鍵字在ntext列中搜索類似'%abc%',並且正在進行全表掃描。

解決方案:

將查詢分爲兩部分。 1)CTE中的第一部分(Common Table Express)2)在CTE上應用所有搜索條件。

WITH SearchData(Column1,Column2,Column3,Column4,........) 
    AS 
    (
    SELECT Column1,Column2,Column3,Column4,........... 
    FROM myTable1 WITH(NOLOCK) 
      INNER JOIN MyTable2 WITH(NOLOCK) 
       ON MyTable1.id = MyTable2.Id 
    WHERE (MyTable1.CreationTime >= '2014-04-27' AND MyTable1.CreationTime <= '2014-05-01') 
) 

    SELECT DISTINCT top 250 Column1,Column2,Column3,Column4 
    FROM SearchData 
    WHERE (ISNULL(Column1,'') LIKE @Column1 +'%' OR @Column1 IS NULL) 
      and (Column2 LIKE @Column2+ '%' OR @Column2 IS NULL) 
      ... 
      ... 
      ... 
      ... 
      AND (Column10 like '%'[email protected]+'%' or @Column10 IS NULL) 
      AND @[email protected][email protected][email protected] <> '' 
      ORDER BY [CreationTime] DESC 

它爲我工作。

+0

我認爲對於具有LIKE條件的查詢使用'TOP'子句將會提高性能。因此,它是TOP子句,而不是CTE對性能增益的貢獻,因爲使用TOP時,只要掃描了所需的記錄數量,就會縮短全面掃描的時間。 – Sunil 2015-10-18 20:03:27

+1

作爲在每個連接中使用WITH(NOLOCK)的離題註釋非常危險,您必須非常小心,否則您將獲得重複的數據等等......我看到人們將這種暗示作爲優化查詢的標準方式,它的不。你應該總是三思而後行,在nolock提示中拖延之前查看錶格在應用程序中的寫入方式(插入/更新)。 (只是說) – 2015-12-18 01:21:07

0

如果你不能使用FullTextSearch,你可以增加10倍的速度。下一步:

1添加計算領域:

alter table TableName 
add CalculatedColumnName as upper(Column1 + '|' + Column2...) collate Latin1_General_100_Bin2 
persisted; 

2加指數計算領域:

create nonclustered index IDX_TableName_CalculatedColumnName 
on TableName(CalculatedColumnName); 

3更改您的查詢文本

select count(*) 
from TableName 
where CalculatedColumnName like '%' + upper(@ParameterValue) + '%' collate Latin1_General_100_Bin2 

來源:http://aboutsqlserver.com/2015/01/20/optimizing-substring-search-performance-in-sql-server