SQL Server 2012 Convert several comma delimited values to table rows/columns - sql

Currently, I have a table that is storing historical data in the following fashion. I have no control over this server or how the data is stored.
ID | FName | LName |Stuff| More
--------+---------+---------+-----+------
1,2,3,4 | j,p,g,r | l,m,h,s | ,,, | a,,b,
I need to get this data into a result set so that it is in the following format:
ID | FName | LName |Stuff| More
--------+---------+---------+-----+------
1 | j | l | | a
2 | p | m | |
3 | g | h | | b
4 | r | s | |
I would like to avoid using a function as I am unsure of the access I will have to the servers in other environments. I have tried using xml with cross apply, which I can get to work for a singular field, but I cannot seem to get the full table to work.
Any suggestions would be greatly appreciated,
Thanks
~JM

It can be done by recursive query
;WITH CTE(ID, ID_tmp, FName, FName_tmp, LName, LName_tmp, Stuf, Stuf_tmp, more, more_tmp)
AS
(
SELECT CAST(LEFT(ID, CHARINDEX(',',ID+',')-1) AS NVARCHAR(50)) ID,
STUFF(ID, 1, CHARINDEX(',',ID+','), '') ID_tmp,
CAST(LEFT(FName, CHARINDEX(',',FName+',')-1) AS NVARCHAR(50)) FName,
STUFF(FName, 1, CHARINDEX(',',FName+','), '') ID_tmp,
CAST(LEFT(LName, CHARINDEX(',',LName+',')-1) AS NVARCHAR(50)) LName,
STUFF(LName, 1, CHARINDEX(',',LName+','), '') LName_tmp,
CAST(LEFT(Stuf, CHARINDEX(',',Stuf+',')-1) AS NVARCHAR(50)) Stuf,
STUFF(Stuf, 1, CHARINDEX(',',Stuf+','), '') Stuf_tmp,
CAST(LEFT(more, CHARINDEX(',',more+',')-1) AS NVARCHAR(50)) more,
STUFF(more, 1, CHARINDEX(',',more+','), '') more_tmp
FROM TAB
UNION ALL
SELECT CAST(LEFT(ID_tmp, CHARINDEX(',',ID_tmp+',')-1) AS NVARCHAR(50)) ID,
STUFF(ID_tmp, 1, CHARINDEX(',',ID_tmp+','), '') ID_tmp,
CAST(LEFT(FName_tmp, CHARINDEX(',',FName_tmp+',')-1) AS NVARCHAR(50)) FName,
STUFF(FName_tmp, 1, CHARINDEX(',',FName_tmp+','), '') FName_tmp,
CAST(LEFT(LName_tmp, CHARINDEX(',',LName_tmp+',')-1) AS NVARCHAR(50)) LName,
STUFF(LName_tmp, 1, CHARINDEX(',',LName_tmp+','), '') LName_tmp,
CAST(LEFT(Stuf_tmp, CHARINDEX(',',Stuf_tmp+',')-1) AS NVARCHAR(50)) Stuf,
STUFF(Stuf_tmp, 1, CHARINDEX(',',Stuf_tmp+','), '') Stuf_tmp,
CAST(LEFT(more_tmp, CHARINDEX(',',more_tmp+',')-1) AS NVARCHAR(50)) more,
STUFF(more_tmp, 1, CHARINDEX(',',more_tmp+','), '') more_tmp
FROM CTE
WHERE ID_tmp > ''
)
SELECT ID, FName , LName, stuf, more
FROM CTE

