-- The inline_split_me as presented in the article, originally from Anith Sen
-- and with a modification from Brian W Perrin, where he reduces the number of
-- concatenations with the delimiter to two. Here using fn_nums rather than a
-- table.
CREATE FUNCTION fn_inline_split_me(@param nvarchar(MAX))
RETURNS TABLE
AS
RETURN(SELECT ltrim(rtrim(convert(nvarchar(4000),
                  substring(@param, Number,
                            charindex(N',' COLLATE Slovenian_BIN2,
                                      @param + convert(nvarchar(MAX), N','), Number) -
                            Number)
              ))) AS Value
       FROM   fn_nums(len(@param))
       WHERE  substring(convert(nvarchar(MAX), N',') + @param, Number, 1) = N',' COLLATE Slovenian_BIN2)
go

-- The DISTINCT version CREATE FUNCTION fn_inline_split_me_distinct(@param nvarchar(MAX)) RETURNS TABLE AS RETURN(SELECT DISTINCT ltrim(rtrim(convert(nvarchar(4000), substring(@param, Number, charindex(N',' COLLATE Slovenian_BIN2, @param + convert(nvarchar(MAX), N','), Number) - Number) ))) AS Value FROM fn_nums(len(@param)) WHERE substring(convert(nvarchar(MAX), N',') + @param, Number, 1) = N',' COLLATE Slovenian_BIN2) go
-- This multi-statement function is the same as fn_inline_split_me. CREATE FUNCTION fn_mstmt_split_me(@param nvarchar(MAX)) RETURNS @t TABLE (str nvarchar(4000) NOT NULL) AS BEGIN INSERT @t(str) SELECT ltrim(rtrim( substring(@param, Number, charindex(N',' COLLATE Slovenian_BIN2, @param + convert(nvarchar(MAX), N','), Number) - Number) )) AS Value FROM fn_nums(len(@param)) WHERE substring(convert(nvarchar(MAX), N',') + @param, Number, 1) = N',' COLLATE Slovenian_BIN2 RETURN END go
-- This multi-statement splices the list into chunks and has one output column -- only. This corrersponds to the contender FNNUM$CHUNK. Note that we have -- to put the output from fn_nums into table variable for best performance. CREATE FUNCTION fn_chunk_split_me(@list nvarchar(MAX), @delim nchar(1) = N',') RETURNS @t TABLE (str nvarchar(4000) NOT NULL) AS BEGIN DECLARE @slices TABLE (slice nvarchar(4000) NOT NULL) DECLARE @numbers TABLE (Number int NOT NULL PRIMARY KEY) DECLARE @slice nvarchar(4000), @textpos int, @maxlen int, @stoppos int SELECT @textpos = 1, @maxlen = 4000 - 2 WHILE datalength(@list) / 2 - (@textpos - 1) >= @maxlen BEGIN SELECT @slice = substring(@list, @textpos, @maxlen) SELECT @stoppos = @maxlen - charindex(@delim COLLATE Slovenian_BIN2, reverse(@slice)) INSERT @slices (slice) VALUES (@delim + left(@slice, @stoppos) + @delim) SELECT @textpos = @textpos - 1 + @stoppos + 2 -- On the other side of the comma. END INSERT @slices (slice) VALUES (@delim + substring(@list, @textpos, @maxlen) + @delim) INSERT @numbers(Number) SELECT Number FROM fn_nums(CASE WHEN len(@list) < @maxlen THEN len(@list) ELSE @maxlen END + 2) INSERT @t (str) SELECT ltrim(rtrim(substring(s.slice, N.Number + 1, charindex(@delim COLLATE Slovenian_BIN2, s.slice, N.Number + 1) - N.Number - 1))) AS Value FROM @numbers N JOIN @slices s ON N.Number <= len(s.slice) - 1 AND substring(s.slice, N.Number, 1) = @delim COLLATE Slovenian_BIN2 RETURN END go
-- In this version, we fn_nums() directly with slices. This works well with -- the fn_nums that uses TOP, but is a disaster with the fn_nums that only has -- a WHERE clause. CREATE FUNCTION fn_chunk_split_me2(@list nvarchar(MAX), @delim nchar(1) = N',') RETURNS @t TABLE (str nvarchar(4000) NOT NULL) AS BEGIN DECLARE @slices TABLE (slice nvarchar(4000) NOT NULL) DECLARE @slice nvarchar(4000), @textpos int, @maxlen int, @stoppos int SELECT @textpos = 1, @maxlen = 4000 - 2 WHILE datalength(@list) / 2 - (@textpos - 1) >= @maxlen BEGIN SELECT @slice = substring(@list, @textpos, @maxlen) SELECT @stoppos = @maxlen - charindex(@delim COLLATE Slovenian_BIN2, reverse(@slice)) INSERT @slices (slice) VALUES (@delim + left(@slice, @stoppos) + @delim) SELECT @textpos = @textpos - 1 + @stoppos + 2 -- On the other side of the comma. END INSERT @slices (slice) VALUES (@delim + substring(@list, @textpos, @maxlen) + @delim) INSERT @t (str) SELECT ltrim(rtrim(substring(s.slice, N.Number + 1, charindex(@delim COLLATE Slovenian_BIN2, s.slice, N.Number + 1) - N.Number - 1))) AS Value FROM @slices s CROSS APPLY fn_nums(len(s.slice) - 1) AS N WHERE substring(s.slice, N.Number, 1) = @delim COLLATE Slovenian_BIN2 RETURN END go