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

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'
*/

Related

How to fix all caps to normal case

I have a sql table that contains an employee's full name in all caps (i.e. SMITH-EASTMAN,JIM M).
I need to be able to separate the full name into two separate columns (Last Name and First Name). This part is going well. Now I could use some assistance with removing the capital letters and putting it in normal case.
How can I take the results of my common table expression and pass them into a function?
WITH CTE AS
(
SELECT FullName = [Employee Name],
LastName = SUBSTRING([Employee Name], 1, CHARINDEX(',',[Employee Name])-1),
FirstNameStartPos = CHARINDEX(',',[Employee Name]) + 1,
MidlleInitialOrFirstNameStartPos = CHARINDEX(' ',[Employee Name]),
MiddleInitialOrSecondFirstName = SUBSTRING([Employee Name], CHARINDEX(' ',[Employee Name]),LEN([Employee Name])),
MiddleInitialOrSecondFirstNameLen = LEN(SUBSTRING([Employee Name], CHARINDEX(' ',[Employee Name]),LEN([Employee Name]))) - 1
FROM ['Med-PS PCN Mapping$']
WHERE [PS Employee ID] IS NOT NULL
),
CTE2 AS
(
SELECT FullName = CTE.FullName,
DerivedFirstName = CASE
WHEN CTE.MiddleInitialOrSecondFirstNameLen = 1
THEN SUBSTRING(CTE.FullName, CTE.FirstNameStartPos, CTE.MidlleInitialOrFirstNameStartPos - CTE.FirstNameStartPos)
ELSE SUBSTRING(CTE.FullName, CTE.FirstNameStartPos, CTE.FirstNameStartPos + CTE.MiddleInitialOrSecondFirstNameLen)
END,
DerivedLastName = CTE.LastName
FROM CTE
)
SELECT *
FROM CTE2
RESULTS
FullName DerivedFirstName DerivedLastName
SMITH-EASTMAN,JIM M JIM SMITH-EASTMAN
O'DAY,MARTIN C MARTIN O'DAY
TROUT,MADISON MARIE MADISON MARI TROUT
CREATE FUNCTION [dbo].[FixCap] ( #InputString varchar(4000) )
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE #Index INT
DECLARE #Char CHAR(1)
DECLARE #PrevChar CHAR(1)
DECLARE #OutputString VARCHAR(255)
SET #OutputString = LOWER(#InputString)
SET #Index = 1
WHILE #Index <= LEN(#InputString)
BEGIN
SET #Char = SUBSTRING(#InputString, #Index, 1)
SET #PrevChar = CASE WHEN #Index = 1 THEN ' '
ELSE SUBSTRING(#InputString, #Index - 1, 1)
END
IF #PrevChar IN (' ', ';', ':', '!', '?', ',', '.', '_', '-', '/', '&', '''', '(')
BEGIN
IF #PrevChar != '''' OR UPPER(#Char) != 'S'
SET #OutputString = STUFF(#OutputString, #Index, 1, UPPER(#Char))
END
SET #Index = #Index + 1
END
RETURN #OutputString
END
GO
select [dbo].[FixCap] (pass in DerivedFirstName from CTE2);
select [dbo].[FixCap] (pass in DerivedLastName from CTE2);
Do you want something like INITCAP ?
CREATE FUNCTION dbo.F_INITCAP (#PHRASE NVARCHAR(max))
RETURNS NVARCHAR(max)
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
IF LEN(#PHRASE) < 1 RETURN #PHRASE;
DECLARE #I INT = 1, #C CHAR(1), #P BIT = 0, #OUT VARCHAR(max) = '';
WHILE #I <= LEN(#PHRASE)
BEGIN
SET #C = SUBSTRING(#PHRASE, #I, 1);
IF #C BETWEEN 'A' AND 'Z' COLLATE Latin1_General_CI_AI
BEGIN
IF #P = 0
SET #OUT = #OUT + UPPER(#C);
ELSE
SET #OUT = #OUT + LOWER(#C);
SET #P = 1
END
ELSE
BEGIN
SET #P = 0;
SET #OUT = #OUT + LOWER(#C);
END
SET #I = #I + 1;
END
RETURN #OUT;
END
GO

MSG 537 :Invalid length parameter passed to the LEFT or SUBSTRING function

when i create a function in sql server 2012 , in this function
CREATE FUNCTION howords (#str nvarchar(50), #word nvarchar(50))
RETURNS int
AS
BEGIN
DECLARE #tempstr nvarchar(max)
DECLARE #space int
DECLARE #count int
DECLARE #size int
SET #count = 0
WHILE (LEN(#str) >= 0)
BEGIN
SET #space = CHARINDEX(' ', #str, 1)
SET #tempstr = LEFT(#str, (#space - 1))
IF (#tempstr = #word)
SET #count = #count + 1
SET #size = LEN(#tempstr)
IF (((LEN(#str)) - #space) > 0)
SET #str = SUBSTRING(#str, #space + 1, ((LEN(#str)) - #space))
IF (((LEN(#str)) - #space) <= 0)
BEGIN
IF (#str = #word)
SET #count = 0
WHILE (LEN(#str) > 0)
BEGIN
SET #space = CHARINDEX(' ', #str + 1)
SET #tempstr = LEFT(#str, (#space - 1))
IF (#tempstr = #word)
SET #count = #count + 1
SET #size = LEN(#tempstr)
IF (((LEN(#str)) - #space) > 0)
SET #str = SUBSTRING(#str, #space + 1, ((LEN(#str)) - #space))
IF (((LEN(#str)) - #space) <= 0)
BEGIN
IF (#str = #word)
SET #count = #count + 1
BREAK
END
END
END
END
RETURN #count
END
when i exec this query
select dbo.howords ('hello my hello','hello' )
i want to give me count (2)
but it give me an error
MSG 537 :Invalid length parameter passed to the LEFT or SUBSTRING function.
any help?
Try this
DECLARE #tosearch VARCHAR(MAX)='Hello'
DECLARE #string VARCHAR(MAX)='hello my hello'
SELECT (DATALENGTH(#string)-DATALENGTH(REPLACE(#string,#tosearch,'')))/DATALENGTH(#tosearch)
AS OccurrenceCount

Split delimited string in SQL Server

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)

MSSQL - Create table function, return substring

I need to create this table function. The function needs to return single words from passed parameters like: hello, hhuu, value
The table function should return:
hello,
hhuu,
value
But I am always getting some errors, please could you help me?
you can write as:
DECLARE #input_char VARCHAR(255)
SET #input_char = 'hello, hhuu, value'
;WITH cte AS (
SELECT
CAST('<r>' + REPLACE(#input_char, ' ', '</r><r>') + '</r>' AS XML) AS input_char
)
SELECT
rtrim( LTRIM (xTable.xColumn.value('.', 'VARCHAR(MAX)')) ) AS input_char
FROM cte
CROSS APPLY input_char.nodes('//r') AS xTable(xColumn)
Please give a look at this article:
http://www.codeproject.com/Tips/625872/Convert-a-CSV-delimited-string-to-table-column-in
and you could use ' ' (space) as delimiter.
SELECT * FROM dbo.CSVtoTable('hello, hhuu, value', ' ')
I have used the following function many times. It is a bit lengthy forgive me, but it has become a great tool for me.
CREATE Function [dbo].[ParseText2Table]
(
#p_SourceText varchar(MAX)
,#p_Delimeter varchar(100) = ',' --default to comma delimited.
)
RETURNS #retTable TABLE
(
POSITION INT
,Int_Value bigint
,Num_value REAL--Numeric(18,3)
,txt_value varchar(MAX)
)
AS
BEGIN
DECLARE #tmpTable TABLE
(
Position2 INT IDENTITY(1,1) PRIMARY KEY
,Int_Value bigint
,Num_value REAL--Numeric(18,3)
,txt_value varchar(MAX)
)
DECLARE #w_Continue INT
,#w_StartPos INT
,#w_Length INT
,#w_Delimeter_pos INT
,#w_tmp_int bigint
,#w_tmp_num REAL--numeric(18,3)
,#w_tmp_txt varchar(MAX)
,#w_Delimeter_Len INT
IF len(#p_SourceText) = 0
BEGIN
SET #w_Continue = 0 -- force early exit
END
ELSE
BEGIN
-- if delimiter is ' ' change
IF #p_Delimeter = ' '
BEGIN
SET #p_SourceText = replace(#p_SourceText,' ','ÿ')
SET #p_Delimeter = 'ÿ'
END
-- parse the original #p_SourceText array into a temp table
SET #w_Continue = 1
SET #w_StartPos = 1
SET #p_SourceText = RTRIM( LTRIM( #p_SourceText))
SET #w_Length = DATALENGTH( RTRIM( LTRIM( #p_SourceText)))
SET #w_Delimeter_Len = len(#p_Delimeter)
END
WHILE #w_Continue = 1
BEGIN
SET #w_Delimeter_pos = CHARINDEX( #p_Delimeter
,(SUBSTRING( #p_SourceText, #w_StartPos
,((#w_Length - #w_StartPos) + #w_Delimeter_Len)))
)
IF #w_Delimeter_pos > 0 -- delimeter(s) found, get the value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,(#w_Delimeter_pos - 1)) ))
IF dbo.isReallyNumeric(#w_tmp_txt) = 1 --and not #w_tmp_txt in('.', '-', '+', '^')
BEGIN
--set #w_tmp_int = cast( cast(#w_tmp_txt as real) as bigint)--numeric) as bigint)
SET #w_tmp_int = CASE WHEN (CAST(#w_tmp_txt AS REAL) BETWEEN -9223372036854775808 AND 9223372036854775808) THEN CAST( CAST(#w_tmp_txt AS REAL) AS bigint) ELSE NULL END
SET #w_tmp_num = CAST( #w_tmp_txt AS REAL)--numeric(18,3))
END
ELSE
BEGIN
SET #w_tmp_int = NULL
SET #w_tmp_num = NULL
END
SET #w_StartPos = #w_Delimeter_pos + #w_StartPos + (#w_Delimeter_Len- 1)
END
ELSE -- No more delimeters, get last value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,((#w_Length - #w_StartPos) + #w_Delimeter_Len)) ))
IF dbo.isReallyNumeric(#w_tmp_txt) = 1 --and not #w_tmp_txt in('.', '-', '+', '^')
BEGIN
--set #w_tmp_int = cast( cast(#w_tmp_txt as real) as bigint)--as numeric) as bigint)
SET #w_tmp_int = CASE WHEN (CAST(#w_tmp_txt AS REAL) BETWEEN -9223372036854775808 AND 9223372036854775808) THEN CAST( CAST(#w_tmp_txt AS REAL) AS bigint) ELSE NULL end
SET #w_tmp_num = CAST( #w_tmp_txt AS REAL)--numeric(18,3))
END
ELSE
BEGIN
SET #w_tmp_int = NULL
SET #w_tmp_num = NULL
END
SELECT #w_Continue = 0
END
INSERT INTO #tmpTable VALUES( #w_tmp_int, #w_tmp_num, #w_tmp_txt )
END
INSERT INTO #retTable SELECT Position2, Int_Value ,Num_value ,txt_value FROM #tmpTable
RETURN
END
Here are the supporting functions for above as well:
CREATE FUNCTION dbo.isReallyInteger
(
#num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(#num, 1) = '-'
SET #num = SUBSTRING(#num, 2, LEN(#num))
RETURN CASE
WHEN PATINDEX('%[^0-9-]%', #num) = 0
AND CHARINDEX('-', #num) <= 1
AND #num NOT IN ('.', '-', '+', '^')
AND LEN(#num)>0
AND #num NOT LIKE '%-%'
THEN
1
ELSE
0
END
END
CREATE FUNCTION dbo.isReallyNumeric
(
#num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(#num, 1) = '-'
SET #num = SUBSTRING(#num, 2, LEN(#num))
DECLARE #pos TINYINT
SET #pos = 1 + LEN(#num) - CHARINDEX('.', REVERSE(#num))
RETURN CASE
WHEN PATINDEX('%[^0-9.-]%', #num) = 0
AND #num NOT IN ('.', '-', '+', '^')
AND LEN(#num)>0
AND #num NOT LIKE '%-%'
AND
(
((#pos = LEN(#num)+1)
OR #pos = CHARINDEX('.', #num))
)
THEN
1
ELSE
0
END
END
Usage Examples:
--Single Character Delimiter
--select * from dbo.ParseText2Table('100|120|130.56|Yes|Cobalt|Blue','|')
--select txt_value from dbo.ParseText2Table('100 120 130.56 Yes Cobalt Blue',' ') where Position = 3
--select * from dbo.ParseText2Table('100,120,130.56,Yes,Cobalt,Blue,,',',')
/*
POSITION Int_Value Num_value txt_value
----------- ----------- -------------------- --------------
1 100 100.000 100
2 120 120.000 120
3 131 130.560 130.56
4 NULL NULL Yes
5 NULL NULL Cobalt Blue
*/

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)