If, you want to avoid use of UDF, then XML nodes() method still able to help you by using multiple CTE approach.
WITH cteId AS
(
SELECT Ids.value('.', 'INT') Id FROM
(
SELECT
cast('<x>'+replace(Id, ',', '</x><x>')+'</x>' as xml) as Id
FROM table t
)a CROSS APPLY Id.nodes ('/x') as split(Ids)
), ctefname AS
(
SELECT
row_number() over (order by (select 1)) Seq,
FNames.value('.', 'varchar') FNames FROM
(
SELECT
cast('<x>'+replace(FName, ',', '</x><x>')+'</x>' as xml) as FName
FROM table t
)a CROSS APPLY FName.nodes ('/x') as split(FNames)
), cteLname AS
(
SELECT
row_number() over (order by (select 1)) Seq,
LNames.value('.', 'varchar') LNames FROM
(
SELECT
cast('<x>'+replace(LName, ',', '</x><x>')+'</x>' as xml) as LName
FROM table t
)a CROSS APPLY LName.nodes ('/x') as split(LNames)
), ...
SELECT
id.Id, fn.FNames, ln.LNames, ...
FROM cteId id
INNER JOIN ctefname fn on fn.Seq = id.Id
INNER JOIN cteLname ln on ln.Seq = id.Id
...

Related

Put numbers for each rows in STRING_AGG in SQL Server

