SQL LIKE語句多條件貪婪匹配算法


   在CMS開發中,經常會有類似這樣的需求:

         提問——回答模式,最經典的例子就是百度提問。

         提問者提出問題,由其他人回答,其他人可以是用戶,也可以是服務商。

         在這個模式中,如何充分利用歷史數據是最關鍵的技術。很多時候,由於客戶不擅長使用搜索功能,一上來就提問,而這些問題往往早已經有近乎完美的答案,但沒有充分利用。這樣一來,不僅加大了勞動量,又增加了數據冗余。

         如果在提問的時候能充分調動歷史數據,提交問題之前先看看歷史問題能不能解決客戶疑問,解決了,最好不過,解決不了,再提交。百度提問就是采用的這種方案:

 

 

 

         模式固然好,可怎么實現就有些困難了,畢竟這是百度作為搜索引擎的看家本領。

         從上圖可以看出“CSDN網站如何注冊用戶”這句話被拆成了N個詞,然后分開去數據庫中匹配,為什么?因為直接去匹配“CSDN網站如何注冊用戶”這句話,漢語博大精深,稍微變動一下:“如何在CSDN網站注冊用戶”,意思完全一樣,但直接匹配絕對匹配不到!

         因此,我們需要把一句話拆分成詞組,這個在網上有現成的組件,比如“庖丁解牛”等,它們大多數是免費開源的。拆成詞組之后,應該還要有一個關鍵詞篩選詞庫,用這個詞庫確定出有效的詞組,比如上圖中,“CSDN”、“注冊”、“用戶”是有效的,而“網站”顯然沒有匹配,因為它在這句話中沒有實際意義。

         有點跑題了,拆詞、選詞不是本文的重點,但卻是本文的前提。拿到關鍵詞之后,怎么去數據庫中匹配呢?

         大家都知道T-SQL中的LIKE語句,通過類似LIKE “%abc%”這樣的語法,可以進行模糊匹配,但是它僅僅能進行一次模糊匹配。舉個例子:

         假如我們確定了a,b,c三個關鍵詞,要查找的記錄當然是匹配的越多越好,於是可以這樣寫:LIKE “%a%b%c%”,這樣匹配出的就是包含a,b,c三個關鍵詞的記錄,但是如果根本沒有包含這三個關鍵詞的記錄,最多只有包含兩個的,甚至是只包含一個,那么如何寫LIKE語句呢?這樣LIKE “%a%b%”?這樣LIKE “%a%c%”?這樣LIKE “%b%c%”?這樣LIKE “%a%”?這樣LIKE “%b%”?這樣LIKE “%c%”?

         顯然,需要判斷的情況太多,簡單的LIKE語句已經無法滿足需求。需要注意的是,千萬不要試圖選出范范的記錄,返回到程序中去處理,在程序中處理雖然簡單,但是范范的記錄,在一個中型系統中,往往能達到千萬級別,這么大的數據量,從數據庫返回到程序,無疑會給服務器造成相當大的壓力。

         經過探索,本小菜總結了一個比較簡單的方法,暫且稱為“LIKE語句多條件貪婪匹配算法”。

         算法思想:先用LIKE選出每一組符合一個條件的記錄,只選擇表的主鍵。然后把這些記錄合並在一起,通過主鍵分組、統計數量,數量最多的,也就是匹配最多的,最后根據數量降序排序,越靠上的記錄,匹配的越多。選出匹配的多的記錄主鍵字段,再根據主鍵去表中選出內容即可。

         為了方便大家使用,已經把算法封裝成存儲過程(直接把下邊代碼在查詢分析器中執行即可)。

存儲過程(函數)如下:

GO  
CREATE function Get_StrArrayLength  
(  
 @str varchar(1024),  --要分割的字符串  
 @split varchar(10)  --分隔符號  
)  
returns int  
as  
 begin  
  declare @location int  
  declare @start int  
  declare @length int  
  set @str=ltrim(rtrim(@str))  
  set @location=charindex(@split,@str)  
  set @length=1  
   while @location<>0  
     begin  
      set @start=@location+1  
      set @location=charindex(@split,@str,@start)  
      set @length=@length+1  
     end  
   return @length  
 end  
 GO  
 CREATE function Get_StrArrayStrOfIndex  
(  
 @str varchar(1024),  --要分割的字符串  
 @split varchar(10),  --分隔符號  
 @index int --取第幾個元素  
)  
returns varchar(1024)  
as  
begin  
 declare @location int  
 declare @start int  
 declare @next int  
 declare @seed int  
 set @str=ltrim(rtrim(@str))  
 set @start=1  
 set @next=1  
 set @seed=len(@split)  
 set @location=charindex(@split,@str)  
 while @location<>0 and @index>@next  
   begin  
    set @start=@location+@seed  
    set @location=charindex(@split,@str,@start)  
    set @next=@next+1  
   end  
 if @location =0 select @location =len(@str)+1  
   
--這兒存在兩種情況:1、字符串不存在分隔符號 2、字符串中存在分隔符號,跳出while循環后,@location為0,那默認為字符串后邊有一個分隔符號。  
 return substring(@str,@start,@location-@start)  
end  
GO  
CREATE PROCEDURE proc_Common_SuperLike  
    --要查詢的表的主鍵字段名稱  
    @primaryKeyName varchar(999),  
    --要查詢的表名  
    @talbeName varchar(999),  
    --要查詢的表的字段名稱,即內容所在的字段  
    @contentFieldName varchar(999),  
    --查詢記錄的個數(TOP *),匹配的個數越多,排名越靠前  
    @selectNumber varchar(999),  
    --匹配字符分隔標記  
    @splitString varchar(999),  
    --匹配字符組合字符串  
    @words varchar(999)  
      
AS  
    declare @sqlFirst varchar(999)  
    declare @sqlCenter varchar(999)  
    declare @sqlLast varchar(999)  
BEGIN  
    set @sqlCenter=''  
    declare @next int    
    set @next=1  
    while @next<=dbo.Get_StrArrayLength(@words,@splitString)  
    begin  
        --構造sql查詢條件(中間部分)  
        set @sqlCenter = @sqlCenter+'SELECT '+@primaryKeyName+' FROM '+@talbeName+' WHERE '+@contentFieldName+' like ''%'+dbo.Get_StrArrayStrOfIndex(@words,@splitString,@next)+'%'' UNION ALL '  
        set @next=@next+1  
    end  
    --處理sql語句中間部分,去除最后無用語句  
    set @sqlCenter=left(@sqlCenter,(len(@sqlCenter)-10))  
    --構造sql語句開頭部分  
    set @sqlFirst='SELECT TOP '+@selectNumber+' '+@primaryKeyName+',COUNT(*) AS showCout FROM ('  
    --構造sql語句結尾部分  
    set @sqlLast=') AS t_Temp GROUP BY '+@primaryKeyName+' ORDER BY showCout DESC'  
    --拼接出完整sql語句,並執行  
    execute(@sqlFirst+@sqlCenter+@sqlLast)  
END  
View Code

 

 

調用示例:

 

[sql]  view plain copy print ?
 
  1. executeproc_Common_SuperLike 'id','t_test','content','20','|','i|o|c'  

 

 

         id表的主鍵字段名稱。

         t_test表名。

         content匹配內容字段名稱。

         20選出20個記錄(從頂至下匹配度越來越低)。

         |關鍵字的分隔符號。

         i|o|c一共有i,o,c三個關鍵字,通過|分隔。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM