1. 程式人生 > >SQL Server 全文索引的硬傷(轉載)

SQL Server 全文索引的硬傷(轉載)

本文關鍵字:SQL Server全文索引、CONTAINS、FREETEXT、CONTAINSTABLE、FREETEXTTABLE等謂詞。
 
想象這樣一個場景:在DataBase_name.dbo.Table_name中有一個名為Title(標題)和Contents(內容)的欄位,現在需要查詢在Title或者Contents中包括“qq”字元的所有記錄。
 
面對這樣的一個場景,我們通常都會寫這樣一個指令碼:SELECT * FROM DataBase_name.dbo.Table_name WHERE Title LIKE '%qq%' OR Contents LIKE '%qq%'; 沒錯,這也是我第一個想到的方法。
但是我們需要思考的是:隨著時間的推移,資料會越來越大,那個時候我們該如何提高我們的效能?客戶隨時都有可能要求加入對Remark(備註)欄位的查詢,難道我們就應該不厭其煩地修改程式程式碼?
 
面對上面的質問,我們需要提醒你的是:①對於這樣的查詢條件,即使Title和Contents上都有索引,我們也無法使用到索引,因為在 '%qq%'的“qq”前面使用了萬用字元,所以無法使用到索引;如果查詢的條件是'qq%',那倒是可以利用上索引。②在許多資料庫效能調優的文章上都說OR這個謂詞可以使用SELECT UNION ALL SELECT這樣的方式來提高效能,但是需要提醒大家的是:如果在一條記錄中欄位Title和Contents都同時存在“中國”字元的話,那麼返回的結果就會出現兩條相同的記錄,如果你希望是唯一的記錄,那麼這個時候你就要注意了。

 

現在回到我們上面提出的疑問上,大概這個時候大家都應該想到了資料庫的全文索引了。全文索引是一種特殊型別的基於標記的功能性索引,由 Microsoft SQL Server 全文引擎 (MSFTESQL) 服務建立和維護。建立全文索引的過程與建立其他型別的索引的過程差別很大。MSFTESQL 不是基於某一特定行中儲存的值來構造 B 樹結構,而是基於要索引的文字中的各個標記來建立倒排、堆積且壓縮的索引結構。

 

講了那麼久,硬傷在哪裡呢?可能大家都懷疑我是不是標題黨了,呵呵,馬上就講到,那就是這個全文索引能解決我們一開始提到的場景嗎?回答是否定。為什麼呢?因為SQL Server對字串“tqq.tencent.com”進行分詞和倒排索引後,我們是無法通過查詢條件‘“*qq*”’來返回上面那條字串的記錄的,這樣的查詢條件只能查詢到類似“qqt.tencent.com”、“www.qq.com”這樣的字串。SQL Server的分詞應該是正向最大值的分詞方法,它沒有把字串進行反方向再進行一次分詞和索引,所以只能查詢到詞或短語的字首符合的記錄。這一點有可能會被大家所忽略掉。

 

就針對上面的說法,我們來進行測試一下:

--已經對錶Test_FullText_Index的uri,uri_path建立了全文索引.
--下面的查詢是為了說明CONTAINS與LIKE的區別.
SELECT ID,uri,uri_path 
FROM Test_FullText_Index
where uri LIKE '%qq%' 
AND ID NOT IN(SELECT ID FROM Test_FullText_Index WHERE CONTAINS(uri,'"qq*"'))

--下圖為執行結果

如何大家有什麼好的解決方案可以解決這樣的Like查詢的話,可以拿出來大家探討一下。 


 
主題的內容講完了,下面附帶講一些建立全文索引的步驟和注意事項,懂的童鞋(同學)可以跳過。

 

全文索引需要注意:
  1. 表中必須有一個唯一性索引,當並不需要是主鍵。
  2. 一個表中只能有一個全文索引。
  3. 你需要告訴你的指令碼你想使用全文索引,如何告訴呢?那就是使用關鍵字:CONTAINS、FULLTEXT、CONTAINSTABLE、FREETEXTTABLE。例如:SELECT * FROM table_name WHERE CONTAINS(fullText_column,'"search contents*"');需要記住CONTAINS等在不同場景、需求下的用法。
  4. 如果定義了變數作為傳入值,那麼就要注意是否需要在set字元的時候的前面加入N標識。
  5. 要對錶設定全文索引,那就得先對資料庫設定了全文索引,這樣點選表右鍵的時候,“全文索引”選項才能用。
  6. 指令碼在查詢的時候是不區分大小寫的。解決辦法:SELECT * FROM Table_name WHERE Column_name='A' COLLATE Chinese_PRC_CS_AI;或者SELECT * FROM Table_name WHERE ASCII(Column_name) = ASCII('A');
  7. Microsoft SQL Server 全文引擎 (MSFTESQL) 不是基於某一特定行中儲存的值來構造 B 樹結構,而是基於要索引的文字中的各個標記來建立倒排、堆積且壓縮的索引結構。
  8. 全文索引並不一定能達到like這個謂詞的效果,如LIKE '%qq%'。這正是本篇文章想要說明的。
  9. 如果資料庫是在移動碟符上,好像就無法設定:資料庫-屬性-檔案-“使用全文索引”了,這個時候chckbox是不可用的。(這個大家可以求證一下)
  10. 關於搜尋結果的排序問題,全文索引並沒有這個功能,也就是匹配度排序或者說是相似度排序。
  11. Lucene中有一個Similarity類,Lucene Practical Scoring Function就包含了得分的計算公式,tf、idf。
  CONTAINS的幾種用法
CONTAINS 謂詞可以搜尋:
  • 詞或短語。
  • 詞或短語的字首。
  • 與另一個詞相鄰的詞。
  • 由另一個詞的詞形變化而生成的詞(例如,drive 一詞是 drives、drove、driving 和 driven 詞形變化的詞幹)。
  • 使用同義詞庫確定的另一個詞的同義詞(例如,metal 一詞可能有 aluminum 和 steel 等同義詞)。

 

原文連結