Technical Article

Delimited String Parser

,

Delimited String Parser

Many times there is a need from the business logic layer to pass a list of items into stored procedure . As T-SQL has no concept of an array, the delimited strings are way to go. Most likely the required argument will be an "array" of integers (IDs), but it can be other data, or multidimensional array.

This function parses up to 9-dimensions delimited string data and generates a temporary table with the inidividual records.

The returned table always had 9 columns (named Field1, Field2, ... FIeld9), even if only 1 is used (the rest is populated with NULLs, therefore should not add much of the memory overhead).

Some example usages for example:

DECLARE @delimitedData VARCHAR(MAX)
SET @delimitedData =
'1023|Count of attempts|20
1045|Possible hacking|12
2010|Abandoned sessions|3231
2021|Hacked URLs|211'
CREATE TABLE #Array (ID INT PRIMARY KEY, [Description] VARCHAR(255), [Value] INT)
INSERT INTO #Array (ID, [Description], [Value])
SELECT Field1, Field2, Field3 FROM dbo.ufn_ParseDelimitedString(@delimitedData, '|', char(13)+char(10))
SELECT * FROM #Array
DROP TABLE #Array

OUTPUT from this is:
ID Description Value
==============================================
1023 Count of attempts 20
1045 Possible hacking 12
2010 Abandoned sessions 3231
2021 Hacked URLs 211


The function also handles missing (NULL) values:
DECLARE @delimitedData VARCHAR(MAX)
SET @delimitedData =
'1023|Count of attempts|
1045||12
2010|Abandoned sessions|3231
2021|Hacked URLs|211'

CREATE TABLE #Array (ID INT PRIMARY KEY, [Description] VARCHAR(255), [Value] INT)
INSERT INTO #Array (ID, [Description], [Value])
SELECT Field1, Field2, Field3 FROM dbo.ufn_ParseDelimitedString(@delimitedData, '|', char(13)+char(10))
SELECT * FROM #Array
DROP TABLE #Array

OUTPUT IS:
ID Description Value
==============================================
1023 Count of attempts NULL
1045 NULL 12
2010 Abandoned sessions 3231
2021 Hacked URLs 211
-- ============================================================
--
-- Author:Jano Petras
-- Create date: 09-Nov-2008
-- Description:Parses a delimited string and produces a table
--
-- ============================================================
CREATE FUNCTION [dbo].[ufn_ParseDelimitedString] 
(
@delimitedData VARCHAR(MAX),
@fieldDelimiter VARCHAR(5),
@recordDelimiter VARCHAR(5)
)
RETURNS @T TABLE(
RecNo INT IDENTITY(1,1),
Field1 VARCHAR(MAX), 
Field2 VARCHAR(MAX), 
Field3 VARCHAR(MAX), 
Field4 VARCHAR(MAX), 
Field5 VARCHAR(MAX), 
Field6 VARCHAR(MAX), 
Field7 VARCHAR(MAX),
Field8 VARCHAR(MAX),
Field9 VARCHAR(MAX))
AS
BEGIN
-- Placeholder variables for fields
DECLARE @F1 VARCHAR(MAX)
DECLARE @F2 VARCHAR(MAX)
DECLARE @F3 VARCHAR(MAX)
DECLARE @F4 VARCHAR(MAX)
DECLARE @F5 VARCHAR(MAX)
DECLARE @F6 VARCHAR(MAX)
DECLARE @F7 VARCHAR(MAX)
DECLARE @F8 VARCHAR(MAX)
DECLARE @F9 VARCHAR(MAX)

-- loop controlling variables
DECLARE @currentFieldValue VARCHAR(MAX) -- used to store value of a single field
DECLARE @recordStartIndex INT -- the index where current record starts
DECLARE @recordDelimiterIndex INT -- the index where record delimiter begins
DECLARE @currentFieldIndex INT -- the index where current field starts
DECLARE @currentFieldNumber INT -- the current field number (1 to 9)
DECLARE @nextFieldDelimiter INT -- the index of the first field delimiter found after the current field start index

-- initial positions
SET @recordStartIndex = 1
SET @recordDelimiterIndex = CHARINDEX(@recordDelimiter, @delimitedData, @recordStartIndex)
-- this would be true if only 1 record
IF @recordDelimiterIndex = 0 
SET @recordDelimiterIndex = LEN(@delimitedData)+1
SET @currentFieldIndex = @recordStartIndex

