2013-04-21 106 views
10

首先,似乎沒有辦法使用全文搜索來獲得完全匹配。這似乎是一個高度討論的問題,當使用全文搜索方法,並有很多不同的解決方案來實現預期的結果,但大多數似乎非常低效。由於我的數據庫的容量,我不得不使用全文搜索,所以我最近必須實施其中的一種解決方案才能獲得更準確的結果。SQL Server全文搜索與回退的完全匹配

由於工作原理,我無法使用全文搜索的排名結果。例如,如果您搜索了名爲Toy Story的電影,並且還有一個名爲The Story Behind Toy Story的電影,它會出現而不是完全匹配,因爲它找到了Story兩次字詞和Toy

我會跟蹤自己的排名,我稱之爲「人氣」,每次用戶訪問記錄時數量都會增加。我使用這個數據點來衡量我的結果,以幫助確定用戶可能在尋找什麼。

我也有問題,有時需要退回到LIKE搜索並且不返回完全匹配。即搜索Goonies應該返回The Goonies(最流行的結果)

因此,這裏是實現這個我目前的存儲過程的例子:

DECLARE @Title varchar(255) 
SET @Title = '"Toy Story"' 
--need to remove quotes from parameter for LIKE search 
DECLARE @Title2 varchar(255) 
SET @Title2 = REPLACE(@title, '"', '') 

--get top 100 results using full-text search and sort them by popularity 
SELECT TOP(100) id, title, popularity As Weight into #TempTable FROM movies WHERE CONTAINS(title, @Title) ORDER BY [Weight] DESC 

--check if exact match can be found 
IF EXISTS(select * from #TempTable where Title = @title2) 
--return exact match 
SELECT TOP(1) * from #TempTable where Title = @title2 
ELSE 
--no exact match found, try using like with wildcards 
SELECT TOP(1) * from #TempTable where Title like '%' + @title2 + '%' 
DROP TABLE #TEMPTABLE 

此存儲過程執行分鐘約5000次,足夠它的瘋狂不要把我的服務器放到膝蓋上。但我真的想知道是否有更有效的方法呢?謝謝。

回答

3

你應該使用全文搜索CONTAINSTABLE來找到前100名(可能是200名)候選結果,然後根據自己的標準對結果進行排序。

這聽起來像你想ORDER BY

  1. 短語的精確匹配(=
  2. 完全匹配的短語(LIKE
  3. 更高價值,爲Popularity
  4. Rank來自CONTAINSTABLE

但是你可以玩你喜歡的確切順序。

在SQL看起來像:

DECLARE @title varchar(255) 
SET @title = '"Toy Story"' 
--need to remove quotes from parameter for LIKE search 
DECLARE @title2 varchar(255) 
SET @title2 = REPLACE(@title, '"', '') 

SELECT 
    m.ID, 
    m.title, 
    m.Popularity, 
    k.Rank 
FROM Movies m 
INNER JOIN CONTAINSTABLE(Movies, title, @title, 100) as [k] 
    ON m.ID = k.[Key] 
ORDER BY 
    CASE WHEN m.title = @title2 THEN 0 ELSE 1 END, 
    CASE WHEN m.title LIKE @title2 THEN 0 ELSE 1 END, 
    m.popularity desc, 
    k.rank 

SQLFiddle

+0

您能否解釋ORDER BY CASE部分,因爲它以某種方式工作,但它應該在某些情況下拋出一個「ORDER BY位置編號0超出了選擇列表中項目數量的範圍」,這讓我非常困惑。謝謝 – bfritz 2014-10-07 17:27:33

+1

@bfritz ORDER BY表達式不會引用結果的索引。但結果按表達式值的順序列出。因此,對於標題完全匹配的行,第一個表達式('CASE WHEN m.title = @ title2 THEN 0 ELSE 1 END')的計算結果爲0.此行在所有其他標題不是其標題的行之前列出完全匹配。對於這些行,第一個表達式的計算結果爲1. – flup 2014-10-07 19:33:30

+0

感謝您的迴應,我將試着優化我所有沉重的存儲過程(每天有超過2千5百萬個請求)如果你的內部連接方法將使用比我選擇的100更少的資源到2個選擇運行的#temp表中。然而,你的方法確實看起來更好看。 – bfritz 2014-10-07 19:49:01

2

這將爲您提供包含精確短語「玩具總動員」的影片,按其受歡迎程度排序。

SELECT 
    m.[ID], 
    m.[Popularity], 
    k.[Rank] 
FROM [dbo].[Movies] m 
INNER JOIN CONTAINSTABLE([dbo].[Movies], [Title], N'"Toy Story"') as [k] 
    ON m.[ID] = k.[Key] 
ORDER BY m.[Popularity] 

請注意,如果您搜索「The Goonies」,上述內容也會給你「The Goonies Return」。

+0

嘿Ĵ羅,這種方法是行不通的。我需要的東西會默認爲完全匹配,或者如果找不到,請使用最接近的結果。使用CONTAINSTABLE方法有上述相同的問題,例如,如果您搜索「玩具總動員2」,它會返回「玩具總動員」作爲最高成績。這也可能是全文的限制,而不是索引數字或單個字符。 – bfritz 2013-05-17 08:30:53

+0

我最近禁用了所有的Stoplist/Noise Words,所以也許全文排名會更準確?我會重新測試並讓你知道。 – bfritz 2014-02-12 20:08:45

0

如果感覺你並不真的喜歡全文搜索的模糊部分,但你喜歡性能部分。

也許這是一條路徑:如果你堅持在加權匹配之前獲得精確匹配,你可以嘗試哈希值。例如'玩具總動員' - >帶小寫 - >玩具故事 - >散列成4de2gs5sa(使用任何你喜歡的散列)並在散列上執行搜索。

+0

我必須使用全文搜索從那裏抓取前100條結果(有超過950k條記錄和3-5k條請求),我可以使用較少優化的方法來搜索那些100.將標題轉換爲HashBytes並執行精確搜索似乎沒有帶走我當前流程中的任何步驟? – bfritz 2014-02-12 20:06:00

+1

從這一點很難說,你需要一個前100名的結果 - 除了性能問題 - 你沒有全文搜索就逃脫了嗎?如果是這樣......散列可能是一條好路。 950K記錄和每秒100個請求應該可以使用適當的索引(以及每秒更多的請求)。儘量保持索引儘可能小(以使其容易記憶),並強調測試該解決方案。在選擇最終路徑之前,構建一組隨機的查詢並測量整個事物。 – Paul 2014-02-12 20:24:48

0

在Oracle中,爲了類似的目的,我使用了UTL_MATCH。 (http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/u_match.htm

即使使用Jaro Winkler算法,例如,如果您比較表1和表2中的標題列可能需要一段時間,但如果部分加入2個表,則可以提高性能。在某些情況下,我使用Jaro Winkler將表1中的人名與表2中的人名進行了比較,但不僅限於某個Jaro Winkler閾值以上的有限結果,還包括兩個表中第一個字母相同的名稱。例如,我會比較阿爾伯特與亞丁,阿方索和阿爾貝託,使用加羅溫克勒,但不是阿爾伯特和弗蘭克(限制算法需要使用的情況的數量)。

Jaro Winkler可能實際上也適用於電影片名。儘管您使用的是SQL Server(不能使用utl_match包),但它看起來像是一個名爲「SimMetrics」的免費庫,其中包含Jaro Winkler算法以及其他字符串比較度量標準。你可以在這裏找到詳細的說明和指示:http://anastasiosyal.com/POST/2009/01/11/18.ASPX?#simmetrics