SQL Server split string with delimiter - sql

I have an input string
100|2|3,101|2|1,103|2|3.
I would like to parse and add this in table having 3 columns so it should go f.x
col1 col2 col3
100 2 3
similar other data separated by comma as record and | as column.
Thanks
nik

Or this way:
SELECT
LEFT(value, Charindex('|', value) - 1),
SUBSTRING(value, Charindex('|', value) + 1,Len(value) - Charindex('|', Reverse(value)) - Charindex('|', value)),
RIGHT(value, Charindex('|', Reverse(value)) - 1)
FROM
string_split('100|2|3,101|2|1,103|2|3',',')

Try this way
DECLARE #TAB TABLE(COLUMN1 INT, COLUMN2 INT, COLUMN3 INT)
DECLARE #STRING VARCHAR(MAX)='100|2|3,101|2|1,103|2|3,'
SELECT #STRING = 'SELECT ' + REPLACE( REPLACE (#STRING, ',','
UNION ALL
SELECT '),'|',',')
SELECT #STRING = SUBSTRING(#STRING,1,LEN(#STRING)-18)
INSERT INTO #TAB
EXEC(#STRING)
SELECT * FROM #TAB
And the result will be
+---------+---------+---------+
| COLUMN1 | COLUMN2 | COLUMN3 |
+---------+---------+---------+
| 100 | 2 | 3 |
| 101 | 2 | 1 |
| 103 | 2 | 3 |
+---------+---------+---------+

Declare #temp table(col1 int,col2 int ,col3 int)
Declare #pos int,#str nvarchar(max),#len int
Set #str='100|2|3'
Set #pos=1
Select #len=len(#str)
Insert into #temp
Select substring(#str,#pos,charindex('|',#str,#pos)-1),
substring(#str,charindex('|',#str,#pos)+1,charindex('|',#str,#pos)-3),
substring(#str,charindex('|',#str,charindex('|',#str,#pos)+1)+1,#len)
Select * from #temp

;WITH tb(s)AS(
SELECT '100|2|3' UNION
SELECT '101|2|1' UNION
SELECT '103|2|3'
)
SELECT PARSENAME(REPLACE(s,'|','.'),3)
,PARSENAME(REPLACE(s,'|','.'),2)
,PARSENAME(REPLACE(s,'|','.'),1)
FROM tb
(No column name) (No column name) (No column name)
100 2 3
101 2 1
103 2 3

Related

How to divide a name into rows in sql server?

I want a name to be passed as a parameter to a stored procedure and when i execute this stored procedure, it should divide the name into rows.
For example if pass 'Peter', output should be
P
e
t
e
r
The fastest method to do this would be with a Tally and SUBSTRING. I assume that the name won't be longer than 100 characters, and that (as it's a name) it's an nvarchar rather than a varchar. You can then do something like this:
DECLARE #Name nvarchar(100) = N'Peter';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP(DATALENGTH(#Name) / 2) --If a varchar, remove the / 2
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2)
SELECT SUBSTRING(#Name,T.I,1) AS C
FROM Tally T;
declare #input varchar(max);
set #input = 'Peter'
declare #table TABLE (char varchar(1));
while (LEN(#input)> 0)
begin
insert into #table select substring(#input,1,1)
select #input = RIGHT(#input,Len(#input)-1)
end
select * from #table
GO
| char |
| :--- |
| P |
| e |
| t |
| e |
| r |
declare #T table
(
ID int identity,
names varchar(10)
)
insert into #T
select 'Peter'
;with cte as
(
select ID,
left(names, 1) as Data,
stuff(names, 1, 1, '') as remains
from #T
where len(names) > 0
union all
select ID,
left(remains, 1) as Data,
stuff(remains, 1, 1, '') as remains
from cte
where len(remains) > 0
)
select ID,
Data
from cte
order by ID
ID | Data
-: | :---
1 | P
1 | e
1 | t
1 | e
1 | r
db<>fiddle here

Convert row into column like pivot

Table is:
+----+------+
| Id | Name |
+----+------+
| 1 | aaa |
| 1 | bbb |
| 2 | ccc |
| 2 | ddd |
| 3 | eee |
+----+------+
Required output:
+----+---------------------++---------------------+
| Id | colum1 | column2 |
+----+---------------------+ +--------------------+
| 1 | aaa | | bbb |
+----+---------------------++---------------------+
+----+---------------------+ +--------------------+
| 2 | ccc | | ddd |
+----+---------------------++---------------------+
+----+---------------------+ +--------------------+
| 3 | eee | | null |
+----+---------------------++---------------------+
I've been trying on 'with' a and pivot but it seems not in the right way I want a column if I have more than one id
like the image
You can use row_number() & do aggregation if you have a some limited amount of names else you would need to use dynamic SQL for this:
select id,
max(case when seq = 1 then name end) as col1,
max(case when seq = 2 then name end) as col2,
max(case when seq = 3 then name end) as col3,
. . .
from (select t.*, row_number() over (partition by id order by name) as seq
from table t
) t
group by id;
You can use PIVOT for making this.
DECLARE #Tbl TABLE ( Id INT, Name VARCHAR(10))
INSERT INTO #Tbl VALUES
(1, 'aaa'),
(1, 'bbb'),
(2, 'ccc'),
(2, 'ddd'),
(3, 'eee')
SELECT Id, [1] column1, [2] column2 FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Name) RN
FROM #Tbl ) AS SRC PIVOT (MAX(Name) FOR RN IN ([1], [2])) PVT
Result:
Id column1 column2
----------- ---------- ----------
1 aaa bbb
2 ccc ddd
3 eee NULL
Using row_number() build a dynamic query to execute()
--example table
create table #t (Id int, Name varchar(100))
insert into #t values (1,'aaa'),(1,'bbb'),(2,'ccc'),(2,'ddd'),(3,'eee')
-- number of columns to create
declare #columns int
select top 1 #columns = count(*) from #t group by id order by COUNT(*) desc
--build a query
declare #i int = 2, #qry varchar(max) = 'select Id, column1 = max(case when ord = 1 then name end)'
while #i<=#columns begin
select #qry = #qry + ', column'+cast(#i as varchar(5))+' = max(case when ord = '+cast(#i as varchar(5))+' then name end)'
set #i = #i + 1
end
select #qry = #qry + ' from (select *, ord = row_number() over (partition by id order by name)
from #t
) t
group by id'
--execute the query
execute (#qry)
drop table #t

Extracting data between two delimiters in SQL Server?

I have a column that contains data in the format of aaa|bbb|ccc and need to extract aaa, bbb & ccc from the data separately.
I tried
SELECT
SUBSTRING(Column1, 0, CHARINDEX('|', Column1)) AS [First],
SUBSTRING(Column1, CHARINDEX('|', Column1) + 1, LEN(Column1)) AS [Second]
FROM
Table1
OUTPUT:
aaa [FIRST],bbb|ccc [Second]
but I need aaa [FIRST],bbb [Second],ccc [Third]
If you have a string that are exactly same format (three times) delimited by | then you can PARSENAME() :
select col1, parsename(cols, 3) fisrt, parsename(cols, 2) second, parsename(cols, 1) third
from table1 t1 cross apply
( values (replace(col1, '|', '.'))
) t2 (cols);
You can try below way where function charindex,SUBSTRING and REVERSE used
with t as (select 'aaa|bbb|ccc' as val)
select t.*,
LEFT(val, charindex('|', val) - 1),
SUBSTRING(val, charindex('|', val)+1, len(val) - CHARINDEX('|', reverse(val)) - charindex('|', val)),
REVERSE(LEFT(reverse(val), charindex('|', reverse(val)) - 1))
from t;
val
aaa|bbb|ccc aaa bbb ccc
You can split the value in the column using a common table expression (CTE) since split function is not available in your version of SQL Server
WITH CTE(userString,startIndex,EndIndex)
AS
(
SELECT Column1,1,CHARINDEX('|',Column1)
FROM Table1
UNION ALL
SELECT Column1, EndIndex+1,CHARINDEX('|',Column1,EndIndex+1)
FROM CTE
WHERE EndIndex !=0
)
SELECT SUBSTRING(userString,
startIndex,
CASE WHEN EndIndex > 0
THEN EndIndex - startIndex ELSE LEN(Column1) END)
as splitVALUES
FROM CTE
I do this all the time and it works well for me:
DECLARE #delimString VARCHAR(255) = 'aaa|bbb|ccc';
DECLARE #xml XML = '<val>' + REPLACE( #delimString, '|', '</val><val>' ) + '</val>'
SELECT
x.f.value( '.', 'VARCHAR(50)' ) AS val
FROM #xml.nodes( '//val' ) x( f );
Returns
+-----+
| val |
+-----+
| aaa |
| bbb |
| ccc |
+-----+
If you're looking for a columnar return and know you will always only have three values to parse, you might be able to get away with something like the below example. You can run it in SSMS.
DECLARE #table TABLE ( [value] VARCHAR(255) );
INSERT INTO #table ( [value] ) VALUES
( 'aaa|bbb|ccc' )
, ( '0A-PRDS|JQLM-1|1967' )
, ( 'J1658|G-1|2003' );
SELECT
[value]
, SUBSTRING( [value], 0, CHARINDEX( '|', [value] ) ) AS Column1
, SUBSTRING(
[value]
, ( CHARINDEX( '|', [value]) + 1 ) -- starting position of column 2.
, CHARINDEX( '|', [value], ( CHARINDEX( '|', [value] ) + 1 ) ) - ( CHARINDEX( '|', [value]) + 1 ) -- length of column two is the number of characters between the two delimiters.
) AS Column2
, SUBSTRING(
[value]
, CHARINDEX( '|', [value], ( CHARINDEX( '|', [value] ) + 1 ) ) + 1
, LEN( [value] )
) AS Column3
FROM #table;
Returns
+---------------------+---------+---------+---------+
| value | Column1 | Column2 | Column3 |
+---------------------+---------+---------+---------+
| aaa|bbb|ccc | aaa | bbb | ccc |
| 0A-PRDS|JQLM-1|1967 | 0A-PRDS | JQLM-1 | 1967 |
| J1658|G-1|2003 | J1658 | G-1 | 2003 |
+---------------------+---------+---------+---------+

How to insert string value into SQL Server table by separating by comma

int QcEditorId = 21
string Freelancer = '2,3,4,5'
I want to insert this value into the SQL Server table like
QcEditorId Freelancer
----------------------
21 2
21 3
21 4
21 5
Please help me with a query or stored procedure
String_split starting in SQL server 2016 have ability to split your strings
Example:
declare #table table (QcEditorId int, Freelancer varchar (100))
insert into #table
select 21, '2,3,4,5'
declare #freelancer varchar(100)
= (select freelancer from #table)
select QcEditorId,x.value Name from #table
cross apply(
select * from string_split(#freelancer,',') ) x
-- Use this function to split strings and , as delimiter
-- or for previous versions, create a table valued function , a lot
----available in web
Example:
CREATE FUNCTION [dbo].[splitstring] ( #stringToSplit VARCHAR(MAX) )
RETURNS
#returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(',', #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END
Solution:
declare #table table (QcEditorId int, Freelancer varchar (100))
insert into #table
select 21, '2,3,4,5'
declare #freelancer varchar(100)
= (select freelancer from #table)
select QcEditorId,x.Name from #table
cross apply(
select * from [dbo].[SplitString](#freelancer) ) x
DECLARE #FLXML AS XML
SET #FLXML = cast(('<a>'+replace(#FreelancerId,',' ,'</a><a>')
+'</a>') AS XML)
INSERT INTO [QMT_UserMaping](QcEditor_ID,Freelancer_ID)
SELECT #QCId,A.VALUE('.', 'varchar(max)')
FROM #FLXML.nodes('a') AS FN(a)
SQL User Defined Split Function
ALTER FUNCTION [dbo].[fnSplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX))
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
INSERT Command
INSERT INTO YourTable(QcEditorId,Freelancer)
SELECT 21,splitdata FROM [dbo].[fnSplitString]('2,3,4,5' ,',' )
STRING_SPLIT (MSSQL Server 2016)
no need custom user defined function
INSERT INTO YourTable(QcEditorId,Freelancer)
SELECT 21,value FROM STRING_SPLIT('2,3,4,5' ,',' )
I have a SQL Table like this:
| SomeID | OtherID | Data
+----------------+-------------+-------------------
| abcdef-..... | cdef123-... | 18,20,22
| abcdef-..... | 4554a24-... | 17,19
| 987654-..... | 12324a2-... | 13,19,20
Is there a query where I can perform a query like SELECT OtherID, SplitData WHERE SomeID = 'abcdef-.......' that returns individual rows, like this?
| OtherID | SplitData
+-------------+-------------------
| cdef123-... | 18
| cdef123-... | 20
| cdef123-... | 22
| 4554a24-... | 17
| 4554a24-... | 19
Basically split my data at the comma into individual rows?
create table Testdata(SomeID int, OtherID int, Data varchar(max))
insert Testdata select 1, 9, '18,20,22'
insert Testdata select 2, 8, '17,19'
insert Testdata select 3, 7, '13,19,20'
insert Testdata select 4, 6, ''
--The query
;with tmp(SomeID, OtherID, DataItem, Data) as (
select SomeID, OtherID, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from Testdata
union all
select SomeID, OtherID, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from tmp
where Data > ''
)
select SomeID, OtherID, DataItem
from tmp
order by SomeID
-- OPTION (maxrecursion 0)
-- normally recursion is limited to 100. If you know you have very long
-- strings, uncomment the option
Output
SomeID OtherID DataItem
1 9 18
1 9 20
1 9 22
2 8 17
2 8 19
3 7 13
3 7 19
3 7 20
4 6
9 11 1
9 11 2
9 11 3
9 11 4
etc.

sql query with substring charindex

table1
::::::::::::::::::::::::::::::::::
id | id_data | id_t | value
1 | 43 | 1 |
2 | 46 | 1 | 111,112,113
3 | 43 | 2 |
4 | 46 | 2 | 90,5
table2
:::::::::::::::::::::::::::::::::::
id_value | cat
112 | cat1
5 | cat2
Hi, I need some help here if possible, please.
I need to update table1.value where id_data is 43 with table2.cat where id_value = the digits after value's ',' 'till the next ',' if there is any for each group in 'id_t'
I tried with a simple query but it's returning some null but 'value' can't be null
update table1
set value = (select cat from table2
where convert(nvarchar,id_value) = substring(value,5,3))
where id_data='43'
I've been trying to incorporate CHARINDEX to take from the ',' but i just can't figure it out how it works.
Ideally it should look like this:
::::::::::::::::::::::::::::::::::
id | id_data | id_t | value
1 | 43 | 1 | cat1
2 | 46 | 1 | 111,112,113
3 | 43 | 2 | cat2
4 | 46 | 2 | 90,5
Can anyone point me on the right direction, please?
I guess it's simple.. but I'm still learning...
thanks in advance.
::::::::::::::::
UPDATE1
WITH UpdateableCTE AS
(
SELECT t1.id
,t1.id_data
,t1.id_t
,SecondNr
,(
SELECT t2.cat
FROM #table2 AS t2 WHERE t2.id_value=SecondNr
) AS NewCat
,t1.value
FROM #table1 AS t1
OUTER APPLY(SELECT CAST('<x>' + REPLACE(x.value,',','</x><x>') + '</x>' AS XML).value('/x[2]','int')
FROM #table1 AS x
WHERE x.id_t=t1.id_t AND x.value IS NOT NULL AND id_data='46') AS ID(SecondNr)
WHERE t1.value IS NULL
)
UPDATE UpdateableCTE SET value=NewCat;
--somehow where id_data='43'
I'll leave here one table that looks a little more to the real one with all the id_data and fields:
17974492 1 999251 somevalue
17974493 2 999251 somevalue
17974494 3 999251 somevalue
17974495 4 999251 somevalue
17974496 5 999251 somevalue
17974497 43 999251 (thishsouldbeupdated)
17974498 6 999251 somevalue
17974499 7 999251 somevalue
17974500 46 999251 111,311
17974501 8 999251 somevalue
17974502 9 999251 somevalue
17974503 10 999251 somevalue
17974504 11 999251 somevalue
17974505 12 999251 somevalue
17974506 13 999251 somevalue
17974507 1 999252 somevalue
17974508 2 999252 somevalue
17974509 3 999252 somevalue
17974510 4 999252 somevalue
17974511 5 999252 somevalue
17974512 43 999252 (thisshouldbeupdated)
17974513 6 999252 somevalue
17974514 7 999252 somevalue
17974515 46 999252 98,98
17974516 8 999252 somevalue
17974517 9 999252 somevalue
17974518 10 999252 somevalue
17974519 11 999252 somevalue
17974520 12 999252 somevalue
17974521 13 999252 somevalue
This is not clean, nor is it something I'd recommend, but you might find some help:
DECLARE #table1 TABLE(id INT,id_data INT,id_t INT,value VARCHAR(100));
INSERT INTO #table1 VALUES
(1,43,1,NULL)
,(2,46,1,'111,112,113')
,(3,43,2,NULL)
,(4,46,2,'90,5')
DECLARE #table2 TABLE(id_value INT,cat VARCHAR(100));
INSERT INTO #table2 VALUES
(112,'cat1')
,(5,'cat2');
SELECT t1.id
,t1.id_data
,t1.id_t
,ID.List
,(
SELECT t2.cat
FROM #table2 AS t2 WHERE CHARINDEX(',' + CAST(t2.id_value AS VARCHAR(100)) + ',',',' + ID.List + ',')>0
)
FROM #table1 AS t1
OUTER APPLY(SELECT x.value FROM #table1 AS x WHERE x.id_t=t1.id_t AND x.value IS NOT NULL) AS ID(List)
WHERE t1.value IS NULL
UPDATE: Your explanation about use the second number as update
Try this
DECLARE #table1 TABLE(id INT,id_data INT,id_t INT,value VARCHAR(100));
INSERT INTO #table1 VALUES
(1,43,1,NULL)
,(2,46,1,'111,112,113')
,(3,43,2,NULL)
,(4,46,2,'90,5')
DECLARE #table2 TABLE(id_value INT,cat VARCHAR(100));
INSERT INTO #table2 VALUES
(112,'cat1')
,(5,'cat2');
WITH UpdateableCTE AS
(
SELECT t1.id
,t1.id_data
,t1.id_t
,SecondNr
,(
SELECT t2.cat
FROM #table2 AS t2 WHERE t2.id_value=SecondNr
) AS NewCat
,t1.value
FROM #table1 AS t1
OUTER APPLY(SELECT CAST('<x>' + REPLACE(x.value,',','</x><x>') + '</x>' AS XML).value('/x[2]','int')
FROM #table1 AS x
WHERE x.id_t=t1.id_t AND x.value IS NOT NULL) AS ID(SecondNr)
WHERE t1.value IS NULL
)
UPDATE UpdateableCTE SET value=NewCat;
SELECT * FROM #table1
The concept is the updateable CTE, where you can use a normal SELECT to get the values you need. You then can update the derived table (as long as there are only columns of one single table affected) directly.
The OUTER APPLY uses a trick with XML to split the CSV list in order to read the second number.
UPDATE 2: Use your new sample data
The following will use the new sample data and work with the IDs:
DECLARE #table1 TABLE(id INT,id_data INT,id_t INT,value VARCHAR(100));
INSERT INTO #table1 VALUES
(17974492,1,999251,'somevalue')
,(17974493,2,999251,'somevalue')
,(17974494,3,999251,'somevalue')
,(17974495,4,999251,'somevalue')
,(17974496,5,999251,'somevalue')
,(17974497,43,999251,'(thishsouldbeupdated)')
,(17974498,6,999251,'somevalue')
,(17974499,7,999251,'somevalue')
,(17974500,46,999251,'111,311')
,(17974501,8,999251,'somevalue')
,(17974502,9,999251,'somevalue')
,(17974503,10,999251,'somevalue')
,(17974504,11,999251,'somevalue')
,(17974505,12,999251,'somevalue')
,(17974506,13,999251,'somevalue')
,(17974507,1,999252,'somevalue')
,(17974508,2,999252,'somevalue')
,(17974509,3,999252,'somevalue')
,(17974510,4,999252,'somevalue')
,(17974511,5,999252,'somevalue')
,(17974512,43,999252,'(thisshouldbeupdated)')
,(17974513,6,999252,'somevalue')
,(17974514,7,999252,'somevalue')
,(17974515,46,999252,'98,98')
,(17974516,8,999252,'somevalue')
,(17974517,9,999252,'somevalue')
,(17974518,10,999252,'somevalue')
,(17974519,11,999252,'somevalue')
,(17974520,12,999252,'somevalue')
,(17974521,13,999252,'somevalue');
DECLARE #table2 TABLE(id_value INT,cat VARCHAR(100));
INSERT INTO #table2 VALUES
(311,'cat1')
,(98,'cat2');
WITH UpdateableCTE AS
(
SELECT t1.id
,t1.id_data
,t1.id_t
,SecondNr
,(
SELECT t2.cat
FROM #table2 AS t2 WHERE t2.id_value=SecondNr
) AS NewCat
,t1.value
FROM #table1 AS t1
OUTER APPLY(SELECT CAST('<x>' + REPLACE(x.value,',','</x><x>') + '</x>' AS XML).value('/x[2]','int')
FROM #table1 AS x
WHERE x.id_t=t1.id_t AND x.id_data=46) AS ID(SecondNr)
WHERE t1.id_data=43
)
UPDATE UpdateableCTE SET value=NewCat;
SELECT * FROM #table1;