Get the character between first 2 special character in SQL - sql

I have data in sql (Just to note: SQL STudio is the IDE) like:
data
a_10_b_c
a_1_b_c
I want to get the data between first two symbols _:
Output
10
1

This would be my approach:
SELECT CAST('<x>' + REPLACE(data,'_','</x><x>') + '</x>' AS XML).value('/x[2]','int')
FROM YourTable
First you transform this to an XML and then you pick the second node..
EDIT: Some more examples where this approach is usefull:
CROSS APPLY: You can use this approach to get several tokens at once
DECLARE #tbl TABLE(separated VARCHAR(100));
INSERT INTO #tbl VALUES('1_23:50_Look_this_is_a_test'),('2_12:00_that''s_one_more_test'),('3_13:30_great!_It_works!');
SELECT Converted.value('/x[1]','int') AS number
,Converted.value('/x[2]','time') AS time
,Converted.value('/x[3]','varchar(max)') AS text
FROM #tbl
CROSS APPLY(SELECT CAST('<x>' + REPLACE(separated,'_','</x><x>') + '</x>' AS XML) AS Converted) AS MySeparated
--type-safe and easy:
/*
number time text
1 23:50 Look
2 12:00 that's
3 13:30 great!
*/
GO
CTE: use as parameter
DECLARE #Parameter VARCHAR(100)='1_12:30_SomeValue';
WITH MyParameters AS
(
SELECT CAST('<x>' + REPLACE(#Parameter,'_','</x><x>') + '</x>' AS XML).value('/x[1]','int') AS IntParam
,CAST('<x>' + REPLACE(#Parameter,'_','</x><x>') + '</x>' AS XML).value('/x[2]','time') AS TimeParam
,CAST('<x>' + REPLACE(#Parameter,'_','</x><x>') + '</x>' AS XML).value('/x[3]','varchar(max)') AS TextParam
)
SELECT IntParam,TimeParam,TextParam
FROM MyParameters
/*
IntParam TimeParam TextParam
1 12:30:00 SomeValue
*/
GO
Split String: Transform to list
DECLARE #MyIDs VARCHAR(100)='3,5,7';
SELECT A.B.value('.','int') TheIntValue
FROM(SELECT CAST('<x>' + REPLACE(#MyIDs,',','</x><x>') + '</x>' AS XML) AS MyListAsXML) AS x
CROSS APPLY MyListAsXML.nodes('/x') AS A(B)
/*
TheIntValue
3
5
7
*/
GO
Dynamic IN Statement
DECLARE #tbl TABLE(ID INT,Content VARCHAR(max));
INSERT INTO #tbl VALUES(1,'Value 1'),(2,'Value 2'),(3,'Value 3'),(4,'Value 4'),(5,'Value 5'),(6,'Value 6'),(7,'Value 7');
DECLARE #MyIDs VARCHAR(100)='3,5,7';
/*
This won't work (due to the fact, that #MyIDs is not a list of INTs but a text
SELECT * FROM #tbl WHERE ID IN(#MyIDs)
*/
WITH AsList AS
(
SELECT A.B.value('.','int') TheIntValue
FROM(SELECT CAST('<x>' + REPLACE(#MyIDs,',','</x><x>') + '</x>' AS XML) AS MyListAsXML) AS x
CROSS APPLY MyListAsXML.nodes('/x') AS A(B)
)
SELECT * FROM #tbl WHERE ID IN(SELECT TheIntValue FROM AsList)
/*
ID Content
3 Value 3
5 Value 5
7 Value 7
*/

You can do this with nested string functions. Often, this is simpler using outer apply:
select t3.output
from t outer apply
(select stuff(t.col, 1, charindex('_', t.col), '') as col2
) t2 outer apply
(select left(t2.col2, charindex('_', t2.col2)) as output
) t3;

Related

Loop and insert more than one comma separated List in SQL

I wish to loop through two comma-separated values and perform an insert
As an example lets consider two variables
Declare #Qid= 1,4,6,7,8 #Answers = 4,4,3,2,3
set #pos = 0
set #len = 0
WHILE CHARINDEX(',', #Answers, #pos+1)>0
BEGIN
set #len = CHARINDEX(',', #Answers, #pos+1) - #pos
set #value = SUBSTRING(#Answers, #pos, #len)
insert into table values(#fdid,#Qid,#fusid, #value) -- i need Qid also
set #pos = CHARINDEX(',', #Answers, #pos+#len) +1
END
Using this loop I am able to extract #Answers and can perform insert. But I wish to extract #Qid and insert inside the loop.
edit
for more clarity it is a feedback module. my result table have Qid and Answer field. Answers are ratings (1 to 5). The values we get in variables #Qid and #Answers are sequential. which means 1st answer will be for 1st question and so on.
edit
as per Shnugo's Answer
Declare #Qid varchar(100)= '1,4,6,7,8', #Answers varchar(100)= '4,4,3,2,3'
DECLARE #tbl TABLE(ID INT IDENTITY, Questions VARCHAR(100),Answers VARCHAR(100));
INSERT INTO #tbl VALUES(#Qid,#Answers)
INSERT INTO table(FeedbackId,QuestionId,FeedbackUserId,Answer)
SELECT 1,
A.qXml.value('(/x[sql:column("B.QuestionCount")])[1]','int') AS QuestionNumber,3
,A.aXml.value('(/x[sql:column("B.QuestionCount")])[1]','int') AS AnwerNumber
FROM #tbl t
CROSS APPLY(SELECT CAST('<x>' + REPLACE(#Qid,',','</x><x>') + '</x>' AS XML)
,CAST('<x>' + REPLACE(#Answers,',','</x><x>') + '</x>' AS XML)) A(qXml,aXml)
CROSS APPLY(SELECT TOP(A.qXml.value('count(/x)','int')) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) B(QuestionCount)
I'd prefer Zhorov's JSON answer (needs v2016+).
If you use a SQL-Server below 2016 you might use this position-safe XML-based solution:
A mockup table to simulate your issue with two different rows.
DECLARE #tbl TABLE(ID INT IDENTITY, Questions VARCHAR(100),Answers VARCHAR(100));
INSERT INTO #tbl VALUES('1,4,6,7,8','4,4,3,2,3')
,('1,2,3','4,5,6');
--The query
SELECT t.*
,A.qXml.value('(/x[sql:column("B.QuestionCount")])[1]','int') AS QuestionNumber
,A.aXml.value('(/x[sql:column("B.QuestionCount")])[1]','int') AS AnwerNumber
FROM #tbl t
CROSS APPLY(SELECT CAST('<x>' + REPLACE(t.Questions,',','</x><x>') + '</x>' AS XML)
,CAST('<x>' + REPLACE(t.Answers,',','</x><x>') + '</x>' AS XML)) A(qXml,aXml)
CROSS APPLY(SELECT TOP(A.qXml.value('count(/x)','int')) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) B(QuestionCount);
The idea in short:
We need a CROSS APPLY and some string methods to transform something like 1,2,3 to an xml like <x>1</x><x>2</x><x>3</x>.
Now we can use value() with XQuery count() to find the actual count of questions.
We need one more CROSS APPLY with a computed TOP() clause to get a set of running number from 1 to n with n=countOfQuestions. I do this against master..spt_values. This is just a well-filled standard table... We do not need the values, just any set to create the counter...
Finally we can use .value() in connection with sql:column() in order to fetch the question and the corresponding answer by their positions.
UPDATE: Non-tabular data
If you do not get these CSV parameters as a table you can use this:
Declare #Qid varchar(100)= '1,4,6,7,8', #Answers varchar(100)= '4,4,3,2,3'
--INSERT INTO table(FeedbackId,QuestionId,FeedbackUserId,Answer)
SELECT 1
,A.qXml.value('(/x[sql:column("B.QuestionCount")])[1]','int') AS QuestionNumber
,3
,A.aXml.value('(/x[sql:column("B.QuestionCount")])[1]','int') AS AnwerNumber
FROM (SELECT CAST('<x>' + REPLACE(#Qid,',','</x><x>') + '</x>' AS XML)
,CAST('<x>' + REPLACE(#Answers,',','</x><x>') + '</x>' AS XML)) A(qXml,aXml)
CROSS APPLY(SELECT TOP(A.qXml.value('count(/x)','int')) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) B(QuestionCount);
If you use SQL Server 2016 or higher, you may try to use the next JSON-based approach to map questions and answers by their positions in the input strings. You need to transform the input strings into valid JSON arrays and then use OPENJSON() with default schema to parse the arrays. The result is a table, with columns key, value and type and the key column holds the index of the element in the specified array.
Note, that STRING_SPLIT() function does not guarantee the order of the rows and the output rows might be in any order.
Statement:
DECLARE #Qid nvarchar(max) = N'1,4,6,7,8'
DECLARE #Answers nvarchar(max) = N'4,4,3,2,3'
-- Build your INSERT statement as you expect
-- INSERT INTO Table ...
SELECT j1.[value] AS Qid, j2.[value] AS Answers
FROM OPENJSON(CONCAT(N'[', #Qid, N']')) j1
JOIN OPENJSON(CONCAT(N'[', #Answers, N']')) j2 ON j1.[key] = j2.[key]
Result from the SELECT statement:
Qid Answers
1 4
4 4
6 3
7 2
8 3
You have not described relationship of question and its answers. I feel its one to one relationship and for that, I have given the answer.
declare #Qid varchar(200)= '1,4,6,7,8' , #Answers varchar(200) = '4,4,3,2,3'
;with cte
as(
select id, data qid from dbo.Split (#qid, ',')
),
cte1 as
(
select id, data ansid from dbo.Split (#answers, ',')
)
--insert into tablename
select
qid, ansid from cte join cte1 on cte.id = cte1.id
Result will be:
qid ansid
1 4
4 4
6 3
7 2
8 3
See other option for later version of sqlserver : Split function equivalent in T-SQL?

How to SORT in order as entered in SQL Server?

I'm using SQL Server and I'm trying to find results but I would like to get the results in the same order as I had input the conditions.
My code:
SELECT
AccountNumber, EndDate
FROM
Accounts
WHERE
AccountNumber IN (212345, 312345, 145687, 658975, 256987, 365874, 568974, 124578, 125689) -- I would like the results to be in the same order as these numbers.
Here is an in-line approach
Example
Declare #List varchar(max)='212345, 312345, 145687, 658975, 256987, 365874, 568974, 124578, 125689'
Select A.AccountNumber
,A.EndDate
From Accounts A
Join (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = v.value('(./text())[1]', 'int')
From (values (convert(xml,'<x>' + replace(#List,',','</x><x>')+'</x>'))) x(n)
Cross Apply n.nodes('x') node(v)
) B on A.AccountNumber = B.RetVal
Order By B.RetSeq
EDIT - the subquery Returns
RetSeq RetVal
1 212345
2 312345
3 145687
4 658975
5 256987
6 365874
7 568974
8 124578
9 125689
You can replace IN with a JOIN, and set a field for ordering, like this:
SELECT AccountNumber , EndDate
FROM Accounts a
JOIN (
SELECT 212345 AS Number, 1 AS SeqOrder
UNION ALL
SELECT 312345 AS Number, 2 AS SeqOrder
UNION ALL
SELECT 145687 AS Number, 3 AS SeqOrder
UNION ALL
... -- and so on
) AS inlist ON inlist.Number = a.AccountNumber
ORDER BY inlist.SeqOrder
I will offer one more approach I just found out, but this needs v2016. Regrettfully the developers forgot to include the index into the resultset of STRING_SPLIT(), but this would work and is documented:
A solution via FROM OPENJSON():
DECLARE #str VARCHAR(100) = 'val1,val2,val3';
SELECT *
FROM OPENJSON('["' + REPLACE(#str,',','","') + '"]');
The result
key value type
0 val1 1
1 val2 1
2 val3 1
The documentation tells clearly:
When OPENJSON parses a JSON array, the function returns the indexes of the elements in the JSON text as keys.
This is not an answer, just some test-code to check John Cappelletti's approach.
DECLARE #tbl TABLE(ID INT IDENTITY,SomeGuid UNIQUEIDENTIFIER);
--Create more than 6 mio rows with an running number and a changing Guid
WITH tally AS (SELECT ROW_NUMBER()OVER(ORDER BY (SELECT NULL)) AS Nmbr
FROM master..spt_values v1
CROSS JOIN master..spt_values v2)
INSERT INTO #tbl
SELECT NEWID() from tally;
SELECT COUNT(*) FROM #tbl; --6.325.225 on my machine
--Create an XML with nothing more than a list of GUIDs in the order of the table's ID
DECLARE #xml XML=
(SELECT SomeGuid FRom #tbl ORDER BY ID FOR XML PATH(''),ROOT('root'),TYPE);
--Create one invalid entry
UPDATE #tbl SET SomeGuid = NEWID() WHERE ID=10000;
--Read all GUIDs out of the XML and number them
DECLARE #tbl2 TABLE(Position INT,TheGuid UNIQUEIDENTIFIER);
INSERT INTO #tbl2(Position,TheGuid)
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
,g.value(N'text()[1]',N'uniqueidentifier')
FROM #xml.nodes(N'/root/SomeGuid') AS A(g);
--then JOIN them via "Position" and check,
--if there are rows, where not the same values get into the same row.
SELECT *
FROM #tbl t
INNER JOIN #tbl2 t2 ON t2.Position=t.ID
WHERE t.SomeGuid<>t2.TheGuid;
At least in this simple case I always get exactly only the one record back which was invalidated...
Okay, after some re-thinking I'll offer the ultimative XML based type-safe and sort-safe splitter:
Declare #List varchar(max)='212345, 312345, 145687, 658975, 256987, 365874, 568974, 124578, 125689';
DECLARE #delimiter VARCHAR(10)=', ';
WITH Casted AS
(
SELECT (LEN(#List)-LEN(REPLACE(#List,#delimiter,'')))/LEN(REPLACE(#delimiter,' ','.')) + 1 AS ElementCount
,CAST('<x>' + REPLACE((SELECT #List AS [*] FOR XML PATH('')),#delimiter,'</x><x>')+'</x>' AS XML) AS ListXml
)
,Tally(Nmbr) As
(
SELECT TOP((SELECT ElementCount FROM Casted)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values v1 CROSS JOIN master..spt_values v2
)
SELECT Tally.Nmbr AS Position
,(SELECT ListXml.value('(/x[sql:column("Tally.Nmbr")])[1]','int') FROM Casted) AS Item
FROM Tally;
The trick is to create a list of running numbers with the fitting number of element (a number's table was even better) and to pick the elements according to their position.
Hint: This is rather slow...
UPDATE: even better:
WITH Casted AS
(
SELECT (LEN(#List)-LEN(REPLACE(#List,#delimiter,'')))/LEN(REPLACE(#delimiter,' ','.')) + 1 AS ElementCount
,CAST('<x>' + REPLACE((SELECT #List AS [*] FOR XML PATH('')),#delimiter,'</x><x>')+'</x>' AS XML)
.query('
for $x in /x
return <x p="{count(/x[. << $x])}">{$x/text()[1]}</x>
') AS ListXml
)
SELECT x.value('#p','int') AS Position
,x.value('text()[1]','int') AS Item
FROM Casted
CROSS APPLY Casted.ListXml.nodes('/x') AS A(x);
Elements are create as
<x p="99">TheValue</x>
Regrettfully the XQuery function position() is not available to retrieve the value. But you can use the trick to count all elements before a given node. this is scaling badly, as this count must be performed over and over. The more elements the worse it goes...
UPDATE2: With a known count of elements one might use this (much better performance)
Use XQuery to iterate a literally given list:
WITH Casted AS
(
SELECT (LEN(#List)-LEN(REPLACE(#List,#delimiter,'')))/LEN(REPLACE(#delimiter,' ','.')) + 1 AS ElementCount
,CAST('<x>' + REPLACE((SELECT #List AS [*] FOR XML PATH('')),#delimiter,'</x><x>')+'</x>' AS XML)
.query('
for $i in (1,2,3,4,5,6,7,8,9)
return <x p="{$i}">{/x[$i]/text()[1]}</x>
') AS ListXml
)
SELECT x.value('#p','int') AS Position
,x.value('text()[1]','int') AS Item
FROM Casted
CROSS APPLY Casted.ListXml.nodes('/x') AS A(x);
In Azure SQL, there is now extended version of STRING_SPLIT which also can return the order of items if the third optional argument enable_ordinal is set to 1.
Then this simple task is finally easy:
DECLARE #string AS varchar(200) = 'a/b/c/d/e'
DECLARE #position AS int = 3
SELECT value FROM STRING_SPLIT(#string, '/', 1) WHERE ordinal = #position
Unfortunately not available in SQL Server 2019, only in Azure for now, lets hope it will be in SQL Server 2022.

Split multiple comma separated columns into rows

I have one table (SQL Server), which has comma separated values in multiple columns, like below:
Rule_ID ListType_ID Values
1 1,2 100,200
2 3,4 300,400
I want to split the comma separated values and convert them into rows.
The required output must be like below:
Rule_ID ListType_ID Values
1 1 100
1 2 200
2 3 300
2 4 400
I have tried the below query:
DECLARE #TEMP AS TABLE (
[Rule_ID] INT,
[ListType_ID] VARCHAR(MAX),
[Values] VARCHAR(MAX)
)
INSERT INTO #TEMP
SELECT 1, '1,2', '100,200'
UNION ALL
SELECT 2, '3,4', '300,400'
SELECT
[Rule_ID],
PARSENAME(REPLACE(Split1.b.value('.', 'VARCHAR(100)'),'-','.'),1) AS [ListType_ID],
PARSENAME(REPLACE(Split.a.value('.', 'VARCHAR(100)'),'-','.'),1) AS [Values]
FROM
(
SELECT [Rule_ID],
CAST ('<M>' + REPLACE([ListType_ID], ',', '</M><M>') + '</M>' AS XML) AS [ListType_ID],
CAST ('<M>' + REPLACE([Values], ',', '</M><M>') + '</M>' AS XML) AS [Values]
FROM #TEMP
) AS A
CROSS APPLY [Values].nodes ('/M') AS Split(a)
CROSS APPLY [ListType_ID].nodes ('/M') AS Split1(b)
ORDER BY [Rule_ID], [ListType_ID], [Values]
This query returns the below output, which is different from the required output:
Rule_ID ListType_ID Values
1 1 100
1 1 200
1 2 100
1 2 200
2 3 300
2 3 400
2 4 300
2 4 400
Please help me here....!!!!
Please check following SQL script
To split string in SQL I used one of the following user-defined SQL split string functions
These functions return the order of the splitted string which I used in WHERE clause so I can map field values one-to-one
/*
create table Table_1 (
Rule_ID int, ListType_ID varchar(max), [Values] varchar(max)
)
insert into Table_1 select 1,'1,2','100,200'
insert into Table_1 select 2,'3,4','300,400'
*/
select
Rule_ID,
idlist.val as ListType_ID,
valueslist.val as [Values]
from Table_1
cross apply dbo.SPLIT(ListType_ID,',') as idlist
cross apply dbo.SPLIT([Values],',') as valueslist
where
idlist.id = valueslist.id
Using CTE, a double CROSS APPLY and XML based split you can use this script:
;WITH Splitted AS
(
SELECT
[Rule_ID]
,CAST('<x>' + REPLACE([ListType_ID],',','</x><x>') + '</x>' AS XML) AS [ListType_ID_Val]
,CAST('<x>' + REPLACE([Values],',','</x><x>') + '</x>' AS XML) AS [Values_Val]
FROM #TEMP
)
SELECT
Rule_ID, cs.VAL as[ListType_ID], cd.VAL as [Values]
FROM Splitted
CROSS APPLY (VALUES ('a',ListType_ID_Val.value(N'/x[1]','int') ),
('b',ListType_ID_Val.value(N'/x[2]','int') )
)CS (COL,VAL)
CROSS APPLY (VALUES ('a',Values_Val.value(N'/x[1]','int') ),
('b',Values_Val.value(N'/x[2]','int') )
)CD (COL,VAL)
where CS.COL = CD.COL
Results:

Is it possible to compare comma delimited string in T-SQL without looping?

Let's say I have 2 tables where both has column called Brand. The value is comma delimited so for example if one of the table has
ACER,ASUS,HP
AMD,NVIDIA,SONY
as value. Then the other table has
HP,GIGABYTE
MICROSOFT
SAMSUNG,PHILIPS
as values.
I want to compare these table to get all matched record, in my example ACER,ASUS,HP and HP,GIGABYTE match because both has HP. Right now I'm using loop to achieve this, I'm wondering if it's possible to do this in a single query syntax.
You are correct in wanting to step away from the loop.
Since you are on 2012, String_Split() is off the table. However, there are any number of split/parse TVF functions in-the-wild.
Example 1 - without a TVF
Declare #T1 table (Brand varchar(50))
Insert Into #T1 values
('ACER,ASUS,HP'),
('AMD,NVIDIA,SONY')
Declare #T2 table (Brand varchar(50))
Insert Into #T2 values
('HP,GIGABYTE'),
('MICROSOFT'),
('SAMSUNG,PHILIPS')
Select Distinct
T1_Brand = A.Brand
,T2_Brand = B.Brand
From (
Select Brand,B.*
From #T1
Cross Apply (
Select RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace(Brand,',','</x><x>')+'</x>' as xml)) as A
Cross Apply x.nodes('x') AS B(i)
) B
) A
Join (
Select Brand,B.*
From #T2
Cross Apply (
Select RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace(Brand,',','</x><x>')+'</x>' as xml)) as A
Cross Apply x.nodes('x') AS B(i)
) B
) B
on A.RetVal=B.RetVal
Example 2 - with a TVF
Select Distinct
T1_Brand = A.Brand
,T2_Brand = B.Brand
From (
Select Brand,B.*
From #T1
Cross Apply [dbo].[tvf-Str-Parse](Brand,',') B
) A
Join (
Select Brand,B.*
From #T2
Cross Apply [dbo].[tvf-Str-Parse](Brand,',') B
) B
on A.RetVal=B.RetVal
Both Would Return
T1_Brand T2_Brand
ACER,ASUS,HP HP,GIGABYTE
The UDF if interested
CREATE FUNCTION [dbo].[tvf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[tvf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[tvf-Str-Parse]('John Cappelletti was here',' ')
--Select * from [dbo].[tvf-Str-Parse]('this,is,<test>,for,< & >',',')
Had the same problem with comparing "," delimited strings
you can use "XML" to do that and compare the outputs and return the same/different value:
declare #TestInput nvarchar(255)
, #TestInput2 nvarchar(255)
set #TestInput = 'ACER,ASUS,HP'
set #TestInput2 = 'HP,GIGABYTE'
;WITH FirstStringSplit(S1) AS
(
SELECT CAST('<x>' + REPLACE(#TestInput,',','</x><x>') + '</x>' AS XML)
)
,SecondStringSplit(S2) AS
(
SELECT CAST('<x>' + REPLACE(#TestInput2,',','</x><x>') + '</x>' AS XML)
)
SELECT STUFF(
(
SELECT ',' + part1.value('.','nvarchar(max)')
FROM FirstStringSplit
CROSS APPLY S1.nodes('/x') AS A(part1)
WHERE part1.value('.','nvarchar(max)') IN(SELECT B.part2.value('.','nvarchar(max)')
FROM SecondStringSplit
CROSS APPLY S2.nodes('/x') AS B(part2)
)
FOR XML PATH('')
),1,1,'') as [Same Value]
Edit:
Changed 'Stuff' to 'XML'

Inserting multiple value in table with String input

I am passing one string to store procedure : 1:20,2:30,4:50
It contains id and appropriate value for it.
how can I add value as shown in below table in database.
ID Value
1 20
2 30
4 50
I have already "stringSplit" function which works perfectly and gives out put in row value some think like this :
1:20
2:30
4:50
can anyone please help me to insert data into table with any solution.
i already try this solution
insert <table> (colname)
select y.item
from dbo.SplitString(#testString, ':') x
cross apply
dbo.SplitString(x.item, ',') y
but this will return duplicate value as more as id value.
my store procedure is
CREATE PROCEDURE [dbo].[temp_result_insert]
#dataString varchar(max)
AS
insert into tempTable(id,marks)
select x.Item,y.Item
from dbo.SplitStringVarcahr(#dataString, ':') x
cross apply
dbo.SplitStringVarcahr(x.Item,',') y
RETURN 0
As you already splitted into rows and you want insert into some table by splliting into two columns may be this works
CREATE TABLE #Test(ID INT,Val INT)
declare #t table (val varchar(50))
insert into #t (val)values ('1:20,2:30,4:50')
declare #str varchar(max)
;with cte as (
SELECT
Split.a.value('.', 'VARCHAR(100)') AS String
FROM (SELECT
CAST ('<M>' + REPLACE([val], ',', '</M><M>') + '</M>' AS XML) AS String
FROM #t) AS A CROSS APPLY String.nodes ('/M') AS Split(a))
INSERT INTO #Test
select SUBSTRING(String,0,CHARINDEX(':',String)),REVERSE(SUBSTRING(reverse(String),0,CHARINDEX(':',reverse(String)))) from cte
select * from #test
You can also try XML.nodes() and string functions to spit the data. Something like this
DECLARE #var VARCHAR(100) = '1:20,2:30,4:50'
DECLARE #xml xml = CONVERT(xml, '<r>' + REPLACE(#var,',','</r><r>') + '</r>')
SELECT LEFT(val,cindex - 1) c1,RIGHT(val,LEN(val) - cindex) c2
FROM
(
SELECT CHARINDEX(':',c.value('text()[1]','VARCHAR(100)')) cindex,c.value('text()[1]','VARCHAR(100)') val
FROM #xml.nodes('r') as t(c))c
Use substring and Charindex:
SELECT Substring(col, 0, Charindex(col, ':') - 1) AS id,
Substring(col, Charindex(col, ':') + 1, Len(col)-Charindex(col, ':')) AS value
FROM splittedtable