2012-09-18 66 views
5

我有一個函數可以分割一個字符串(爲了清晰起見在末尾粘貼)。 此功能在單獨使用時按預期工作。 例子:包含在「WHERE ... IN」子句中的udf中的奇怪錯誤

SELECT * FROM TableA WHERE TableA.id IN 
(
    SELECT value 
    FROM dbo.mg_fn_Split('2#1','#') 
) 

我得到:

SELECT value 
FROM dbo.mg_fn_Split('2#1','#') 

返回

-- value -- 
-- 2 -- 
-- 1 -- 
----------- 

但在 「其中」 的條款在本例中(更TableA上以後)使用時,如錯誤:「傳遞給LEFT或SUBSTRING函數的長度參數無效。」

表A在此用作示例。使用不同的表(假設他們有id列)有時會返回正確的結果,而在其他表中,我會得到錯誤。

我假設它與執行順序有關,但我仍然無法看到可能「損壞」該功能的東西。

我正在尋找「正在發生什麼」的解釋,而不是「使用此代替」。我知道我可以使用連接來獲得結果。

函數定義:

-- Description: Returns a table containing the results of a string-split operation. 
-- Params: 
--  DelimitedList: The string to split 
--  Delimiter: The delimiter char, defaults to ',' 
-- Columns: 
--  Position - The char index of the item 
--  Value - The actual item 
-- ============================================= 
CREATE Function [dbo].[mg_fn_Split] 
( 
    @DelimitedList nvarchar(max) 
    , @Delimiter nvarchar(2) = ',' 
) 
RETURNS TABLE 
AS 
RETURN 
    (
    With CorrectedList As 
     (
     Select Case When Left(@DelimitedList, Len(@Delimiter)) <> @Delimiter Then @Delimiter Else '' End 
      + @DelimitedList 
      + Case When Right(@DelimitedList, Len(@Delimiter)) <> @Delimiter Then @Delimiter Else '' End 
      As List 
      , Len(@Delimiter) As DelimiterLen 
     ) 
     , Numbers As 
     (
     Select TOP(Coalesce(DataLength(@DelimitedList)/2,0)) Row_Number() Over (Order By c1.object_id) As Value 
     From sys.columns As c1 
      Cross Join sys.columns As c2 
     ) 
    Select CharIndex(@Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position 
     , Substring (
        CL.List 
        , CharIndex(@Delimiter, CL.list, N.Value) + CL.DelimiterLen  
        , CharIndex(@Delimiter, CL.list, N.Value + 1)       
         - (CharIndex(@Delimiter, CL.list, N.Value) + CL.DelimiterLen) 
        ) As Value 
    From CorrectedList As CL 
     Cross Join Numbers As N 
    Where N.Value <= DataLength(CL.List)/2 
     And Substring(CL.List, N.Value, CL.DelimiterLen) = @Delimiter 
    ) 

編輯:我已經建立了一個小提琴表現出這一點: http://sqlfiddle.com/#!3/9f9ff/3

+3

內聯UDF被擴展到查詢中,因此可能會有一些連接操作或過濾器以不符合預期的順序進行評估。 –

+0

不使用連接的原因是什麼?恕我直言,如果可能的話,最好將字符串解析出來。 –

+0

我同意Martin的評論,我相信你的函數的WHERE子句被推到你從TableA中選擇*。如果你在你的函數中註釋掉下面的和子字符串(CL.List,N.Value,CL.DelimiterLen)= @Delimiter,你會得到同樣的問題,因爲你的一個子字符串值爲-1。 –

回答

0

這種情況發生時,你在內部查詢數據變爲如下。

SELECT value FROM dbo.mg_fn_Split('#','#')-------------->你會在這裏得到錯誤。

SELECT value FROM dbo.mg_fn_Split('2#1','#')------------->這裏沒有錯誤。

SELECT value FROM dbo.mg_fn_Split('2','#')-------------------->這裏沒有錯誤。

SELECT value FROM dbo.mg_fn_Split('','#')---------------------->這裏沒有錯誤。

所以基本上當你分裂的數據和分隔符相同時就會發生錯誤。

問題在於這些陳述。

 " Select Case When Left(@DelimitedList, Len(@Delimiter)) <> @Delimiter Then @Delimiter Else '' End 
     + @DelimitedList 
     + Case When Right(@DelimitedList, Len(@Delimiter)) <> @Delimiter Then @Delimiter Else '' End" 

如果你改變這

Select Case When Left(@DelimitedList, Len(@Delimiter)) <> @Delimiter Then @Delimiter Else '1' End 
     + @DelimitedList 
     + Case When Right(@DelimitedList, Len(@Delimiter)) <> @Delimiter Then @Delimiter Else '1' End 

那麼它會迎刃而解。所有你正在做的是增加「1」,而不是'......希望這有助於。

+0

正如我在文章中提到的那樣,函數的參數不會改變。我已經做了一個編輯來添加小提琴,請參閱這個問題的演示。 – pkExec