Splitting an integer list in TSQL
Often times I have a list of integers I need to pass to the database to get worked on. Such as checkboxes on a web page or some other list. I needed some TSQL that would take a text string and split it by a separator, in this case a comma. The following is the result of that need.
The way I normally use it is in a stored procedure like the one below with several text type arguments. Sometimes I use a variation designed to split a list of strings separated by a special character sequence. Image two lists, one of the ids and one of the data. You parse the first list to get a table of the ids and you parse the second list to get the data and insert/update as appropriate.
1 2 IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[uspSplitIntegerList]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1) 3 DROP PROCEDURE [dbo].[uspSplitIntegerList] 4 GO 5 6 SET QUOTED_IDENTIFIER ON 7 GO 8 SET ANSI_NULLS ON 9 GO 10 11 12 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 13 -- uspSplitIntegerList 14 -- 15 -- Description: 16 -- splits a comma separated list of integers and returns the integer list 17 -- 18 -- Arguments: 19 -- @list_integers - list of integers 20 -- 21 -- Notes: 22 -- 02/22/2006 - WSR : use DATALENGTH instead of LEN throughout because LEN doesn't count trailing blanks 23 -- 24 -- History: 25 -- 02/22/2006 - WSR : revised algorithm to account for items crossing 8000 character boundary 26 -- 27 CREATE PROCEDURE uspSplitIntegerList 28 @list_integers text 29 AS 30 31 SET NOCOUNT ON 32 33 DECLARE @InputLen integer -- input text length 34 DECLARE @TextPos integer -- current position within input text 35 DECLARE @Chunk varchar(8000) -- chunk within input text 36 DECLARE @ChunkPos integer -- current position within chunk 37 DECLARE @DelimPos integer -- position of delimiter 38 DECLARE @ChunkLen integer -- chunk length 39 DECLARE @DelimLen integer -- delimiter length 40 DECLARE @Delimiter varchar(3) -- delimiter 41 DECLARE @ItemBegPos integer -- item starting position in text 42 DECLARE @ItemOrder integer -- item order in list 43 44 -- create table to hold list items 45 -- actually their positions because we may want to scrub this list eliminating bad entries before substring is applied 46 CREATE TABLE #list_items ( item_order integer, item_begpos integer, item_endpos integer ) 47 48 -- process list 49 IF @list_integers IS NOT NULL 50 BEGIN 51 52 -- initialize 53 -- notice that this loop assumes a delimiter length of 1 54 -- if the delimiter is longer we have to deal with stuff like delimiters straddling the chunk boundaries 55 SET @InputLen = DATALENGTH(@list_integers) 56 SET @TextPos = 1 57 SET @Delimiter = ',' 58 SET @DelimLen = DATALENGTH(@Delimiter) 59 SET @ItemBegPos = 1 60 SET @ItemOrder = 1 61 SET @ChunkLen = 1 62 63 -- cycle through input processing chunks 64 WHILE @TextPos <= @InputLen AND @ChunkLen <> 0 65 BEGIN 66 67 -- get current chunk 68 SET @Chunk = SUBSTRING(@list_integers, @TextPos, 8000) 69 70 -- setup initial variable values 71 SET @ChunkPos = 1 72 SET @ChunkLen = DATALENGTH(@Chunk) 73 SET @DelimPos = CHARINDEX(@Delimiter, @Chunk, @ChunkPos) 74 75 -- loop over the chunk, until the last delimiter 76 WHILE @ChunkPos <= @ChunkLen AND @DelimPos <> 0 77 BEGIN 78 79 -- insert position 80 INSERT INTO #list_items (item_order, item_begpos, item_endpos) 81 VALUES (@ItemOrder, @ItemBegPos, (@TextPos + @DelimPos - 1) - 1) 82 83 -- adjust positions 84 SET @ItemOrder = @ItemOrder + 1 85 SET @ItemBegPos = (@TextPos + @DelimPos - 1) + @DelimLen 86 SET @ChunkPos = @DelimPos + @DelimLen 87 88 -- find next delimiter 89 SET @DelimPos = CHARINDEX(@Delimiter, @Chunk, @ChunkPos) 90 91 END 92 93 -- adjust positions 94 SET @TextPos = @TextPos + @ChunkLen 95 96 END 97 98 -- handle last item 99 IF @ItemBegPos <= @InputLen 100 BEGIN 101 102 -- insert position 103 INSERT INTO #list_items (item_order, item_begpos, item_endpos) 104 VALUES (@ItemOrder, @ItemBegPos, @InputLen) 105 106 END 107 108 -- delete the bad items 109 DELETE FROM #list_items 110 WHERE item_endpos < item_begpos 111 112 -- return list items 113 SELECT CAST(SUBSTRING(@list_integers, item_begpos, (item_endpos - item_begpos + 1)) AS integer) AS item_integer, item_order, item_begpos, item_endpos 114 FROM #list_items 115 WHERE ISNUMERIC(SUBSTRING(@list_integers, item_begpos, (item_endpos - item_begpos + 1))) = 1 116 ORDER BY item_order 117 118 END 119 120 DROP TABLE #list_items 121 122 RETURN 123 124 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ 125 126 GO 127 SET QUOTED_IDENTIFIER OFF 128 GO 129 SET ANSI_NULLS ON 130 GO