I have to put some columns and group them together by STRING_AGG also I want to put number first of each rows
What I have:
Name
Cake
Coca
ice-cream
Same
one
five
six
Sara
one
one
NULL
John
two
two
NULL
I want the output be something like this:
Name
Description
Sam
1.two 2.five 3. six
Sara
1.one 2.one
John
1.two 2.two
My Code:
SELECT Name, STRIN_AGG(CONCAT(Cake, ' ,', Coca,' ,', ice-cream))
FROM FoodTable
but I do not know how to consider numbers first of each rows in STRING_AGG
You don't need string_agg():
select name,
concat('1.' + cake, ' 2.' + coca, ' 3.' + ice_cream)
from t;
Note that + returns NULL if any value is NULL. However, concat() simply ignores NULL values.
If you really, really wanted to use string_agg() you could:
select t.name, v.all_together
from t cross apply
(select string_agg(v.n + v.val, ' ') within group (order by v.n) as all_together
from (values ('1.', t.cake),
('2.', t.coca),
('3.', t.ice_cream)
) v(n, val)
) v;
Here is a complete dynamic sql approach. No need to serialize and deserialize the data using XML or JSON. In this case the list of food items is contained in a temporary table so it reads the column names from tempdb.sys.columns.
The query uses CROSS APPLY to unpivot the columns (of food items) and assigns a ROW_NUMBER() to each non NULL item value. Something like this
drop table if exists #FoodTable;
go
create table #FoodTable(
[Name] varchar(100) not null,
Cake varchar(100) null,
Coca varchar(100) null,
[ice-cream] varchar(100) null);
--select * from dbo.test_actuals
insert #FoodTable values
('Sam', 'one', 'five', 'six'),
('Sara', 'one', 'one', null),
('Jon', 'two', 'two', null);
;with unpvt_cte([Name], item, val, rn) as (
select f.[Name], v.*, row_number() over (partition by [Name] order by (select null))
from #FoodTable f
cross apply (values ('Cake', Cake),
('Coca', Coca),
('IceCream', [ice-cream])) v(item, val)
where v.val is not null)
select [Name], string_agg(concat(rn, '.', val), ' ') within group (order by rn) answer
from unpvt_cte
group by [Name];
Name answer
Jon 1.two 2.two
Sam 1.one 2.five 3.six
Sara 1.one 2.one
to make the query dynamic
declare #food_list nvarchar(max);
select #food_list=string_agg(quotename(concat_ws(',', quotename(sysc.[name], ''''),
quotename(sysc.[name], '[]')), '()'), ',')
from tempdb.sys.columns sysc
where object_id = Object_id('tempdb..#FoodTable')
and [name]<>'Name';
declare
#sql_prefix nvarchar(max)=N'
;with unpvt_cte([Name], item, val, rn) as (
select f.[Name], v.*, row_number() over (partition by [Name] order by (select null))
from #FoodTable f
cross apply (values ',
#sql_suffix nvarchar(max)=N'
) v(item, val)
where v.val is not null)
select [Name], string_agg(concat(rn, ''.'', val), '' '') within group (order by rn) answer
from unpvt_cte
group by [Name];';
declare
#sql nvarchar(max)=concat(#sql_prefix, #food_list, #sql_suffix);
print(#sql);
exec sp_executesql #sql;
The print statement outputs the following
;with unpvt_cte([Name], item, val, rn) as (
select f.[Name], v.*, row_number() over (partition by [Name] order by (select null))
from #FoodTable f
cross apply (values ('Cake',[Cake]),('Coca',[Coca]),('ice-cream',[ice-cream])
) v(item, val)
where v.val is not null)
select [Name], string_agg(concat(rn, '.', val), ' ') within group (order by rn) answer
from unpvt_cte
group by [Name];
You may use a union to acquire the numbers for each column. Here I've used a cte but you could have used a subquery. Each query in the union renames the food type column to food and adds a column num that will be used in the final query. In the final query the where clause filters NULL foods and a group by with the string_agg and concat is used to retrieve the data in the desired format. I've included a working fiddle below:
WITH FoodTableNums AS (
SELECT Name, Cake as food, 1 as num FROM FoodTable UNION ALL
SELECT Name, Coca as food, 2 as num FROM FoodTable UNION ALL
SELECT Name, icecream as food, 3 as num FROM FoodTable
)
SELECT
Name,
STRING_AGG(CONCAT(num,'.', food),',' ) WITHIN GROUP( ORDER BY num asc) as Description
FROM
FoodTableNums
WHERE
food IS NOT NULL
GROUP BY
Name
Name
Description
John
1.two,2.two
Sam
1.one,2.five,3.six
Sara
1.one,2.one
db<>fiddle here
Let me know if this works for you.
Here is an option that is a bit more dynamic. You only have to Exclude certain columns ... in this case NAME
We use a bit of JSON to dynamically UNPIVOT the row, and then string_agg() to consolidate.
Example or dbFiddle
Select A.Name
,B.NewValue
From YourTable A
Cross Apply (
Select NewValue=STRING_AGG(concat(Seq,'.',Value),' ') within group (order by Seq)
From (
Select [Key]
,[Value]
,[Seq] = row_number() over (order by ##spid)
From OpenJson( (Select A.* For JSON Path,Without_Array_Wrapper ) )
Where [Key] not in ('Name')
) B1
) B
Results
Name NewValue
Same 1.one 2.five 3.six
Sara 1.one 2.one
John 1.two 2.two
Here is s generic way regardless of how many columns in a table.
It is based on XML/XQuery.
No need to UNPIVOT the rows, and then STRING_AGG() to consolidate.
All data in each row stays in a row.
SQL
-- DDL and data population, start
DECLARE #tbl table (
[Name] varchar(100) not NULL PRIMARY KEY,
Cake varchar(100) null,
Coca varchar(100) null,
[ice-cream] varchar(100) null);
INSERT #tbl VALUES
('Sam', 'one', 'five', 'six'),
('Sara', 'one', 'one', null),
('Jon', 'two', 'two', null);
-- DDL and data population, end
SELECT p.[Name]
, x.query('
for $r in /root/*[local-name()!="Name"]/text()
let $pos := count(root/*[. << $r]) - 1
return concat(string($pos), ".", $r)').value('text()[1]', 'VARCHAR(MAX)') AS Result
FROM #tbl AS p
CROSS APPLY (SELECT * FROM #tbl AS c
WHERE c.[Name] = p.[Name]
FOR XML PATH(''), TYPE, ROOT('root')) AS t(x);
Output
+------+--------------------+
| Name | Result |
+------+--------------------+
| Jon | 1.two 2.two |
| Sam | 1.one 2.five 3.six |
| Sara | 1.one 2.one |
+------+--------------------+
Although I agree with those saying that it would be much better to normalise your tables, but if you can't do that then this proposal makes GGordon's solution dynamic, building an SQL statement that retrieves all the columns on your FoodTable. No matter if they are 3 or 100 food columns.
CREATE TABLE FoodTable (
Name VARCHAR(4),
Cake VARCHAR(3),
Coca VARCHAR(4),
icecream VARCHAR(4)
);
INSERT INTO FoodTable ("Name", "Cake", "Coca", "icecream")
VALUES ('Sam', 'one', 'five', 'six'),
('Sara', 'one', 'one', NULL),
('John', 'two', 'two', NULL);
declare #SQL nvarchar(max);
WITH Food As (
SELECT ORDINAL_POSITION - 1 AS Num, COLUMN_NAME AS Food
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'FoodTable' AND COLUMN_NAME <> 'Name'
)
SELECT #SQL = N'WITH FoodTableNums AS ( ' +
string_agg('SELECT Name, ' + Food + ' as Food, ' + convert(varchar(20), Num) + ' as Num FROM FoodTable', ' UNION ALL ') +
') SELECT Name, STRING_AGG(CONCAT(num,''.'', food),'','' ) WITHIN GROUP( ORDER BY num asc) as Description FROM FoodTableNums WHERE food IS NOT NULL GROUP BY Name'
FROM Food;
EXECUTE sp_ExecuteSQL #SQL;
You can see it working here : Fiddle

Adding 'and' at the end of a comma separated list

Currently, I'm using the stuff function to create a comma separated list per each row.
x,y,z
What I want is to add commas for n-1 items in the list, with the final item being preceded by 'and'
x,y, and z.
For these purposes, just checking row number won't work because this list is being generated per unique Id, therefore I can't just iterate to the end of the table. Code below:
SELECT DISTINCT (sw.OwnerID)
,stuff((
SELECT DISTINCT ', ' + e.pn
FROM fct.enrtablev e
WHERE sw.OwnerID = e.OwnerId
FOR XML PATH('')), 1, 1, '') AS [Pet(s)]
A bit of a hack... AND string_agg() would be a better fit if 2017+
Here we use test the row_number() of the item count sum(1) over(), when equal this is the last item in the list
Example
Declare #YourTable table (OwnerID int,pn varchar(50))
Insert Into #YourTable values
(1,'X')
,(1,'Y')
,(1,'Z')
,(1,'Z')
,(2,'Apples')
Select Distinct
OwnerID
,stuff( ( Select case when row_number() over(order by pn) = nullif(sum(1) over() ,1)
then ', and '
else ', '
end + pn
FROM (Select distinct pn
From #YourTable
Where OwnerID = A.OwnerId
) e
Order By PN
For XML Path('')), 1, 2, '') AS [Pet(s)]
From #YourTable A
Returns
OwnerID Pet(s)
1 X, Y, and Z
2 Apples
XQUery and XML data model is based on ordered sequences. Exactly what we need.
Here is a simple solution based on XQuery and its FLWOR expression.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (OwnerID int, pn VARCHAR(50));
INSERT INTO #tbl (OwnerID, pn) VALUES
(1,'X'),
(1,'Y'),
(1,'Z'),
(2,'Apples');
-- DDL and sample data population, end
SELECT p.OwnerID
, (SELECT *
FROM #tbl AS c
WHERE c.OwnerID = p.OwnerID
FOR XML PATH('r'), TYPE, ROOT('root')
).query('
for $x in /root/r/pn/text()
return if ($x is (/root/r[last()]/pn/text())[1]) then
if (count(/root/r) gt 1) then concat("and ", $x) else string($x)
else concat($x, ",")
').value('.', 'VARCHAR(MAX)') AS Result
FROM #tbl AS p
GROUP BY p.OwnerID;
Output
+---------+----------------+
| OwnerID | Result |
+---------+----------------+
| 1 | X, Y, and Z |
| 2 | Apples |
+---------+----------------+
You can achieve this using ORDER BY and count.
Declare #YourTable table (OwnerID int,pn varchar(50))
Insert Into #YourTable values
(1,'X')
,(1,'Y')
,(1,'Z')
,(2,'Apples')
;WITH CTE_OwnerIdRank as
(
SELECT ownerid, pn, row_number() over (order by ownerId) as totalrn,
count(*) over(partition by ownerid order by ownerid) as ownercnt
from #yourtable
)
SELECT distinct OwnerId,
stuff((
SELECT ', ' + CASE WHEN c.totalrn = c.ownercnt then CONCAT(' and ',c.pn) else c.pn end
FROM CTE_OwnerIdRank as c
WHERE c.OwnerID = o.OwnerId
order by c.totalrn
FOR XML PATH('')), 1, 1, '') AS [Pet(s)]
from #yourtable as o
OwnerId
Pet(s)
1
X, Y, and Z
2
Apples

How to concatenate strings in SQL Server, and sort/ order by a different column?

I've seen many examples of concatenating strings in SQL Server, but if they worry about sorting, it's always by the column being concatenated.
I need to order the values based on data in a different fields.
Sample table:
ClassID | StudentName | SortOrder
-----------------------------
A |James |1
A |Janice |3
A |Leonard |2
B |Luke |2
B |Leia |1
B |Artoo |3
And the results I'd like to get are:
ClassID |StudentName
--------------------------------
A |James, Leonard, Janice
B |Leia, Luke, Artoo
How can this be done in SQL Server 2016?
(I'm looking forward to STRING_AGG in 2017, but we're not there yet...)
Thanks!
Here you go:
SELECT
s1.ClassID
, STUFF((SELECT
',' + s2.StudentName
FROM dbo.Student AS s2
WHERE s1.classID = s2.ClassID
ORDER BY s2.SortOrder
FOR XML PATH('')), 1, 1, '') AS StudentNames
FROM dbo.Student AS s1
GROUP BY s1.ClassID
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE MyTable(ClassID varchar(255),StudentName varchar(255),SortOrder int)
INSERT INTO MyTable(ClassID,StudentName,SortOrder)VALUES('A','James',1),('A','Janice',3),('A','Leonard',2),
('B','Luke',2),('B','Lela',1),('B','Artoo',3)
Query 1:
SELECT
t.ClassID
, STUFF((SELECT
',' + t1.StudentName
FROM MyTable t1
WHERE t.classID = t1.ClassID
ORDER BY t1.SortOrder
FOR XML PATH('')), 1, 1, '') AS StudentNamesConcat
FROM MyTable AS t
GROUP BY t.ClassID
Results:
| ClassID | StudentNamesConcat |
|---------|----------------------|
| A | James,Leonard,Janice |
| B | Lela,Luke,Artoo |
Here the query
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL DROP TABLE #Temp;
CREATE TABLE #Temp(ClassId varchar(10),studName varchar(100),SortOrder int)
INSERT INTO #Temp(ClassId , studName, SortOrder)
SELECT 'A','James',1 UNION ALL
SELECT 'A','Janice',3UNION ALL
SELECT 'A','Leonard',2 UNION ALL
SELECT 'B','Luke',2 UNION ALL
SELECT 'B','Leia',1 UNION ALL
SELECT 'B','Artoo',3
-- select * from #Temp
select
distinct
stuff((
select ',' + u.studName
from #Temp u
where u.studName = studName and U.ClassId = L.ClassId
order by u.SortOrder
for xml path('')
),1,1,'') as userlist,ClassId
from #Temp L
group by ClassId
You can try using PIVOT also. This will support even older version of SQL server.
Only limitation : you should know the maximum SortOrder value. Below code will work for SortOrder <=20 of any ClassID
SELECT ClassID, ISNULL([1],'') +ISNULL(', '+[2],'')+ISNULL(', '+[3],'')+ISNULL(', '+
[4],'')+ISNULL(', '+[5],'')+ISNULL(', '+[6],'')+ISNULL(', '+[7],'')+ISNULL(', '+
[8],'')+ISNULL(', '+[9],'')+ISNULL(', '+[10],'')+ISNULL(', '+[11],'')+ISNULL(', '+
[12],'')+ISNULL(', '+[13],'')+ISNULL(', '+[14],'')+ISNULL(', '+[15],'')+ISNULL(', '+
[16],'')+ISNULL(', '+[17],'')+ISNULL(', '+[18],'')+ISNULL(', '+[19],'')+ISNULL(', '+
[20],'') AS StudentName
FROM
(SELECT SortOrder,ClassID,StudentName
FROM [Table1] A
) AS SourceTable
PIVOT
(
MAX(StudentName)
FOR SortOrder IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20])
) AS PivotTable

SQL Server: Merge two values with regexp

I am using an SQL Server database and have these following tables
Table "Data"
--------------------------------------------------------------------------------------------------|
| Id |col_1_type | col_1_name | col_2_type | col_2_name | col_3_type | col_3_name |
--------------------------------------------------------------------------------------------------|
| 1 |KI | Inflation Rate | KI | Currency Rate | MI | Government Spending |
--------------------------------------------------------------------------------------------------|
And i just want to make my result to be like this:
+----+------------------------+
| Id | results |
+----+------------------------+
| 1 | KI-Inflation Rate |
| 2 | KI-Currency Rate |
| 3 | MI-Government Spending |
+----+------------------------+
The column name is mandatory though, thats what made it complicated i guess?
i know you can merge 2 values or concatenate it, but i'm stuck on the column name such as col_1_name and col_2_type. Do i need to use regexp maybe?
Please try this-
select ROW_NUMBER() OVER (ORDER BY (select null)) id , results
from
(
SELECT CONCAT(col_1_type,'-',col_1_name) results
FROM [Data]
UNION ALL
SELECT CONCAT(col_2_type,'-',col_2_name)
FROM Data
UNION ALL
SELECT CONCAT(col_3_type,'-',col_3_name)
FROM Data
)o
Or this also
SELECT Id,results
FROM Data
CROSS apply
(VALUES (CONCAT(col_1_type,'-',col_1_name),1),(CONCAT(col_2_type,'-',col_2_name),2)
,(CONCAT(col_3_type,'-',col_3_name) ,3) ) cs (results,Id)
I would use of cross apply with the help of CTE and stuff()
;with cte as
(
select a.* from table t
cross apply (
values
(t.col_1_type, 'col_1'),
(t.col_1_name, 'col_1'),
(t.col_2_type, 'col_2'),
(t.col_2_name, 'col_2'),
(t.col_3_type, 'col_3'),
(t.col_3_name, 'col_3')
) a(name, id)
)
select distinct stuff(
(select '-'+name from cte where id= c.id for xml path('')),
1,1, ''
) [Results],
from cte c
EDIT :
Not sure about Id column but my guess that could be resolved by using ranking function
select row_number() over (order by (select 1)) Id,
cc.Results from
(
select distinct stuff(
(select '-'+id from cte where name = c.name for xml path('')),
1,1, ''
) [Results]
from cte c
) cc
Result :
Id Results
1 KI-Currency Rate
2 KI-Inflation Rate
3 MI-Government Spending
Sample data
IF OBJECT_ID('tempdb..#t') iS NOT NULL
DROP TABLE #t
IF OBJECT_ID('dbo.temp','U') iS NOT NULL
DROP TABLE temp
;With CTe(
Id ,col_1_type , col_1_name, col_2_type , col_2_name, col_3_type , col_3_name )
AS
(
SELECT 1,'KI','Inflation Rate','KI','Currency Rate','MI','Government Spending'
)
SELECT * INTO temp FROM CTe
Using Dynamic sql
DECLARE #Sqlstring nvarchar(max)
,#SQlQuery nvarchar(max)
;WITH cte
AS
(
SELECT COLUMN_NAME ,
((ROW_NUMBER()OVER(ORDER BY (SELECT NULL))-1)/2 )+1 AS BatchSeq
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='temp' AND COLUMN_NAME<>'Id'
)
SELECT #Sqlstring=STUFF((SELECT ', '+COLUMN_NAME FROM
(
SELECT DISTINCT '('+STUFF((SELECT ', '+COLUMN_NAME
FROM cte i
WHERE i.BatchSeq=o.BatchSeq FOR XML PATH ('')),1,1,'') +')' AS COLUMN_NAME
FROM cte o
)dt
FOR XML PATH ('')),1,1,'')
SET #SQlQuery='
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS ID,
CONCAT(col_type,''-'',Col_names) AS Result
FROM Temp
CROSS APPLY ( VALUES '+#Sqlstring+') dt(col_type,Col_names)'
PRINT #SQlQuery
EXEC(#SQlQuery)
Result
ID Result
-----------------------
1 KI-Inflation Rate
2 KI-Currency Rate
3 MI-Government Spending
Try like this
select (column1 || ' '|| column2) from table;
or
SELECT tablename.col_1_type + ' ' + tablename.col_1_name AS results;

how to write SQL query for this result?

I have so many long database so I used seq_no in commas separate using more than one sequence store in single column but now I want all sequence in a single column so I am confused how to create this sql result for this.
For example:
TABLE STRUCTURE
SR_NO IS INT ,
SEQ_NO IS VARCHAR(MAX)
SR_NO SEQ_NO
---------------------------------
1 1839073,
2 1850097,1850098,
3 1850099,1850100,1850110
I need to get this result:
SEQ_NO
--------------
1839073
1850097
1850098
1850099
1850100
1850110
Thanks!
declare #t table(Id int,seq varchar(100))
insert into #t (Id,seq) values (1,'1839073,'),(2,'1839073,1850098,'),(3,'1850099,1850100,1850110 ')
;With Cte as (
SELECT A.Id,
Split.a.value('.', 'VARCHAR(100)') AS Seq
FROM
(
SELECT Id,
CAST ('<M>' + REPLACE(seq, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM #t
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a) )
Select ID,Seq from Cte Where Seq > ''
Try splitting it with XML
SELECT SR_NO, t.c.value('.', 'VARCHAR(2000)') COL1
FROM (
SELECT SR_NO, x = CAST('<t>' +
REPLACE(SEQ_NO, ',', '</t><t>') + '</t>' AS XML)
FROM
(values(1,'1839073'),(2, '1850097,1850098'),
(3, '1850099,1850100,1850110')) y(SR_NO, SEQ_NO)
) a
CROSS APPLY x.nodes('/t') t(c)
Result:
SR_NO COL1
1 1839073
2 1850097
2 1850098
3 1850099
3 1850100
3 1850110
You can replace this with your table:
(values (1,'1839073'),(2, '1850097,1850098'),
(3, '1850099,1850100,1850110')) y(SR_NO, SEQ_NO)
This should do it: (Replace YourTableName with your table name)
;WITH CTE(NEW_SEQ_NO, SEQ_NO) as (
SELECT LEFT(SEQ_NO, CHARINDEX(',',SEQ_NO + ',') -1),
STUFF(SEQ_NO, 1, CHARINDEX(',',SEQ_NO + ','), '')
FROM YourTableName
WHERE SEQ_NO <> '' AND SEQ_NO IS NOT NULL
UNION all
SELECT LEFT(SEQ_NO, CHARINDEX(',',SEQ_NO + ',') -1),
STUFF(SEQ_NO, 1, CHARINDEX(',',SEQ_NO + ','), '')
FROM CTE
WHERE SEQ_NO <> '' AND SEQ_NO IS NOT NULL
)
SELECT NEW_SEQ_NO from CTE ORDER BY NEW_SEQ_NO
You can check this topic for more information:
Turning a Comma Separated string into individual rows
I have written the following query after referring Turning a Comma Separated string into individual rows
It will work for you
create table STRUCTURE(SR_NO int, SEQ_NO varchar(max))
insert STRUCTURE select 1, '1839073,'
insert STRUCTURE select 2, '1850097,1850098,'
insert STRUCTURE select 3, '1850099,1850100,1850110'
;with tmp(SR_NO, DataItem, SEQ_NO) as (
select SR_NO, LEFT(SEQ_NO, CHARINDEX(',',SEQ_NO+',')-1),
STUFF(SEQ_NO, 1, CHARINDEX(',',SEQ_NO+','), '')
from STRUCTURE
union all
select SR_NO, LEFT(SEQ_NO, CHARINDEX(',',SEQ_NO+',')-1),
STUFF(SEQ_NO, 1, CHARINDEX(',',SEQ_NO+','), '')
from tmp
where SEQ_NO > ''
)
Select DataItem as SEQ_NO from tmp order by SEQ_NO;