-- master controlling loop
WHILE (@recordStartIndex <= LEN(@delimitedData) AND @recordDelimiterIndex>0)
BEGIN
-- reset current record variables
SET @F1 = NULL
SET @F2 = NULL
SET @F3 = NULL
SET @F4 = NULL
SET @F5 = NULL
SET @F6 = NULL
SET @F7 = NULL
SET @F8 = NULL
SET @F9 = NULL

-- find the next field delimiter
SET @nextFieldDelimiter = CHARINDEX(@fieldDelimiter, @delimitedData, @currentFieldIndex)
-- if field delimiter was not found (a pipe for example), try to match the record delimiter (one and only field)
IF @nextFieldDelimiter = 0
SET @nextFieldDelimiter = CHARINDEX(@recordDelimiter, @delimitedData, @currentFieldIndex)
-- did we go behind record boundary or have not found next delimiter?
IF @nextFieldDelimiter > @recordDelimiterIndex OR @nextFieldDelimiter=0
SET @nextFieldDelimiter = CHARINDEX(@recordDelimiter, @delimitedData, @currentFieldIndex) 

-- if not luck, we could be on the last record
IF @nextFieldDelimiter = 0
SET @nextFieldDelimiter = @recordDelimiterIndex

-- set the field number
SET @currentFieldNumber = 1

-- if it was found within the range of current record
WHILE (@currentFieldIndex BETWEEN @recordStartIndex AND @recordDelimiterIndex-1) AND (@nextFieldDelimiter BETWEEN @recordStartIndex AND @recordDelimiterIndex)
BEGIN
-- extract the field value
SET @currentFieldValue = SUBSTRING(@delimitedData, @currentFieldIndex, @nextFieldDelimiter-@currentFieldIndex);
IF LEN(@currentFieldValue)>0
BEGIN
IF @currentFieldNumber=1
SET @F1 = @currentFieldValue
ELSE IF @currentFieldNumber=2
SET @F2 = @currentFieldValue
ELSE IF @currentFieldNumber=3
SET @F3 = @currentFieldValue
ELSE IF @currentFieldNumber=4
SET @F4 = @currentFieldValue
ELSE IF @currentFieldNumber=5
SET @F5 = @currentFieldValue
ELSE IF @currentFieldNumber=6
SET @F6 = @currentFieldValue
ELSE IF @currentFieldNumber=7
SET @F7 = @currentFieldValue
ELSE IF @currentFieldNumber=8
SET @F8 = @currentFieldValue
ELSE IF @currentFieldNumber=9
SET @F9 = @currentFieldValue
END
-- prepare next iteration: iNcrease the field number
SET @currentFieldNumber = @currentFieldNumber + 1

-- find the next field delimiter index
SET @currentFieldIndex = @nextFieldDelimiter+LEN(@fieldDelimiter)
SET @nextFieldDelimiter = CHARINDEX(@fieldDelimiter, @delimitedData, @currentFieldIndex)

-- did we go behind record boundary or have not found next delimiter?
IF @nextFieldDelimiter > @recordDelimiterIndex OR @nextFieldDelimiter=0
SET @nextFieldDelimiter = CHARINDEX(@recordDelimiter, @delimitedData, @currentFieldIndex) 

-- if not luck, we could be on the last record
IF @nextFieldDelimiter = 0
SET @nextFieldDelimiter = @recordDelimiterIndex

END

-- the loop is finished, insert the record
INSERT INTO @T (Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9)
VALUES (@F1, @F2, @F3, @F4, @F5, @F6, @F7, @F8, @F9)

--
-- Prepare for the next record
--
SET @recordStartIndex = @recordDelimiterIndex + LEN(@recordDelimiter)
SET @currentFieldIndex = @recordStartIndex
SET @currentFieldNumber = 1
SET @recordDelimiterIndex = CHARINDEX(@recordDelimiter, @delimitedData, @recordStartIndex)
IF @recordDelimiterIndex = 0 AND @recordStartIndex <= LEN(@delimitedData)
SET @recordDelimiterIndex=LEN(@delimitedData)+1
END

RETURN

END

Rate

2.57 (7)

You rated this post out of 5. Change rating

Share

Share

Rate

2.57 (7)

You rated this post out of 5. Change rating