用TSQL求子串在父串中出現的次數

 http://database.ctocio.com.cn/tips/121/8055121.shtml
用TSQL求子串在父串中出現的次數
                                        
作者:王紅波,幹露
摘要
    本文以實例說明網絡上常見的求子字符串在父字符串中出現次數的函數中存在的錯誤以及修改和優化的方法。針對日常工作所需功能,網絡上流傳着很多已有資源,這些資源我們應該借鑑以擴展思路,對於其中的錯誤我們也要仔細檢查並修正。
導言
    由於SQL Server本身沒提供計算一個字符串在另一個字符串重複次數的函數,大家按照自己的思路使用自定義函數實現了該功能,並在網上傳播。我閱讀了同事從網上獲取的該函數的一個版本後,便發現該函數存在一個明顯的邏輯錯誤。爲了進一步確認這個問題,我在Google上搜索相關關鍵字,發現該功能多數的實現思路一致,但大多數都存在這個共同的邏輯錯誤。可見從網絡上獲取的一些資源,可以作爲參考,但要在實際需求中能夠應用,是需要仔細檢查的。
功能實現
常見方法
    以下是網上一種較常見的函數實現:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

--描述:利用循環得到一個字符串在另一個字符串中出現的次數
--
DEMO:
--
    SELECT dbo.f_getcharcount_false('df ththth','tht')
CREATE FUNCTION [dbo].[f_getcharcount_false](
@str varchar(8000),
@chr varchar(8000)
RETURNS INT
AS
BEGIN
DECLARE @re INT,@i INT
SELECT @re=0,@i=charindex(@chr,@str)+1
WHILE @i>1
     
SELECT @re=@re+1
         ,
@str=substring(@str,@i,8000)
         ,
@i=charindex(@chr,@str)+1

RETURN(@re)
END

上面的函數設計思路是循環截斷源字符串,通過計算截斷的次數來獲取子字符串出現的重複數。思路當然是可行的,但是在它截斷的時候,起點是
@i=charindex(@chr,@str)+1,這裏就是錯誤的起因。以我給出的DEMO來講,代碼如下:

    
SELECT dbo.f_getcharcount_false('df ththth','tht')

結果爲2,其實明顯,正確的結果是1。因爲上述函數的截斷內容只有“t”,而後面的“ht”會被繼續使用,導致“t”與後面的“ht”組合,結果計數多了一次。正確的截取位置應該從子串的位置結束處開始,就是從“tht”後面開始。

既然思路正確,那麼我們就修改上述代碼中的錯誤,修改後的代碼如下:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

--描述:利用循環得到一個字符串在另一個字符串中出現的次數
--
DEMO:
--
        SELECT dbo.f_getcharcount_true('df ththth','tht')
CREATE FUNCTION [dbo].[f_getcharcount_true](
@str varchar(8000),
@chr varchar(8000)
RETURNS INT
AS
BEGIN
DECLARE @re INT,@i INT
SELECT @re=0,@i=charindex(@chr,@str)+1
WHILE @i>1
     
SELECT @re=@re+1
         ,
@str=substring(@str,@i-1+len(@chr),8000)
         ,
@i=charindex(@chr,@str)+1
RETURN(@re)
END

當然,截斷字符串很多人喜歡用stuff填充空字符,但網上流傳的該版本,用stuff函數實現截斷操作的,多數也是存在這個截斷位置錯誤的。

優化的方法
上面講的只是在網絡上流傳最廣泛的方法,那麼想要精煉代碼,尋找更便捷和高效的方法也是可行。下面再介紹兩種較便捷的實現方式。
<方法一>

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO


/*
描述:
    利用字符串長度計算字符出現次數
DEMO:
    SELECT dbo.f_get_char_repeat_count('df ththth','tht')
*/

CREATE FUNCTION [dbo].[f_get_char_repeat_count]
(
@str VARCHAR(8000),
@substr VARCHAR(8000)
)
RETURNS INT
BEGIN
    
RETURN (LEN(@str)-LEN(REPLACE(@str,@substr,'')))/LEN(@substr)
END

<方法二>

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

/*
描述:利用長度差值求重複次數
DEMO:
    SELECT fn_get_char_repeat_count_by_add_char('ahahaahde','ah')
*/

CREATE FUNCTION [dbo].[fn_get_char_repeat_count_by_add_char](
@str varchar(8000),--父串
@chr varchar(8000--子串
RETURNS INT
AS
BEGIN
RETURN(len(replace(@str,@chr,@chr+' '))-len(@str))
END

上面的
<方法一>是用空字符替換父字符串中出現的子串,用剩餘字符串的長度與子字符串的長度求商;<方法二>是把爲父字符串中出現子字符串的地方都增加個空格,用替換後的父字符串長度減去原父字符串的長度。

總結
    面對日常的工作需求,針對具體的問題,關鍵在於思路的不同。網絡上豐富的資源是可以借鑑的,但同時要注意辨別這些資源的可用性。網絡資源的作用主要給我們借鑑,擴展我們的思路,不應該直接投入使用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章