Select part of VARCHAR value separated by the same special characters [duplicate] - sql

This question already has answers here:
How do I split a delimited string so I can access individual items?
(46 answers)
Closed 8 years ago.
I am using SQL Server 2008 R2
This is the value that I have :
DECLARE #DBB varchar(200) = 'A2gg3h.B2g3ghh3.Cggh3663.D1jhg23.Eh2hjj2g'
Returning the 2 outer values are easy enough :
SELECT LEFT(#DBB, CHARINDEX('.', #DBB)-1)
SELECT RIGHT(#DBB, CHARINDEX('.', #DBB)-1)
How would I alter script in order to select values :
1. 'Bg2g3ghh3'
2. 'Chggh3663'
3. 'Dh1jhg23'
Using CHARINDEX would only bring back (LEFT) 7 and (RIGHT) 9.
Thanks

Use this.
DECLARE #param NVARCHAR(MAX)
SET #param = 'A2gg3h.B2g3ghh3.Cggh3663.D1jhg23.Eh2hjj2g'
SELECT
Split.a.value('.', 'VARCHAR(100)') AS CVS
FROM
(
SELECT CAST ('<M>' + REPLACE(#param, '.', '</M><M>') + '</M>' AS XML) AS CVS
) AS A CROSS APPLY CVS.nodes ('/M') AS Split(a)

TRY THIS:
DECLARE #string VARCHAR(MAX),
#Split CHAR(1),
#X xml
SELECT #string = 'A2gg3h.B2g3ghh3.Cggh3663.D1jhg23.Eh2hjj2g',
#Split = '.'
SELECT #X = CONVERT(xml,'<root><s>' + REPLACE(#string,#Split,'</s><s>') + '</s></root>')
SELECT T.c.value('.','varchar(max)') AS Result
FROM #X.nodes('/root/s') T(c)

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?

Parse specific strings in SQL Server [duplicate]

This question already has answers here:
How do I split a delimited string so I can access individual items?
(46 answers)
Closed 7 years ago.
I' ve got this string:
DealerCode = [MAZ3].AccountID:[4340].StartDate=[2015-06-01]
select parsename('DealerCode = [MAZ3].AccountID:[4340].StartDate=[2015-06-01]', 1)
and so on gives me null. How should i change my query to get values between dots?
Try this :
DECLARE #param NVARCHAR(MAX)
SET #param = 'DealerCode = [MAZ3].AccountID:[4340].StartDate=[2015-06-01]'
SELECT
Split.a.value('.', 'VARCHAR(100)') AS CVS
FROM
(
SELECT CAST ('<M>' + REPLACE(#param, '.', '</M><M>') + '</M>' AS XML) AS CVS
) AS A CROSS APPLY CVS.nodes ('/M') AS Split(a)

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

SQL select string between known identical characters

I have code which selects the string between the first and second '/' in a typical string which may look like this:
2014-Ceilings/Ceilings/Repair/Asbestos/Supalux
The following code correctly returns the value 'Ceilings' in the above example.
SELECT
REPLACE (LEFT(SUBSTRING(ElementPath,CHARINDEX
('/',ElementPath)+1,LEN(ElementPath)),CHARINDEX
('/',SUBSTRING(ElementPath, CHARINDEX
('/',ElementPath)+1,LEN(ElementPath)))),'/','')
FROM K2_Master.dbo.tbElement
How can I amend it to select the string between the second and third '/' to return the value 'Repair'?
Check this little trick may work :
DECLARE #String VARCHAR(100)= '2014-Ceilings/Ceilings/Repair/Asbestos/Supalux'
SET #String = '<N>' + Replace(#String, '/', '</N><N>')
+ '</N>'
SELECT c.value('/N[1]', 'varchar(30)'),
c.value('/N[2]', 'varchar(30)'),
c.value('/N[3]', 'varchar(30)')
FROM (SELECT Cast(#String AS XML)) t(c)
SELECT c1.value('.', 'varchar(30)')
FROM (SELECT CAST(#String AS XML)) t(c)
CROSS APPLY c.nodes('/N') AS t1(c1)
SELECT value
FROM (SELECT c1.value('.', 'varchar(30)') AS value,
ROW_NUMBER()
OVER(
ORDER BY (SELECT 1)) rn
FROM (SELECT Cast(#String AS XML)) t(c)
CROSS APPLY c.nodes('/N') AS t1(c1)) temp
WHERE rn = 3
In your case
SELECT c.value('/N[1]', 'varchar(30)'),
c.value('/N[2]', 'varchar(30)'),
c.value('/N[3]', 'varchar(30)')
FROM (SELECT Cast ('<N>' + Replace( Replace (ElementPath, '/', '</N><N>'),'&','&')
+ '</N>' AS XML)
FROM K2_Master.dbo.tbElement) t(c)
I chased your question, and reached a lengthy result..
DECLARE #a VARCHAR(40) = '2014-Ceilings/Ceilings/Repair/Asbestos/Supalux'
SELECT #a
,LEFT(
SUBSTRING(SUBSTRING(#a,CHARINDEX('/',#a,1)+1,LEN(#a)),CHARINDEX('/',SUBSTRING(#a,CHARINDEX('/',#a,1)+1,LEN(#a)),1)+1,LEN(SUBSTRING(#a,CHARINDEX('/',#a,1)+1,LEN(#a)))),
CHARINDEX('/',SUBSTRING(SUBSTRING(#a,CHARINDEX('/',#a,1)+1,LEN(#a)),CHARINDEX('/',SUBSTRING(#a,CHARINDEX('/',#a,1)+1,LEN(#a)),1)+1,LEN(SUBSTRING(#a,CHARINDEX('/',#a,1)+1,LEN(#a)))),1) - 1
)
Result:
Answer to you comment.
DECLARE #String VARCHAR(100)= '2014-Ceilings/Ceilings/Repair/Asbestos/Supalux',
#value INT = 2
DECLARE #itra INT = 1
WHILE #itra <= #value
BEGIN
SET #String = (SELECT SUBSTRING(#String,CHARINDEX('/',#String,1)+1,LEN(#String)))
--SELECT #String
SET #itra = #itra + 1
END
SELECT LEFT(#String, CHARINDEX('/',#String,1) - 1)
Give the position of '/' as #value and see the result. For the above sql, the result is Repair. If you give #value = 3, result is Asbestos

Extract one value from a column containing multiple delimited values

How can I get the value from the sixth field in the following column? I am trying to get the 333 field:
ORGPATHTXT
2123/2322/12323/111/222/333/3822
I believe I have to use select substring, but am unsure how to format the query
Assuming SQL Server
The easiest way I can think of is create a Split function that splits based on '/' and you extract the sixth item like below
declare #text varchar(50) = '2123/2322/12323/111/222/333/3822'
select txt_value from fn_ParseText2Table(#text, '/') t where t.Position = 6
I used the function in this url. See it worked at SQLFiddle
Try this - for a string variable or wrap into a function to use with a select query (Sql-Demo)
Declare #s varchar(50)='2123/2322/12323/111/222/333/3822'
Select #s = right(#s,len(#s)- case charindex('/',#s,1) when 0 then len(#s)
else charindex('/',#s,1) end)
From ( values (1),(2),(3),(4),(5)) As t(num)
Select case when charindex('/',#s,1)>0 then left(#s,charindex('/',#s,1)-1)
else #s end
--Results
333
I'd like to offer a solution that uses CROSS APPLY to split up any delimited string in MSSQL and ROW_NUMBER() to return the 6th element. This assumes you have a table with ORGPATHTXT as a field (it can easily be converted to work without the table though):
SELECT ORGPATHTXT
FROM (
SELECT
Split.a.value('.', 'VARCHAR(100)') AS ORGPATHTXT,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT 1)) RN
FROM
(SELECT ID, CAST ('<M>' + REPLACE(ORGPATHTXT, '/', '</M><M>') + '</M>' AS XML) AS String
FROM MyTable
) AS A
CROSS APPLY String.nodes ('/M') AS Split(a)
) t
WHERE t.RN = 6;
Here is some sample Fiddle to go along with it.
Good luck.
For sql, you can use
declare #string varchar(65) = '2123/2322/12323/111/222/333/3822'
select substring(string,25,27) from table_name
If you are using MySQL, then you can use:
select substring_index(orgpathtxt, '/', 6)
Let me just say that it is less convenient in most other databases.
Also you can use option with dynamic management function sys.dm_fts_parser
DECLARE #s nvarchar(50) = '2123/2322/12323/111/222/333/3822'
SELECT display_term
FROM sys.dm_fts_parser('"'+ #s + '"', 1033, NULL, 0)
WHERE display_term NOT LIKE 'nn%' AND occurrence = 6