2012-09-18 7 views
5

文字列を分割する機能があります(分かりやすくするために最後に貼り付けています)。 この機能は単独で使用すると期待どおりに機能します。 例:"WHERE ... IN"節に含まれるudfの奇妙なエラー

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

戻り

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

しかし、この例のように "IN" 句、(にtableAの詳細後で)で使用した場合:

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

私が手エラー:「無効な長さパラメータが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 
    ) 

EDIT:私はこれを発揮するフィドルを設定している: http://sqlfiddle.com/#!3/9f9ff/3

+3

インラインUDFがクエリに展開されるため、おそらく一部の結合操作またはフィルタが期待していなかった別の順序で評価されています。 –

+0

結合を使用しない理由は何ですか? IMHO可能であれば、SQLコマンドから文字列解析を離れることをお勧めします。 –

+0

私はMartinのコメントに同意します。あなたのWHERE句があなたのSelect *からTableAにプッシュされていると思います。あなたの関数の中で以下のAnd Substring(CL.List、N.Value、CL.DelimiterLen)= @Delimiterをコメントアウトすると、-1と評価されるSubstring値の1つによって引き起こされる同じ問題が発生します。 –

答えて

0

次のように内側のクエリでは、あなたのデータはなったときにこれが起こります。

SELECT値 FROM dbo.mg_fn_Split( '#'、 '#')-------------->ここでエラーが発生します。

SELECT値 FROM dbo.mg_fn_Split( '2#1'、 '#')------------->エラーはありません。

SELECT値 FROM dbo.mg_fn_Split( '2'、 '#')-------------------->ここにエラーはありません。

SELECT値 FROM dbo.mg_fn_Split( ''、 '#')---------------------->ここにエラーはありません。

基本的に分割するデータが区切り文字と同じ場合、エラーが発生します。

これらのステートメントに問題があります。あなたがやっているすべてではなく「」の「1」を加算している

 " 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 

にこれを変更した場合、それは大丈夫うまくいくが... ...このことができます願っています。

+0

私が記事で触れたように、関数のパラメータは変更されません。私は、フィドルを追加する編集をしました。問題のデモを見てください。 – pkExec