Weird error in udf when included in “WHERE…IN” clause

拈花ヽ惹草 提交于 2020-01-03 13:37:56

问题


I have a function that splits up a string (pasted at the end for clarity). This function works as expected when used alone. Example:

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

Returns

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

But when used in a "WHERE IN" clause, as in this example (more on tableA later on):

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

I get the error: "Invalid length parameter passed to the LEFT or SUBSTRING function."

TableA is used here as an example. Using different tables (assuming they have the id column) sometimes returns correct results, while on other tables I get the error.

I'm assuming it has something to do with order of execution, but I still fail to see what could "corrupt" the function.

I'm looking for a "what's happening" explanation, not a "use this instead". I know I can use joins for example to get the results.

The function definition:

-- 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
    )

EDIT: I've set up a fiddle to exhibit this: http://sqlfiddle.com/#!3/9f9ff/3


回答1:


This happens when you data in inner query becomes as follows.

SELECT value FROM dbo.mg_fn_Split('#','#') --------------> You will get error here.

SELECT value FROM dbo.mg_fn_Split('2#1','#') -------------> No error here.

SELECT value FROM dbo.mg_fn_Split('2','#') --------------------> No error here.

SELECT value FROM dbo.mg_fn_Split('','#') ----------------------> No error here.

so basically when the data you are splitting and the delimiter is same the error will happen.

The problem is with these statements.

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

if you change this to

    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

then it will work out fine.. all you are doing is adding '1' instead of ''... hope this helps.



来源:https://stackoverflow.com/questions/12480321/weird-error-in-udf-when-included-in-where-in-clause

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!