Split delimited string in SQL Server - sql

I have the following slash-delimited example strings and need to split them:
Record---String
1--------ABC
2--------DEF/123
3--------GHI/456/XYZ
The strings will always have 1 - 3 parts; no more, no less.
To split them I have been using this function:
CREATE FUNCTION [dbo].[Split] (
#chunk VARCHAR(4000)
,#delimiter CHAR(1)
,#index INT
)
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #curIndex INT = 0
,#pos INT = 1
,#prevPos INT = 0
,#result VARCHAR(1000)
WHILE #pos > 0
BEGIN
SET #pos = CHARINDEX(#delimiter, #chunk, #prevPos);
IF (#pos > 0)
BEGIN -- Characters between position and previous position
SET #result = SUBSTRING(#chunk, #prevPos, #pos - #prevPos)
END
ELSE
BEGIN -- Last Delim
SET #result = SUBSTRING(#chunk, #prevPos, LEN(#chunk))
END
IF (#index = #curIndex)
BEGIN
RETURN #result
END
SET #prevPos = #pos + 1
SET #curIndex = #curIndex + 1;
END
RETURN '' -- Else Empty
END
To split the strings, I call this function like so:
MyField1 = dbo.Split(MyInputString, '/', 0),
MyField2 = dbo.Split(MyInputString, '/', 1),
MyField3 = dbo.Split(MyInputString, '/', 2)
The expected results would be
Record 1:
MyField1 = ABC
MyField2 = NULL
MyField3 = NULL
Record 2:
MyField1 = DEF
MyField2 = 123
MyField3 = NULL
Record 3:
MyField1 = GHI
MyField2 = 456
MyField3 = XYZ
It is almost doing what I had hoped, except the last character of MyField1 for Record 1 is being truncated resulting in "AB" instead of "ABC". I believe this is because there is no slash delimiter for this one-part string.
Unfortunately, I did not write this function and my SQL skills are a bit weak. What should I change to make this function return proper results when there is no delimiter in the string?

The following fixes your "SPLIT" function. Add the following line just before the WHILE.
SET #chunk = #chunk + '/'

I would move the charindex to before the while:
alter FUNCTION [dbo].[Split] (
#chunk VARCHAR(4000)
,#delimiter CHAR(1)
,#index INT
)
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #curIndex INT = 0
,#pos INT = 1
,#prevPos INT = 0
,#result VARCHAR(1000)
SET #pos = CHARINDEX(#delimiter, #chunk, #prevPos);
if #pos= 0 return #chunk
WHILE #pos > 0
BEGIN
SET #pos = CHARINDEX(#delimiter, #chunk, #prevPos);
IF (#pos > 0)
BEGIN -- Characters between position and previous position
SET #result = SUBSTRING(#chunk, #prevPos, #pos - #prevPos)
END
ELSE
BEGIN -- Last Delim
SET #result = SUBSTRING(#chunk, #prevPos, LEN(#chunk))
END
IF (#index = #curIndex)
BEGIN
RETURN #result
END
SET #prevPos = #pos + 1
SET #curIndex = #curIndex + 1;
SET #pos = CHARINDEX(#delimiter, #chunk, #prevPos);
END
RETURN #chunk -- Else Empty
END

DECLARE #curIndex INT = 0
,#pos INT = 1
,#prevPos INT = 0
,#result VARCHAR(1000)
if CHARINDEX(#delimiter, #chunk, #prevPos)= 0
set #chunk=#chunk+#delimiter
using (#johns correct solution)

Related

SQL Server stored procedure: finding the values of Odd and even

I'm using #subno as Input. And I had to find the odd and even numbers. 16 is not a fix and it can be any other number. My question is how to find the odd and even number of my input?
Lastly, subno includes ( . ) dot at any position. e.g 123456.789123 I need to find the odd and even number of "123456" and the odd and even number of "789123" the dot ( . ) is the separator.
Once you find the odd for the left side, sum them up together. Once you find the even number for left side sum it up as well and then add the total odd to the total even values. That goes the same for the right side eg "789123".
Please help me. this is my 2nd week of trying to find the solution. Once you find all the total values for each side, multiply them together. example "123456" - total value of odd and even * the "789123" total value of odd and even.
It is for the the check digit validation. Validating the subscriber number. after validating through the calculation it should match the calculated reference number to the valid check digit number. It's the business rule. Kind of algorithm
create procedure ProcedureName
(#subno VARCHAR(16), --Input the 16 subscriber number
#result INT OUT,
)
as
begin
IF(LEN(#subno) <> 16)
SET #result = 1 -- INVALID RESULT
ELSE
IF(#subno % 2 = 0)
SET #result = #subno - even numbers
ELSE
SET #result = #subno --odd numbers
end
Please see below my sample work
-- this is the sample
create procedure ProcedureName
(
#subno VARCHAR(20), --Subscriber no
#result INT OUT, --result is invalid for 1, valid for 0
#payamt int
)
as
DECLARE #WA VARCHAR(2)
DECLARE #Weights varchar(9)
DECLARE #I INT
DECLARE #WD INT
DECLARE #WP INT
DECLARE #A INT
DECLARE #B INT
DECLARE #R INT
DECLARE #WR INT
SET #WR = 0
SET #R = 0
SET #A = 0
SET #B = 0
SET #WP = 0
SET #I = 0
BEGIN
IF (LEN(#subNo) = 7) AND (SUBSTRING(#subno,1,1) = '2') OR (SUBSTRING(#subno,1,1) = '9')
BEGIN
SET #result = 0 --VALID
END
ELSE IF(LEN(#subno) = 8) AND (SUBSTRING(#subno,1,1) = '2') OR
(SUBSTRING(#subno,1,1) = '9')
BEGIN
SET #result = 0 --VALID
END
ELSE IF(LEN(#subno) = 9)
BEGIN
SET #WA = SUBSTRING(#subno,1,2)
IF(#WA = '65')
set #result = 1 -- INVALID
else
BEGIN
SET #Weights = '12121212'
SET #WA = SUBSTRING(#subno,9,1)
SET #WD = 0
SET #I = 1
WHILE #I<9
BEGIN
SET #WP = cast(SUBSTRING(#Weights, #I,1)as int) * cast(SUBSTRING(#subno, #I, 1) as int)
IF(#WP > 9)
BEGIN
SET #A = SUBSTRING(CAST(#WP AS VARCHAR),1,1)
SET #B = SUBSTRING(CAST(#WP AS VARCHAR),2,1)
SET #WP = CAST(#A AS INT) + CAST(#B AS INT)
END
SET #WD = #WP + #WD
SET #I = #I + 1
END
SET #R = #WD % 10
IF(#R <> 0)
SET #WR = 10 - #R
ELSE
SET #WR = #R
IF(#WR <> CAST(#WA AS INT))
BEGIN
SET #result = 1 -- INVALID
END
ELSE
BEGIN
SET #result = 0 -- VALID
END
END
END
ELSE IF (LEN(#subno) = 10)
BEGIN
SET #I =1
SET #WD = 0
SET #Weights = '121212121'
SET #WA = SUBSTRING(#subno,10,1)
WHILE(#I < 10)
BEGIN
SET #WP = CAST(SUBSTRING(#Weights, #I, 1)AS INT) * CAST(SUBSTRING(#subno, #I, 1) AS INT)
IF(#WP > 9)
BEGIN
SET #A = SUBSTRING(CAST(#WP AS VARCHAR),1,1)
SET #B = SUBSTRING(CAST(#WP AS VARCHAR),2,1)
SET #WP = CAST(#A AS INT) + CAST(#B AS INT)
END
SET #WD = #WP + #WD
SET #I = #I + 1
END
SET #R = #WD % 10
IF(#R <> 0)
SET #WR = 10 - #R
ELSE
SET #WR = #R
IF (#WR<> #WA)
BEGIN
SET #result = 1 -- INVALID
END
ELSE
BEGIN
SET #result = 0 -- VALID
END
END
ELSE
SET #result = 1 -- INVALID
END
Split the values which u get . Then iterate iver each side and add them. Please see the sample below.
declare #v varchar (16) , #num1 varchar(20) , #num2 varchar(20)
set #v = '1234567.78906656'
select #num1 = substring(#v,0,charindex('.',#v))
select #num2 = substring(#v,charindex('.',#v)+1,len(#v))
--select #num1 = convert(int, substring(#v,0,charindex('.',#v)))
--select #num2 = substring(#v,charindex('.',#v)+1,len(#v))
declare #index int = 1 ,#len INT , #char CHAR
declare #TotalOddL int = 0
declare #TotalEvenL int = 0
DECLARE #FullTotL INT = 0
declare #TotalOddR int = 0
declare #TotalEvenR int = 0
DECLARE #FullTotR INT = 0
DECLARE #TEMP INT
set #len= LEN(#num1)
WHILE #index <= #len
BEGIN
set #char = SUBSTRING(#num1, #index, 1)
SET #TEMP = cast(#char as int)
IF(#TEMP % 2 = 0)
SET #TotalEvenL = #TotalEvenL + #char
else
SET #TotalOddL = #TotalOddL + #char
SET #FullTotL = #TotalEvenL + #TotalOddL
SET #index= #index+ 1
END
Select 'LeftSide total' , #FullTotL
Select 'Left Side odd' , #TotalOddL
Select 'Left Side Even' , #TotalEvenL
SET #index = 1
set #len= LEN(#num2)
WHILE #index <= #len
BEGIN
set #char = SUBSTRING(#num2, #index, 1)
SET #TEMP = cast(#char as int)
IF(#TEMP % 2 = 0)
SET #TotalEvenR= #TotalEvenR + #char
else
SET #TotalOddR = #TotalOddR + #char
SET #FullTotR = #TotalEvenR + #TotalOddR
SET #index= #index+ 1
END
select 'TotalRSide' , #FullTotR
select 'RsideOdd' , #TotalOddR
select 'RSideEven' , #TotalEvenR
select 'Multiplied value' , #FullTotR * #FullTotL
create or replace procedure prc_even_odd(i_number in number, o_result out varchar2)
as
begin
if (mod(i_number,2) = 0) then
o_result := 'EVEN';
else
o_result := 'ODD';
end prc_even;

Check a chunk of text for tilde and ensure they are 60 characters apart. If not place a tilde inbetween

I currently have a function in SQL that places a tilde every 60 characters, but the original text already has tilde's so , basically I want to change that if there is a tilde and the next tilde is under 60 characters away, then skip to the next tilde. If its over 60 characters, then only then place a extra tilde.
My current function looks like;
function [dbo].[AddTilde] (
#string varchar(max),
#count int
)
returns varchar(max)
as
begin
declare #result varchar(max) = ''
declare #token varchar(max) = ''
while DATALENGTH(#string) > 0
begin
select #token = left(#string, #count)
select #string = REPLACE(#string, #token, '')
select #result +=#token +case when DATALENGTH(#string)=0 then '' else '~' end
end
return #result
end
Any help appreciated
Many Thanks
DECLARE #string1 VARCHAR(max),
#string2 VARCHAR(max) = '',
#i1 INT,
#i2 INT
SET #string1 = '12345678901234567890~1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'
WHILE LEN(#string1) > 0
BEGIN
SET #i1 = CHARINDEX('~',#string1)
SET #i2 = #i1 - (ABS(#i1-60) + (#i1-60)) / 2 -- MINIMUM OF ~ LOCATION AND 60
SET #i1 = LEN(#string1)
IF #i2 = 0 SET #i2 = #i1 - (ABS(#i1-60) + (#i1-60)) / 2 -- MINIMUM OF LENGTH OF #string1 LOCATION AND 60
IF #i2 < 60
BEGIN
SET #string2 = #string2 + LEFT(#string1,#i2)
SET #string1 = RIGHT(#string1,#i1-#i2)
END
ELSE
BEGIN
SET #string2 = #string2 + LEFT(#string1,60) + '~'
SET #string1 = RIGHT(#string1,#i1-60)
END
END
Results: 12345678901234567890~123456789012345678901234567890123456789012345678901234567890~1234567890123456789012345678901234567890
This is based on my String Splitting function, which has a very good performance.
This function should be quite efficient, albeit hard to understand (I have added a few commenets in attempt to make it easier).
You can change internal parameters easily e.g. #Delimitor can be multiple characters
Test cases are included at the bottom.
ALTER FUNCTION [dbo].[AddTilde]
(
#String VARCHAR( MAX ),
#Count INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #CurrentPosition BIGINT, #NextDelimiterPosition BIGINT, #NextFixedPosition BIGINT, #NextPosition BIGINT,
#DelimiterLength INT, #Delimiter VARCHAR( 5 ), #Result VARCHAR( MAX )
-- Initialise
SET #Delimiter = '~'
SET #DelimiterLength = LEN( #Delimiter )
SET #Result = ''
-- Ensures we can terminate loop without using an extra IF condition, saves a few = 0 checks
SET #String = #String + #Delimiter
SET #CurrentPosition = 1
-- Check for next Tilde position
SET #NextDelimiterPosition = CHARINDEX( #Delimiter, #String, #CurrentPosition )
-- Initialise fixed increment position
SET #NextFixedPosition = #Count
-- Compare, which one is earlier and use that one. Handle cases where the next token begins with Tilde
SET #NextPosition = CASE WHEN #NextDelimiterPosition - #DelimiterLength > #NextFixedPosition THEN #NextFixedPosition ELSE #NextDelimiterPosition END
WHILE #NextDelimiterPosition > 0
BEGIN
SET #Result = #Result + SUBSTRING( #String, #CurrentPosition, #NextPosition - #CurrentPosition + 1 )
-- Handle cases where the next token begins with Tilde and avoids doubling up Tildes
+ ( CASE WHEN #NextPosition = #NextDelimiterPosition THEN '' ELSE #Delimiter END )
SET #CurrentPosition = #NextPosition + 1
-- Increment fixed position
SET #NextFixedPosition = #CurrentPosition + #Count - 1
-- Check for next Tilde position
SET #NextDelimiterPosition = CHARINDEX( #Delimiter, #String, #CurrentPosition )
SET #NextPosition = CASE WHEN #NextDelimiterPosition - #DelimiterLength > #NextFixedPosition THEN #NextFixedPosition ELSE #NextDelimiterPosition END
END
-- Remove trailing Tilde
SET #Result = SUBSTRING( #Result, 1, LEN( #Result ) - #DelimiterLength )
RETURN #Result
END
/* Test Cases
SELECT dbo.[AddTilde]( 'ab~c~defghijkl~', 3 ) --> 'ab~c~def~ghi~jkl~'
SELECT dbo.[AddTilde]( '~ab~c~defghijkl', 3 ) --> '~ab~c~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'ab~c~~defghijkl', 3 ) --> 'ab~c~~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'abcdefghijkl', 3 ) --> 'abc~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'a', 3 ) --> 'a'
*/

Function to check the input number of words

Need help to create a function that returns TRUE or FALSE. TRUE - if type 1 or 3 words (like '__hello_', '_hello', '_hello my frend' - spaces should be cut), if the condition is not fulfilled FALSE
CREATE FUNCTION dbo.nazvFac(#f nvarchar(30))
RETURNS bit
AS
BEGIN
DECLARE #l int = 1, #s nvarchar(30), #i int = 0, #b bit
WHILE LTRIM(RTRIM(LEN(#f))) >= #l --error here, but I do not know how to fix it
BEGIN
SET #s = SUBSTRING(#f, #l, 1)
IF #s BETWEEN 'А' AND 'я'
SET #l += 1
ELSE IF #s = ' '
BEGIN
SET #l -= 1
SET #s = SUBSTRING(#f, #l, 1)
SET #s = RTRIM(#s)
SET #l += 2
SET #i += 1
END
ELSE
BREAK
END
IF #i = 0 OR #i = 2
SET #b = 'TRUE'
ELSE
SET #b = 'FALSE'
RETURN #b
END
GO
WHILE LTRIM(RTRIM(LEN(#f))) >= #l --error here, but I do not know how to fix it
LEN() returns an int, which you are then passing to a string function: RTRIM().
You want to return TRUE only if there are one or three words? This should do it:
CREATE FUNCTION dbo.nazvFac(#f NVARCHAR(30))
RETURNS BIT
AS
BEGIN
DECLARE #l INT, #b BIT
SET #l = LEN(#f) - LEN(REPLACE(#f, ' ', '')) + 1
IF #l == 1 OR #l == 3
SET #b = 'TRUE'
ELSE
SET #b = 'FALSE'
RETURN #b
END
Also, JC. is right about the len() error.
You should trim the string and then check the length.
CREATE FUNCTION dbo.nazvFac(#f NVARCHAR(30))
RETURNS BIT
AS
BEGIN
DECLARE #l INT = 1, #s NVARCHAR(30), #i INT = 0, #b BIT
WHILE LEN(LTRIM(RTRIM(#f))) >= #l --I think youn need to trim the string and then check length
BEGIN
SET #s = SUBSTRING(#f, #l, 1)
IF #s BETWEEN 'А' AND 'я'
SET #l += 1
ELSE IF #s = ' '
BEGIN
SET #l -= 1
SET #s = SUBSTRING(#f, #l, 1)
SET #s = RTRIM(#s)
SET #l += 2
SET #i += 1
END
ELSE
BREAK
END
IF #i = 0 OR #i = 2
SET #b = 'TRUE'
ELSE
SET #b = 'FALSE'
RETURN #b
END
GO

get specific part of a string, by index?

I have a string like:
#TempCol = sigma_x1,sigma_x2,...,sigma_xd,XX,YY,ZZ
So how could I get a specific part of that string, based on, lets say an index.
so
if index is 0, get sigma_x1
if index is 1, get sigma_x2
if index is 2, get sigma_x3
if index is d-1,get sigma_xd
if index is d, get XX,
if index is d+1,get YY
and so on.
Previously Andriy M solved a similar issue, his code gets a substring based on a nomber but returns a substring the following way:
if #d is 1,get sigma_x1
if #d is 2,get sigma_x1,sigma_x2
if #d is 3,get sigma_x1,sigma_x2,sigma_x3
if #d is 4,get sigma_x1,sigma_x2,sigma_x3,sigma_x4
if #d is d,get sigma_x1,sigma_x2,sigma_x3,sigma_x4,...,sigma_xd (ALL THE STRING)
How to update this procedure to get specific element?
DECLARE #TempCol varchar(max), #d int, #p int, #Result varchar(max);
SET #TempCol = 'item1,item2,itemA,itemB,item#,item$';
SET #d = 3;
SET #p = 1;
WHILE #d > 0 AND #p > 0 BEGIN
SET #p = CHARINDEX(',', #TempCol, #p);
IF #p > 0 SET #p = #p + 1;
SET #d = #d - 1;
END;
IF #p = 0
SET #Result = #TempCol
ELSE
SET #Result = SUBSTRING(#TempCol, 1, #p - 2);
SELECT #Result;
just try this. hope this will meet your needs.
create a function GetIndex, which accepts string and delimiter to split the string
CREATE FUNCTION dbo.GetIndex(#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (itemindex int identity(1,1), items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
and you can query like,
suppose you need 4th index then
select * from dbo.GetIndex(#TempCol,',') where itemindex = 4
to get an item of 4th index then
select items from dbo.GetIndex(#TempCol,',') where itemindex = 4
to get item to a variable
select #Aux = items from dbo.GetIndex(#TempCol,',') where itemindex = 4

SQL Split function that handles string with delimeter appearing between text qualifiers?

There are several SQL split functions, from loop driven, to using xml commands, and even using a numbers table. I haven't found one that supports text qualifiers.
Using the example string below, I would like to split on ",", but not when it appears between double or single quotes.
Example data:
jsmith#anywhere.com, "Sally \"Heat\" Jones" <sally#anywhere.com>, "Mark Jones" <mjones#anywhere.com>, "Stone, Ron" <rstone#anywhere.com>
Should return a table:
jsmith#anywhere.com
"Sally \"Heat\" Jones" <sally#anywhere.com>
"Mark Jones" <mjones#anywhere.com>
"Stone, Ron" <rstone#anywhere.com>
I know this is a complex query/function, but any suggestions or any guidance would be mucho appreciated.
Here is my solution:
CREATE FUNCTION fnSplitString
(
#input nvarchar(MAX)
)
RETURNS #emails TABLE
(
email nvarchar(MAX)
)
AS
BEGIN
DECLARE #len int = LEN(#input)
DECLARE #pos int = 1;
DECLARE #start int = 1;
DECLARE #ignore bit = 0;
WHILE(#pos<=#len)
BEGIN
DECLARE #ch nchar(1) = SUBSTRING(#input, #pos, 1);
IF ( #ch = '"' or #ch = '''')
BEGIN
SET #ignore = 1 - #ignore;
END
IF (#ch = ',' AND #ignore = 0)
BEGIN
INSERT #emails VALUES (SUBSTRING(#input, #start, #pos-#start));
SET #start = #pos+1;
END
SET #pos = #pos + 1;
END
IF (#start<>#pos)
BEGIN
INSERT #emails VALUES (SUBSTRING(#input, #start, #pos-#start));
END
RETURN
END
GO
DECLARE #input nvarchar(max) = 'jsmith#anywhere.com, "Sally \"Heat\" Jones" <sally#anywhere.com>, "Mark Jones" <mjones#anywhere.com>, "Stone, Ron" <rstone#anywhere.com>';
select * from fnSplitString(#input)
CREATE FUNCTION [dbo].[udfSplit]
(
#nvcString nvarchar(max),
#nvcDelimiter nvarchar(1),
#nvcTQ nvarchar(1)
)
RETURNS #tblTokens TABLE (
Token nvarchar(max)
)
AS
BEGIN
DECLARE #intCounter int
DECLARE #nvcToken nvarchar(4000)
DECLARE #nvcCurrentChar nvarchar(1)
DECLARE #intStart int
IF #nvcString <> ''
BEGIN
SET #intCounter = 1
SET #nvcToken = ''
SET #intStart = 0
--Loop through each character of the string
WHILE #intCounter <= LEN(#nvcString)
BEGIN
SET #nvcCurrentChar = SUBSTRING(#nvcString, #intCounter, 1)
--If current char is TQ
IF #nvcCurrentChar = #nvcTQ
BEGIN
--Concatonate to token
SET #nvcToken = #nvcToken + #nvcCurrentChar
--If this is the end TQ
IF #intStart <> 0
BEGIN
--Fix TQ
SET #nvcToken = dbo.udfRemoveTQFromToken(#nvcToken, #nvcTQ)
IF #nvcToken <> ''
BEGIN
INSERT INTO #tblTokens (Token) VALUES (#nvcToken)
SET #nvcToken = ''
END
--Reset TQ
SET #intStart = 0
END
ELSE
BEGIN
SET #nvcToken = dbo.udfRemoveTQFromToken(#nvcToken, #nvcTQ)
IF #nvcToken <> ''
BEGIN
INSERT INTO #tblTokens (Token) VALUES (#nvcToken)
SET #nvcToken = ''
END
--Mark TQ start position
SET #intStart = #intCounter
END
END
ELSE IF #intStart = 0 AND #nvcCurrentChar = #nvcDelimiter
BEGIN
--If not inside TQ, and char is Delimiter
SET #nvcToken = dbo.udfRemoveTQFromToken(#nvcToken, #nvcTQ)
IF #nvcToken <> ''
BEGIN
INSERT INTO #tblTokens (Token) VALUES (#nvcToken)
SET #nvcToken = ''
END
END
ELSE
BEGIN
--Current char is not TQ or Delim, add to current token
SET #nvcToken = #nvcToken + #nvcCurrentChar
END
SET #intCounter = #intCounter + 1
END
END
SET #nvcToken = dbo.udfRemoveTQFromToken(#nvcToken, #nvcTQ)
IF #nvcToken <> ''
BEGIN
--Current Token has not been added to table
INSERT INTO #tblTokens (Token) VALUES (#nvcToken)
END
RETURN
END
GO
CREATE FUNCTION [dbo].[udfRemoveTQFromToken]
(
#nvcToken nvarchar(4000),
#nvcTQ nvarchar(1)
)
RETURNS nvarchar(4000) AS
BEGIN
DECLARE #nvcReturn nvarchar(4000)
--Trim token, needs to be done first,
--as we dont want to trim any spaces within the TQ
--unless it was malformed
SET #nvcReturn = LTRIM(RTRIM(#nvcToken))
--If Left char is TQ
IF LEFT(#nvcReturn, 1) = #nvcTQ
BEGIN
--Though both cases perform the removal of the left most char (opening TQ)
--We need to perform a trim after removal ONLY if it was malformed
IF RIGHT(#nvcReturn, 1) <> #nvcTQ
BEGIN
--But no matching end TQ, malformed
--fix by removing left most char (the opening TQ)
SET #nvcReturn = RIGHT(#nvcReturn, LEN(#nvcReturn) - 1)
--Reapply the LTRIM, incase there were spaces after the opening TQ
SET #nvcReturn = LTRIM(#nvcReturn)
END
ELSE
BEGIN
--has matching end TQ, well-formed
--fix by removing left most char (the opening TQ)
SET #nvcReturn = RIGHT(#nvcReturn, LEN(#nvcReturn) - 1)
END
END
--Remove the right most char (the closing TQ)
IF RIGHT(#nvcReturn, 1) = #nvcTQ
SET #nvcReturn = LEFT(#nvcReturn, LEN(#nvcReturn) - 1)
RETURN #nvcReturn
END
This is a quick solution, and it is less than perfect, it has no stack, so it will treat the comma inside the quotes as the delimiter.
alter function fnSplit
(
#Delim char(1),
#List nvarchar(4000)
)
returns table as
return
with
Strings(PosIdx) as
(
select 1
union all
select PosIdx + 1 from Strings where PosIdx < 4000
)
select
ltrim(rtrim(substring(#List, PosIdx, charindex(#Delim, #List + #Delim, PosIdx) - PosIdx))) as value
from
Strings
where
PosIdx <= convert(int, len(#List))
and substring(#Delim + #List, PosIdx, 1) = #Delim
go
select * from fnSplit(',', 'jsmith#anywhere.com, "Sally \"Heat\" Jones" <sally#anywhere.com>, "Mark Jones" <mjones#anywhere.com>, "Stone, Ron" <rstone#anywhere.com>')
option (maxrecursion 0)