Converting String List into Int List in SQL - sql

I have a nvarchar(MAX) in my stored procedure which contains the list of int values, I did it like this as it is not possible to pass int list to my stored procedure,
but, now I am getting problem as my datatype is int and I want to compare the list of string.
Is there a way around by which I can do the same?
---myquerry----where status in (#statuslist)
but the statuslist contains now string values not int, so how to convert them into INT?
UPDate:
USE [Database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP]
(
#FromDate datetime = 0,
#ToDate datetime = 0,
#ID int=0,
#List nvarchar(MAX) //This is the List which has string ids//
)
AS
SET FMTONLY OFF;
DECLARE #sql nvarchar(MAX),
#paramlist nvarchar(MAX)
SET #sql = 'SELECT ------ and Code in(#xList)
and -------------'
SELECT #paramlist = '#xFromDate datetime,#xToDate datetime,#xId int,#xList nvarchar(MAX)'
EXEC sp_executesql #sql, #paramlist,
#xFromDate = #FromDate ,#xToDate=#ToDate,#xId=#ID,#xList=#List
PRINT #sql
So when I implement that function that splits then I am not able to specify the charcter or delimiter as it is not accepting it as (#List,',').
or (','+#List+',').

It is possible to send an int list to your stored procedure using XML parameters. This way you don't have to tackle this problem anymore and it is a better and more clean solution.
have a look at this question:
Passing an array of parameters to a stored procedure
or check this code project:
http://www.codeproject.com/Articles/20847/Passing-Arrays-in-SQL-Parameters-using-XML-Data-Ty
However if you insist on doing it your way you could use this function:
CREATE FUNCTION [dbo].[fnStringList2Table]
(
#List varchar(MAX)
)
RETURNS
#ParsedList table
(
item int
)
AS
BEGIN
DECLARE #item varchar(800), #Pos int
SET #List = LTRIM(RTRIM(#List))+ ','
SET #Pos = CHARINDEX(',', #List, 1)
WHILE #Pos > 0
BEGIN
SET #item = LTRIM(RTRIM(LEFT(#List, #Pos - 1)))
IF #item <> ''
BEGIN
INSERT INTO #ParsedList (item)
VALUES (CAST(#item AS int))
END
SET #List = RIGHT(#List, LEN(#List) - #Pos)
SET #Pos = CHARINDEX(',', #List, 1)
END
RETURN
END
Call it like this:
SELECT *
FROM Table
WHERE status IN (SELECT * from fnStringList2Table(#statuslist))

You can work with string list too. I always do.
declare #statuslist nvarchar(max)
set #statuslist = '1, 2, 3, 4'
declare #sql nvarchar(max)
set #sql = 'select * from table where Status in (' + #statuslist + ')'
Execute(#sql)

You can do this by using sql function which will return you an integer array..
It would be great if you pass #Delimiter separated string to your stored procedure which could be processed properly afterwards.
Write one function to split the data as following
CREATE FUNCTION [dbo].[SplitValues] (#StringArray NVARCHAR(MAX), #Delimiter NVARCHAR(10))
RETURNS #ResultedValues table
(
ResultValue INT
)
AS
BEGIN
DECLARE #Tokens TABLE(Token nvarchar)
DECLARE #String nvarchar
WHILE (CHARINDEX(#Delimiter,#StringArray)>0)
BEGIN
INSERT INTO #Tokens (Token) VALUES (LTRIM(RTRIM(SUBSTRING(#StringArray,1,CHARINDEX(#Delimiter,#StringArray)-1))))
SET #String = SUBSTRING(#StringArray,
CHARINDEX(#Delimiter,#StringArray)+LEN(#Delimiter),LEN(#StringArray))
END
INSERT INTO #ResultedValues (ResultValue ) VALUES ( CAST(LTRIM(RTRIM(#String)) AS INT))
RETURN
END
And then use it like following, i am using (,) as #Delimiter here
SELECT ResultValue [YourSchema].[SplitValues](#statuslist,',')

Actually, you can send the list of int values to your procedure by creating a User Defined Table Type. However, this implies more work in order to populate the table parameter.
In your case, you can use the sp_executesql stored procedure to achieve what you want like this:
declare #statement nvarchar(4000) = '----your query---- where status in ('
+ #statusList +')'
sp_executesql #statement

here is an example of how to do it and the Link for more informations
ALTER FUNCTION iter_intlist_to_tbl (#list nvarchar(MAX))
RETURNS #tbl TABLE (listpos int IDENTITY(1, 1) NOT NULL,
number int NOT NULL) AS
BEGIN
DECLARE #startpos int,
#endpos int,
#textpos int,
#chunklen smallint,
#str nvarchar(4000),
#tmpstr nvarchar(4000),
#leftover nvarchar(4000)
SET #textpos = 1
SET #leftover = ''
WHILE #textpos <= datalength(#list) / 2
BEGIN
SET #chunklen = 4000 - datalength(#leftover) / 2
SET #tmpstr = ltrim(#leftover + substring(#list, #textpos, #chunklen))
SET #textpos = #textpos + #chunklen
SET #startpos = 0
SET #endpos = charindex(' ' COLLATE Slovenian_BIN2, #tmpstr)
WHILE #endpos > 0
BEGIN
SET #str = substring(#tmpstr, #startpos + 1, #endpos - #startpos - 1)
IF #str <> ''
INSERT #tbl (number) VALUES(convert(int, #str))
SET #startpos = #endpos
SET #endpos = charindex(' ' COLLATE Slovenian_BIN2, #tmpstr, #startpos + 1)
END
SET #leftover = right(#tmpstr, datalength(#tmpstr) / 2 - #startpos)
END
IF ltrim(rtrim(#leftover)) <> ''
INSERT #tbl (number) VALUES(convert(int, #leftover))
RETURN
END
-- ############################ Example ############################
--CREATE PROCEDURE get_product_names_iter #ids varchar(50) AS
--SELECT P.ProductName, P.ProductID
--FROM Northwind..Products P
--JOIN iter_intlist_to_tbl(#ids) i ON P.ProductID = i.number
--go
--EXEC get_product_names_iter '9 12 27 37'
-- ############################ WICHTIG ############################

This works for me on an Informix DataBase:
DROP FUNCTION rrhh:fnc_StringList_To_Table;
CREATE FUNCTION rrhh:fnc_StringList_To_Table (pStringList varchar(250))
RETURNING INT as NUMERO;
/* A esta Funcion le podes pasar una cadena CSV con una lista de numeros
* Ejem: EXECUTE FUNCTION fnc_StringList_To_Table('1,2,3,4');
* y te devolvera una Tabla con dichos numeros separados uno x fila
* Autor: Jhollman Chacon #Cutcsa - 2019 */
DEFINE _STRING VARCHAR(255);
DEFINE _LEN INT;
DEFINE _POS INT;
DEFINE _START INT;
DEFINE _CHAR VARCHAR(1);
DEFINE _VAL INT;
LET _STRING = REPLACE(pStringList, ' ', '');
LET _START = 0;
LET _POS = 0;
LET _LEN = LENGTH(_STRING);
FOR _POS = _START TO _LEN
LET _CHAR = SUBSTRING(pStringList FROM _POS FOR 1);
IF _CHAR <> ',' THEN
LET _VAL = _CHAR::INT;
ELSE
LET _VAL = NULL;
END IF;
IF _VAL IS NOT NULL THEN
RETURN _VAL WITH RESUME;
END IF;
END FOR;
END FUNCTION;
EXECUTE FUNCTION fnc_StringList_To_Table('1,2,3,4');
SELECT * FROM TABLE (fnc_StringList_To_Table('1,2,3,4'));

Related

Dynamically update column value with replacement for some pattern

I have my table with Column MailText which has values like
1. <strong>abc</strong>:<description1> <strong>bcd</strong>:<description2>
2. <strong>efg</strong>:<description3> <strong>hgl</strong>:<description7>
Upon update I want values like
1. <strong>abc</strong>:<abc> <strong>bcd</strong>:<bcd>
2. <strong>efg</strong>:<efg> <strong>hgl</strong>:<hgl>
Please help with dynamic replacement that it would update all string within <strong> tag to <description>. <strong> tag may contain any values.
CREATE FUNCTION GetString
(
#s NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #trav NVARCHAR(2000) = #s,
#length INT,
#count INT = 1,
#startIndex INT = 0,
#endIndex INT = 0,
#replaceStartIndex INT = 0,
#repalceEndIndex INT = 0,
#replaceword NVARCHAR(2000),
#newWord NVARCHAR(2000)
SELECT #length = LEN(#Trav)
WHILE ((#count + #startIndex) <= #length)
BEGIN
SET #startIndex = CHARINDEX('<strong>', #trav, #startIndex) + LEN('<strong>')
IF (#startIndex > 8)
BEGIN
SET #endIndex = CHARINDEX('</strong>', #trav, #startIndex)
SET #newWord = SUBSTRING(#trav, #startIndex, (#endIndex - #startIndex))
SET #replaceStartIndex = CHARINDEX(':', #trav, #startIndex) + 2
SET #repalceEndIndex = CHARINDEX('>', #trav, #replaceStartIndex)
SET #replaceword = SUBSTRING(
#trav,
#replaceStartIndex,
(#repalceEndIndex - #replaceStartIndex)
)
--SELECT #replaceword as 'repword', #newWord as 'newword'
SET #trav = REPLACE (#trav, #replaceword, #newWord)
SET #count = #repalceEndIndex
END
ELSE
BEGIN
SET #count = #count + 1
END
END
RETURN #trav
END
GO
IF OBJECT_ID('tempdb..#table') IS NOT NULL
DROP TABLE #table
CREATE TABLE #table
(
string VARCHAR(1000)
)
INSERT INTO #table
SELECT
'1. <strong>abc</strong>:<description1> <strong>bcd</strong>:<description2>'
INSERT INTO #table
SELECT
'2. <strong>efg</strong>:<description3> <strong>hgl</strong>:<description7>'
UPDATE #table
SET string = [dbo].[GetString](#table.string)
SELECT *
FROM #table

How to parse an array in sql server

I created a stored procedure that looks like this.
CREATE PROCEDURE [EXP_RfPickAfter] (
#SESSIONVALUE xml,
#WorkDSArray nvarchar(max),
#UserSignonDS nvarchar(max),
#Direction nvarchar(max),
#ImmediateCount nvarchar(max),
#CCWorkUnit nvarchar(max),
#PartialPick nvarchar(max),
#RETURNVALUE nvarchar(max) output
)
AS
SET NOCOUNT ON;
#workDSArray has this long string in it
924WORK0924841SEQUENCE8410841842EQUIPMENT_LOC842842843PARENT_LOGISTICS_
UNIT843843844REFERENCE_TYPE844Order844845INVENTORY_AT_
PD845845846FROMVER846None846847INTERNAL_NUM8476851842847848START_
DATE_TIME848848849INCHKDIG849849850TOTAL_VOLUME8500850851
TO_LOC851SHIP-1851852INTERNAL_NUM_TYPE852Shipment852853
WEIGHT_UM853LB853854COMPANY854SHOS854855QUANTITY_UM855CS855856OUTGOING_
PD_LOC856856857WORK_TYPE857LPNPick857858END_DATE_TIME858858859
ACTUALFROMWHS859L455859860LOT860860861WORK_ZONE1861861862
TOCONTAINERID8621862863INVER863863864TRACK_CONTAINERS864N864865
TO_LOC_INV_ATTRIBUTES_ID865865866CYCLE_COUNT866866867
LAUNCH_NUM86716949391867868ACTUALTOLOC868-jisavm3868869
FROM_CHECK_DIG869869870FROM_WHS870L455870871
WORK_GROUP871Picking871872INTERNAL_LINE_NUM187232612518872873
VOLUME_UM873873874ACTUALFROMLOC874NF-1-F-28-1874875
INTERNAL_REQ_NUM87520774185875876TO_WHS876L455876877
INTERNAL_LINE_NUM87732612518877878TREE_UNIT87826385537878879
INWRKZONE879879880PUTLOC880SHIP-1880881CONTAINER_ID88116489660881882
INTERNAL_CONTAINER_NUM88226385537882883FROMTRKCONT883Y883884
PARENT_CONTAINER_NUM8840884885CONVERTED_QTY_UM885CS885886
ACTUALTOWHS886L455886887OUTWRKZONE887887888
VERIFICATION_METH888None888889USER_DEF4889 L455-RS 2889890
REFERENCE_ID890XKJNNLJN890891LOGISTICS_UNIT89116489660891892
MESSAGE_ID892892893PICKLOC893NF-1-F-28-1893894
CURRENTLOC894F894895TREE_UNIT_ID89516489660895897
TOCONTAINERID089716489660897898TOTAL_WEIGHT8981898899
TOTAL_VALUE8990899900TO_CHECK_DIG900900901OUTCHECKDIG901901902
TO_QTY9020902903INTRKCONT903903904FROM_LOC_INV_ATTRIBUTES_ID904904905
INTERNAL_INSTRUCTION_NUM90520699485905906ITEM_DESC906MSF21D4MDE -
Refrigerator906907ITEM907SHOS-STOCK907908
COMPLETEDBYUSER908jisavm3908909
TRANSPORT_CONT_ID90916489660909910OUTTRKCONT910910911ACCOUNT911
Store #9449911912INCOMING_PD_LOC912912913INVENTORY_TRACKING913Y913914
LOCATION_CLASS914Shipping Dock914915FROM_QTY9151915916OUTVERMETH916916917
CONVERTED_QTY9171917918QUANTITY9181918919
PARENT_INSTR91920699484919920
FROM_LOC920NF-1-F-28-1920921WORK_ZONE921W-
Returns921922WORK_UNIT92216489660922923QTY
CONFIRMED9231923924925WORK9251925
I am trying to parse containerID and its value from this string and use it in a select statement in my stored procedure, but I am unsure how to parse only the containerID.
Any help would be appreciated
This is what I did to get the right ID I had to trigger off of something else. This is not ideal because I don't know if the string will change. If anybody knows a better way please post and thank you #ymuribbi you helped me out a ton. I edited this to show my full solution just in case someone else runs into this issue.
DECLARE #WorkDSArray nvarchar(max);
SET #WorkDSArray= '924WORK0924841SEQUENCE8410841842EQUIPMENT_LOC842842843PARENT_LOGISTICS_UNIT843843844REFERENCE_TYPE844Order844845INVENTORY_AT_PD845845846FROMVER846None846847INTERNAL_NUM8476851842847848START_DATE_TIME848848849INCHKDIG849849850TOTAL_VOLUME8500850851TO_LOC851SHIP-1851852INTERNAL_NUM_TYPE852Shipment852853WEIGHT_UM853LB853854COMPANY854SHOS854855QUANTITY_UM855CS855856OUTGOING_PD_LOC856856857WORK_TYPE857LPN Pick857858END_DATE_TIME858858859ACTUALFROMWHS859L455859860LOT860860861WORK_ZONE1861861862TOCONTAINERID8621862863INVER863863864TRACK_CONTAINERS864N864865TO_LOC_INV_ATTRIBUTES_ID865865866CYCLE_COUNT866866867LAUNCH_NUM86716949391867868ACTUALTOLOC868-jisavm3868869FROM_CHECK_DIG869869870FROM_WHS870L455870871WORK_GROUP871Picking871872INTERNAL_LINE_NUM187232612518872873VOLUME_UM873873874ACTUALFROMLOC874NF-1-F-28-1874875INTERNAL_REQ_NUM87520774185875876TO_WHS876L455876877INTERNAL_LINE_NUM87732612518877878TREE_UNIT87826385537878879INWRKZONE879879880PUTLOC880SHIP-1880881CONTAINER_ID88116489660881882INTERNAL_CONTAINER_NUM88226385537882883FROMTRKCONT883Y883884PARENT_CONTAINER_NUM8840884885CONVERTED_QTY_UM885CS885886ACTUALTOWHS886L455886887OUTWRKZONE887887888VERIFICATION_METH888None888889USER_DEF4889 L455-RS 2889890REFERENCE_ID890XKJNNLJN890891LOGISTICS_UNIT89116489660891892MESSAGE_ID892892893PICKLOC893NF-1-F-28-1893894CURRENTLOC894F894895TREE_UNIT_ID89516489660895897TOCONTAINERID089716489660897898TOTAL_WEIGHT8981898899TOTAL_VALUE8990899900TO_CHECK_DIG900900901OUTCHECKDIG901901902TO_QTY9020902903INTRKCONT903903904FROM_LOC_INV_ATTRIBUTES_ID904904905INTERNAL_INSTRUCTION_NUM90520699485905906ITEM_DESC906MSF21D4MDE - Refrigerator906907ITEM907SHOS-STOCK907908COMPLETEDBYUSER908jisavm3908909TRANSPORT_CONT_ID90916489660909910OUTTRKCONT910910911ACCOUNT911Store #9449911912INCOMING_PD_LOC912912913INVENTORY_TRACKING913Y913914LOCATION_CLASS914Shipping Dock914915FROM_QTY9151915916OUTVERMETH916916917CONVERTED_QTY9171917918QUANTITY9181918919PARENT_INSTR91920699484919920FROM_LOC920NF-1-F-28-1920921WORK_ZONE921W-Returns921922WORK_UNIT92216489660922923QTYCONFIRMED9231923924925WORK9251925'
DECLARE #ContainerID integer;
SET #ContainerID = CHARINDEX('TREE_UNIT_ID', #WorkDSArray);
DECLARE #ID nvarchar(max);
SET #ID = SUBSTRING(#WorkDSArray, #ContainerID,300)
SELECT #ID;
DECLARE #ActualContainerID nvarchar(max);
SET #ActualContainerID = SUBSTRING(#ID, 54, 9)
SET #ActualContainerID = REPLACE(#ActualContainerID, CHAR(31), '')
DECLARE #User integer
DECLARE #UserID nvarchar(max)
SET #User = CHARINDEX('COMPLETEDBYUSER', #WorkDSArray);
SET #UserID = SUBSTRING(#WorkDSArray, #User,27)
DECLARE #ActualUserID nvarchar(max);
SET #ActualUserID = SUBSTRING(#UserID, 20, 8)
SET #ActualUserID = REPLACE(#ActualUserID, CHAR(31), '')
DECLARE #Shipment nvarchar(max)
SET #Shipment = (SELECT CASE WHEN SH.COMPANY = 'SHOS' THEN '' ELSE SH.SHIPMENT_ID + '|' +
SH."ROUTE" + '|' +
SH."STOP" + '|' +
SH.COMPANY + '|' +
LEFT(SH.CUSTOMER_NAME,25) + '|' +
LEFT(SH.SHIP_TO_ADDRESS1,25) + '|' +
CASE WHEN SH.SHIP_TO_ADDRESS2 <> '' THEN SH.SHIP_TO_ADDRESS2 + '|'
ELSE '' END +
LEFT(SH.SHIP_TO_CITY,25) + '|' +
LEFT(SH.SHIP_TO_STATE,25) + '|' +
LEFT(SH.SHIP_TO_POSTAL_CODE,25) END
FROM SHIPMENT_HEADER SH
JOIN SHIPPING_CONTAINER SC
ON SC.INTERNAL_SHIPMENT_NUM = SH.INTERNAL_SHIPMENT_NUM
WHERE SC.CONTAINER_ID = #ActualContainerID)
DECLARE #ShipmentID nvarchar(max)
SET #ShipmentID = SUBSTRING(#Shipment,0,9)
--select top 1 device_name from dbo.DOCUMENT_ROUTING where document_type = 'HJBT_REC' and user_name = #user
INSERT INTO [jbh].[PRT_REQ_WRK]
([REQ_REF_TYP], [REQ_REF_I], [PRT_DVC_NM], [REQ_TMPL_NM], [REQ_VAL], [PRT_STT], [PRT_PRS_TYP], [PRT_Q], [CRT_S], [CRT_UID], [CRT_PGM_C], [LST_UPD_S], [LST_UPD_UID], [LST_UPD_PGM_C])
VALUES (
#ShipmentID, --[REQ_REF_TYP]
'123abc', --[REQ_REF_I]
'LB01885', --[PRT_DVC_NM]
'HJBT_Shipping_Label.lbl', --[REQ_TMPL_NM]
#Shipment, --[REQ_VAL]
0, --[PRT_STT]
'LABEL', --[PRT_PRS_TYP]
1, --[PRT_Q]
CURRENT_TIMESTAMP, --[CRT_S]
#ActualUserID, --[CRT_UID]
'WMSPrintService', --[CRT_PGM_C]
CURRENT_TIMESTAMP, --[LST_UPD_S]
'JISAVM3', --[LST_UPD_UID]
'WMSPrintService') --[LST_UPD_PGM_C]
As #hogan stated in his comment, you can use string operators for extraction with some while and if-else statements
-- I am editing my answer.
To retrieve the container ID you use a store procedure like this:
create proc getid (#string varchar(max)) as
declare #containerIdIndex int = (select charindex('CONTAINER_ID',#string))
declare #internalContainerIndex int = (select(Charindex('INTERNAL_CONTAINER', #string)))
declare #range int = #internalContainerindex-#containerIdindex+12
declare #start int = #containerIdIndex+12 --12 is to pass the 'Container_ID'
declare #containerId varchar(max)
set #containerId = substring(#string,#start,#range)
print #containerId

How to find the First Character starting in SQL Server 2008

DECLARE #input NVARCHAR(MAX) = 'Create Procedure Test'
DECLARE #SearhString NVARCHAR(MAX) = 'Procedure'
DECLARE #index BIGINT = CHARINDEX(#keyword, #input)
1- I want to find the length of the next character starting after string Procedure
2- After that I want to check if the dbo string exists after that length to the specified location.
Suppose the first character Test starting at position 14 after string Procedure, so I have to use
DECLARE #String varchar(20) = SUBSTRING(#input, 21, 10)
Declare #StringIndex BIGINT = CHARINDEX('dbo.', #String)
Declare #FirstCharacterIndex BIGINT(This will be the index of the first character)
If #String not contains string dbo then stuff MySchema before string Test.
Please suggest.
UPDATE
I have updated my question 22 is the result of #index + #FirstCharacterIndex + 9
#index = 8
#FirstCharacterIndex = 4(3 count for spaces and 4th is for first character)
9 = No. of characters of string Procedure
try this sql below:
DECLARE #input NVARCHAR(MAX) = 'Create Procedure Test';
DECLARE #input2 NVARCHAR(MAX) = 'Create Procedure dbo Test';
DECLARE #SearchString NVARCHAR(MAX) = 'Procedure';
DECLARE #index BIGINT = CHARINDEX(#SearchString, #input);
select #input, #SearchString as searchstring, #index as Index_SearchString , #index +len(#SearchString) as Last_CharacterOfSearchString
, charIndex('Test', #input,#index +len(#SearchString)) as test_Index
, charIndex('dbo', #input,#index +len(#SearchString)) as dbo_Index
, substring(#input, 0, #index +len(#SearchString)) as str1
, substring(#input, #index +len(#SearchString), len(#input)-#index +len(#SearchString)) as str2
Declare #dbo_Index int
Declare #str1 nvarchar(max), #str2 nvarchar(max)
set #dbo_Index = charIndex('dbo', #input,#index +len(#SearchString))
--select #dbo_Index
if #dbo_Index = 0
begin
set #str1 = substring(#input, 0, #index +len(#SearchString))
set #str2 = substring(#input, #index +len(#SearchString), len(#input)-#index +len(#SearchString))
select #str1 + ' ' + 'MySchema' + #str2
end
--,charIndex('dbo',#input2, #index +len(#SearchString)) as dbo_Index
Updated:
objective: Get the "Under Fire" first character index ("U")
DECLARE #input2 NVARCHAR(MAX) = 'Create Procedure Under Fire Test';
DECLARE #SearchString NVARCHAR(MAX) = 'Procedure';
--,charIndex('dbo',#input2, #index +len(#SearchString)) as dbo_Index
select substring(#input2,17,25)
, CHARINDEX(#SearchString,#input2,0) + len(#SearchString) as LastIndexOf_Procedure
, CHARINDEX('Test',#input2,0)
,len(#input2)
,substring(#input2, CHARINDEX(#SearchString,#input2,0) + len(#SearchString), CHARINDEX('Test',#input2,0)- (CHARINDEX(#SearchString,#input2,0)+len(#SearchString)))
set #SearchString= Ltrim(rtrim(substring(#input2, CHARINDEX(#SearchString,#input2,0) + len(#SearchString), CHARINDEX('Test',#input2,0)- (CHARINDEX(#SearchString,#input2,0)+len(#SearchString)))))
select #SearchString
select
#input2
,#SearchString as SearchString
,CHARINDEX(#SearchString,#input2,0) as FirstCharacterIndex

Looking for multiples substrings within a SQL string

I have several occurrences of differences strings in the columns, like this example
'dsasdasdsd'+'ewewewew'+'45454545'+(avg('uuuuuuu'))
I need to split this string into several columns with the substrings that are between
aphostropes
like this:
Column 1 = dsasdasdsd
Column 2 = ewewewew
Column 3 = 45454545
Column 4 = uuuuuuu
The numbers of apperances are random, thefore the length of the original column is also not fixed (from 50 char to > 1000)
DECLARE #InStr VarChar(1000) = '''dsasdasdsd''+''ewewewew''+''45454545''+(avg(''uuuuuuu''))'''
DECLARE #intStart INT = 0
DECLARE #intEnd INT = 1
DECLARE #ColNo INT = 1
DECLARE #MyString VARCHAR(2000)
DECLARE #SelectString VARCHAR(8000) = 'SELECT '
WHILE(#intStart < LEN(#InStr) )
BEGIN
SELECT #intStart = CHARINDEX(CHAR(39), #InStr, 0) + 1
SELECT #intEnd = CHARINDEX(CHAR(39), #InStr, #intStart)
SELECT #SelectString = #SelectString + CHAR(39) + SUBSTRING(#InStr, #intStart, #intEnd - #intStart) + CHAR(39) + ' As [Column ' + CAST(#ColNo As Varchar) + '],'
SELECT #InStr = SUBSTRING(#InStr, #intEnd + 1, LEN(#InStr)-#intEnd )
SET #ColNo = #ColNo +1
END
SELECT #SelectString = LEFT(#SelectString, Len(#SelectString) -1)
EXEC (#SelectString)
I have been playing with this and this does run but unfortunately I don't have time right now to carry on with it but maybe you can improve on this?
HTH
You can try this:
create table tSqlStrings (sText nvarchar(1000))
insert tSqlStrings values('''dsasdasdsd''+''ewewewew''+''45454545''+(avg(''uuuuuuu''))')
create table tResults (
sColumn1 nvarchar(1000)
,sColumn2 nvarchar(1000)
,sColumn3 nvarchar(1000)
,sColumn4 nvarchar(1000)
)
and
DELETE tResults
DECLARE #sText nvarchar(1000) = (
SELECT
sText
FROM
tSqlStrings
)
DECLARE #lBegin int = CHARINDEX('''',#sText)
DECLARE #lEnd int = charindex('''',
substring(#sText,
CHARINDEX('''',#sText)+1,
len(#sText)))
DECLARE #sText0 nvarchar(1000)
DECLARE #sColumn1 nvarchar(1000)
DECLARE #sColumn2 nvarchar(1000)
DECLARE #sColumn3 nvarchar(1000)
DECLARE #sColumn4 nvarchar(1000)
DECLARE #iCnt int = 1
while #iCnt<=4
--(0<len(#sText) and 0<#lBegin and 0<#lEnd)
BEGIN
SET #sText0 = substring(#sText,#lBegin+1,#lEnd-2)
IF #iCnt=1 begin SET #sColumn1=#sText0 end
IF #iCnt=2 begin SET #sColumn2=#sText0 end
IF #iCnt=3 begin SET #sColumn3=#sText0 end
IF #iCnt=4 begin SET #sColumn4=#sText0 end
set #sText = substring(#sText,#lBegin + #lEnd+2,len(#sText))
SET #lBegin = CHARINDEX('''',#sText)
SET #lEnd = charindex('''',
substring(#sText,
CHARINDEX('''',#sText)+1,
len(#sText)))
SET #iCnt = #iCnt+1
END
INSERT
tResults (sColumn1,sColumn2,sColumn3,sColumn4)
VALUES (#sColumn1,#sColumn2,#sColumn3,#sColumn4)
SELECT * FROM tResults
on sql fiddle
You will be able to achieve this using CHARINDEX() and SUBSTRING()
Following example shows for splitting to 2 columns. When it has more columns, query will be get little more complicated. However, you can follow this to build your query.
SELECT OriginalColumn
, SUBSTRING(OriginalColumn, 1,CHARINDEX('x',OriginalColumn,1)-1) AS Column1
, SUBSTRING(OriginalColumn, CHARINDEX('x',OriginalColumn,1) + 1 ,CHARINDEX('x',OriginalColumn,CHARINDEX('x',OriginalColumn,1)-1)) AS Column2
FROM YourTable
I have used "x" as the delimiter in the example. Following is a sample result
try this:
declare #delim char
set #delim = ''''
declare #str nvarchar(max)
declare #substr nvarchar(max)
declare #newstr nvarchar(max)
declare #tmpTable table (partStrings nvarchar(max))
declare #count int
set #count = 0
select #str = <***Your String***>
while(charindex(#delim,#str) != 0)
begin
set #count = #count + 1
Select #substr = substring(#str,1,charindex(#delim,#str)-1)
if((#count % 2) = 0)
begin
insert into #tmpTable values(#substr)
end
Set #newstr = substring(#str,charindex(#delim,#str)+1,len(#str)-charindex(#delim,#str))
set #str = #newstr
end
select partStrings from #tmpTable

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)