Trim the data and put in separate columns - sql

I have a below records in the table with single column .
**Name**
Aaa.bbb:ccc;ddd;eee;fff
Www.xxx:yyy;zzz;rrr;hhh
I am looking for a output something like this :
Name1 name2 name3 name4 name5 name6
Aaa bbb ccc ddd eee fff
Www xxx yyy zzz rrr hhh
Please help with a select query to accomplish this .
Thanks.

Perhaps something like this
Select A.[Name]
,C.*
From YourTable A
Cross Apply ( values ( replace(replace([Name],'.',';'),':',';') ))B(CleanString)
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(100)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(100)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(100)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(100)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(100)')))
,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(100)')))
From (Select Cast('<x>' + replace(CleanString,';','</x><x>')+'</x>' as xml) as xDim) as A
) C
Returns

You could also do a straight query with string methods like
select substring(name, 1, charindex('.', name) - 1) as name1,
substring(name, charindex('.', name) + 1, charindex(':', name) - charindex('.', name) -1) as name2,
substring(name, charindex(':', name) + 1, charindex(';', name) - charindex(':', name) -1) as name3,
substring(name, charindex(';', name) + 1, charindex(';', name,charindex(';', name) + 1) - charindex(';', name)-1) as name4,
substring(name, charindex(';', name,charindex(';', name) + 1) + 1, charindex(';', name,charindex(';', name,charindex(';', name) + 1) + 1) - charindex(';', name,charindex(';', name) + 1) -1) as name5,
substring(name, charindex(';', name,charindex(';', name,charindex(';', name) + 1) + 1) + 1, LEN(Name) - charindex(';', name,charindex(';', name,charindex(';', name) + 1) + 1) as name6
from YourTable
but then it still gets very complex.
The best option is to look for a different format to store your data if that is possible.

If you are using SQL Server 2016 or higher, you can use string_split for better performance.
Try the following:
;WITH cte AS(
SELECT [Name]
,value
,ROW_NUMBER() OVER(PARTITION BY [Name] ORDER BY (SELECT NULL)) as rn
FROM #t t
CROSS APPLY STRING_SPLIT(replace(replace([Name], '.', ';'), ':', ';'), ';') AS ss
)
SELECT
[1] AS Name1
,[2] AS Name2
,[3] AS Name3
,[4] AS Name4
,[5] AS Name5
,[6] AS Name6
FROM cte
PIVOT(
MAX(VALUE)
FOR RN IN([1],[2],[3],[4],[5],[6])
) as PVT
db<>fiddle here.
Please find more on performance here.

Related

Retrieve email ids from multiple full Email Address using SQL query

Using SQL-server
Am trying to retrieve email id from list of full email addresses
Example
Input
"ABCD"<abcd#gmail.com>;"TEST" <test#outlook.com>;
Needed output
abcd#gmail.com,test#outlook.com
i have tried the below query but it is giving only one email id not all.Some help will be greatly appreciated.
SELECT (CASE WHEN to_address LIKE '%<%'
THEN SUBSTRING(to_address, CHARINDEX('<',to_address)+1, CHARINDEX('>',to_address) - CHARINDEX('<',to_address)-1)
ELSE to_address
END) AS ToAddress
from test
In SQL Server, you can use string_split():
select replace(stuff(s.value, 1, charindex('<', s.value), ''), '>', '')
from t cross apply
string_split(t.list, ';') s
If you want to re-concatenate them:
select s.emails
from t cross apply
(select string_agg(replace(stuff(s.value, 1, charindex('<', s.value), ''), '>', ''), ';') as email
from string_split(t.list, ';') s
) s;
EDIT:
Another approach which works with earlier versions of SQL Server is a recursive CTE:
with cte as (
select convert(varchar(max), null) as email,
convert(varchar(max), emails) as rest,
convert(varchar(max), '') as output_emails, 0 as lev
from (values ('"ABCD"<abcd#gmail.com>;"TEST" <test#outlook.com>; ')
) v(emails)
union all
select replace(stuff(v.el, 1, charindex('<', v.el), ''), '>;', ''),
stuff(rest, 1, charindex(';', rest), ''),
concat(output_emails,
replace(stuff(v.el, 1, charindex('<', v.el), ''), '>;', ''),
';'),
lev + 1
from cte cross apply
(values (left(rest, charindex(';', rest)))) v(el)
where rest <> '' and lev < 5
)
select output_emails
from (select cte.*, row_number() over (order by lev desc) as seqnum
from cte
) cte
where seqnum = 1;
Here is a db<>fiddle.

How to achieve the following using functions/stored procedure in SQL Server database

I have the following table with following data as
Tab1
FutureMISBoundaryVersion CurrentMISBoundaryVersion FutureHAMBoundaryVersion CurrentHAMBoundaryVersion
2:21,5:50,4:55,7:80,9:33 2:12,5:40,4:35,7:60,9:87 2:52,5:90,4:75,7:30,9:57 2:42,5:60,4:95,7:70,9:37
This key value pair has to be split into and the value of each key has to be inserted into another table in the following fashion
FutureMIS-OAKVersion |FutureMIS-HAMVersion |FutureMIS-DURVersion | FutureMIS-BURVersion| FutureMIS-YRTVersion |DeviceMIS-OAKVersion|DeviceMIS-HAMVersion |DeviceMIS-DURVersion| DeviceMIS-BURVersion| DeviceMIS-YRTVersion
33 | 80 | 21 | 55 | 50 | 87 | 60 |12 |35 | 40
i,e: when it finds column 'FutureMISBoundaryVersion' in tab1 then its value
'2:21,5:50,4:55,7:80,9:33' will be split and its value is inserted in such a way that the corresponding value of key 2 i,e:21 will be inserted into FutureMIS-DURVersion column.
Similarly key 5's value 50 will be inserted into FutureMIS-BURVersion column and so on for other keys
when it finds column 'CurrentMISBoundaryVersion' then
'2:12,5:40,4:35,7:60,9:87' will be split and its value is inserted in such a way that the corresponding value of key 2 i,e:12 will be inserted into CurrentMIS-DURVersion column similarly key 5's 40 value will be inserted into DeviceMIS-YRTVersion column and so on for other columns of the source table.
The table structure may extend as I have shown only 4 source table column but logic for all the columns remain same
Very funky requirements to be honest.
Please note solution below will work only in SQL Server 2016+ as I'm using JSON to parse the data. However you can write your own parser, in this case code will work in almost all versions of SQL Server.
Parse function:
CREATE FUNCTION dbo.ParseIt(#Type NVARCHAR(255),#Value NVARCHAR(MAX))
RETURNS #Parsed TABLE (Code NVARCHAR(255),Value NVARCHAR(255))
AS
BEGIN
INSERT INTO #Parsed(Code,Value)
SELECT #Type + '-' + m.Code + 'Version' AS [Code],p.[1] AS [Value]
FROM (
SELECT j.[key] AS [ID],i.[key],i.value
FROM OPENJSON('["' + REPLACE(#Value,',','","') + '"]') j
CROSS APPLY OPENJSON('[' + REPLACE(j.[value],':',',') + ']') i
) a
PIVOT(MAX(a.value) FOR a.[key] IN ([0],[1])) p
INNER JOIN ( VALUES
(2,'DUR')
,(4,'BUR')
,(5,'YRT')
,(7,'HAM')
,(9,'OAK')
) m(ID, Code) ON m.ID = p.[0]
;
RETURN;
END
Initial data:
DECLARE #Table TABLE (FutureMISBoundaryVersion NVARCHAR(MAX), CurrentMISBoundaryVersion NVARCHAR(MAX),FutureHAMBoundaryVersion NVARCHAR(MAX),CurrentHAMBoundaryVersion NVARCHAR(MAX));
INSERT INTO #Table(FutureMISBoundaryVersion,CurrentMISBoundaryVersion,FutureHAMBoundaryVersion,CurrentHAMBoundaryVersion)VALUES
('2:21,5:50,4:55,7:80,9:33','2:12,5:40,4:35,7:60,9:87','2:52,5:90,4:75,7:30,9:57','2:42,5:60,4:95,7:70,9:37')
;
The code:
SELECT COALESCE(p.[FutureMIS-OAKVersion],'') AS [FutureMIS-OAKVersion]
,COALESCE(p.[FutureMIS-HAMVersion],'') AS [FutureMIS-HAMVersion]
,COALESCE(p.[FutureMIS-DURVersion],'') AS [FutureMIS-DURVersion]
,COALESCE(p.[FutureMIS-BURVersion],'') AS [FutureMIS-BURVersion]
,COALESCE(p.[FutureMIS-YRTVersion],'') AS [FutureMIS-YRTVersion]
,COALESCE(p.[DeviceMIS-OAKVersion],'') AS [DeviceMIS-OAKVersion]
,COALESCE(p.[DeviceMIS-HAMVersion],'') AS [DeviceMIS-HAMVersion]
,COALESCE(p.[DeviceMIS-DURVersion],'') AS [DeviceMIS-DURVersion]
,COALESCE(p.[DeviceMIS-BURVersion],'') AS [DeviceMIS-BURVersion]
,COALESCE(p.[DeviceMIS-YRTVersion],'') AS [DeviceMIS-YRTVersion]
FROM (
SELECT f.Code,f.Value FROM #Table t CROSS APPLY dbo.ParseIt('FutureMIS',t.FutureMISBoundaryVersion) f
UNION ALL
SELECT f.Code,f.Value FROM #Table t CROSS APPLY dbo.ParseIt('DeviceMIS',t.CurrentMISBoundaryVersion) f
) a
PIVOT(MAX(a.Value) FOR a.Code IN ([DeviceMIS-BURVersion],[DeviceMIS-DURVersion],[DeviceMIS-HAMVersion],[DeviceMIS-OAKVersion]
,[DeviceMIS-YRTVersion],[FutureMIS-BURVersion],[FutureMIS-DURVersion],[FutureMIS-HAMVersion],[FutureMIS-OAKVersion]
,[FutureMIS-YRTVersion])) p
;
The following query will parse the comma separated string 2:21,5:50,4:55,7:80,9:33 into individual component 2:21, 5:30 etc. From there you can use similar method to extract bb from aa:bb.
Since the key-value pair is in format aa:bb, you can use datepart(hour, 'aa:bb') and datepart(minute, 'aa:bb') to extract aa and bb
; with
Tab1 as
(
select val = '2:21,5:50,4:55,7:80,9:33'
)
select t.*, k1.k, k2.k, k3.k, k4.k, k5.k
from Tab1 t
cross apply
(
select i = charindex(',', t.val),
k = substring(t.val, 1, charindex(',', t.val + ',', 1) - 1)
) k1
cross apply
(
select i = charindex(',', t.val, k1.i + 1),
k = substring(t.val, k1.i + 1, charindex(',', t.val + ',', k1.i + 1) - k1.i - 1)
) k2
cross apply
(
select i = charindex(',', t.val, k2.i + 1),
k = substring(t.val, k2.i + 1, charindex(',', t.val + ',', k2.i + 1) - k2.i - 1)
) k3
cross apply
(
select i = charindex(',', t.val, k3.i + 1),
k = substring(t.val, k3.i + 1, charindex(',', t.val + ',', k3.i + 1) - k3.i - 1)
) k4
cross apply
(
select i = charindex(',', t.val, k4.i + 1),
k = substring(t.val, k4.i + 1, charindex(',', t.val + ',', k4.i + 1) - k4.i - 1)
) k5
This is a pain in SQL Server. You can do this with recursive CTEs:
with cte as (
select convert(varchar(max), left(FutureMISBoundaryVersion, charindex(',', FutureMISBoundaryVersion) - 1)) as FutureMISBoundaryVersion,
convert(varchar(max), left(CurrentMISBoundaryVersion, charindex(',', CurrentMISBoundaryVersion) - 1)) as CurrentMISBoundaryVersion,
convert(varchar(max), left(FutureHAMBoundaryVersion, charindex(',', FutureHAMBoundaryVersion) - 1)) as FutureHAMBoundaryVersion,
convert(varchar(max), left(CurrentHAMBoundaryVersion, charindex(',', FutureMISBoundaryVersion) - 1)) as CurrentHAMBoundaryVersion,
stuff(FutureMISBoundaryVersion, 1, charindex(',', FutureMISBoundaryVersion), '') + ',' as FutureMISBoundaryVersion_rest,
stuff(CurrentMISBoundaryVersion, 1, charindex(',', CurrentMISBoundaryVersion), '') + ',' as CurrentMISBoundaryVersion_rest,
stuff(FutureHAMBoundaryVersion, 1, charindex(',', FutureHAMBoundaryVersion), '') + ',' as FutureHAMBoundaryVersion_rest,
stuff(CurrentHAMBoundaryVersion, 1, charindex(',', CurrentHAMBoundaryVersion), '') + ',' as CurrentHAMBoundaryVersion_rest,
1 as lev
from t
union all
select convert(varchar(max), left(FutureMISBoundaryVersion_rest, charindex(',', FutureMISBoundaryVersion_rest) - 1)) as FutureMISBoundaryVersion,
convert(varchar(max), left(CurrentMISBoundaryVersion_rest, charindex(',', CurrentMISBoundaryVersion_rest) - 1)) as CurrentMISBoundaryVersion,
convert(varchar(max), left(FutureHAMBoundaryVersion_rest, charindex(',', FutureHAMBoundaryVersion_rest) - 1)) as FutureHAMBoundaryVersion,
convert(varchar(max), left(CurrentHAMBoundaryVersion_rest, charindex(',', CurrentHAMBoundaryVersion_rest) - 1)) as CurrentHAMBoundaryVersion,
stuff(FutureMISBoundaryVersion_rest, 1, charindex(',', FutureMISBoundaryVersion_rest), '') as FutureMISBoundaryVersion_rest,
stuff(CurrentMISBoundaryVersion_rest, 1, charindex(',', CurrentMISBoundaryVersion_rest), '') as CurrentMISBoundaryVersion_rest,
stuff(FutureHAMBoundaryVersion_rest, 1, charindex(',', FutureHAMBoundaryVersion_rest), '') as FutureHAMBoundaryVersion_rest,
stuff(CurrentHAMBoundaryVersion_rest, 1, charindex(',', CurrentHAMBoundaryVersion_rest), '') as CurrentHAMBoundaryVersion_rest,
lev + 1
from cte
where FutureMISBoundaryVersion_rest like '%,%'
)
select FutureMISBoundaryVersion, CurrentMISBoundaryVersion, FutureHAMBoundaryVersion, CurrentHAMBoundaryVersion, lev
from cte;
Here is a db<>fiddle.

How To Split Separate Strings in 2 Different Columns in SQL Server

I have 2 columns of pipe delimited data that I need to break out into rows but the columns must stay together. Here's what my data looks like:
Plan Name: ABC|DEF|GHI|JKL
Plan Type: HMO|POS|HMO|PPO
I need to end up with 4 rows that look like this:
1 - ABC HMO
2 - DEF POS
3 - GHI HMO
4 - JKL PPO
I know how to separate each column individually using the STUFF function but how do I keep the first value from column 1 with the first value from column 2, etc? Don't know where to start. Appreciate any help!
p.s. - I am not on SQL Server 2016 so can't use STRING_SPLIT
One method is a recursive CTE:
with t as (
select *
from (values ('ABC|DEF|GHI|JKL', 'HMO|POS|HMO|PPO')) v(plannames, plantypes)
),
cte as (
select convert(varchar(max), left(plannames, charindex('|', plannames + '|') - 1)) as planname,
convert(varchar(max), left(plantypes, charindex('|', plantypes + '|') - 1)) as plantype,
convert(varchar(max), stuff(plannames, 1, charindex('|', plannames + '|'), '')) as planname_rest,
convert(varchar(max), stuff(plantypes, 1, charindex('|', plantypes + '|'), '')) as plantype_rest,
1 as lev
from t
union all
select convert(varchar(max), left(planname_rest, charindex('|', planname_rest + '|') - 1)) as planname,
convert(varchar(max), left(plantype_rest, charindex('|', plantype_rest + '|') - 1)) as plantype,
convert(varchar(max), stuff(planname_rest, 1, charindex('|', planname_rest + '|'), '')) as planname_rest,
convert(varchar(max), stuff(plantype_rest, 1, charindex('|', plantype_rest + '|'), '')) as plantype_rest,
lev + 1
from cte
where planname_rest <> ''
)
select *
from cte;
Here is a db<>fiddle.
Using delimitedsplit8k_lead you could do:
SELECT CONVERT(varchar(3), itemnumber) + ' - ' + PN.item + ' ' + PT.item
FROM YourTable YT
CROSS APPLY dbo.delimitedsplit8k_lead(YT.PlanName,'|') PN
CROSS APPLY dbo.delimitedsplit8k_lead(YT.PlanType,'|') PT
WHERE PN.ItemNumber = PT.ItemNumber;
This assumes PlanName and PlanType have the same number of elements.

Cant cast in CTE

I'm curious as to why I get a syntax error for casting "tmp's" dataitem like so
;WITH tmp(date, CAST(dataitem AS VARCHAR(255)), data) AS
(
SELECT
date, LEFT(msg, CHARINDEX(';', msg + ';') - 1),
STUFF(msg, 1, CHARINDEX(';', msg + ';'), '')
FROM
DB1
WHERE
action LIKE 'FILE UPLOAD FTP'
AND date BETWEEN '06/01/2016' AND '07/05/2017'
UNION ALL
SELECT
date, CHARINDEX(';', data + ';'),
STUFF(data, 1, CHARINDEX(';', Data + ';'), '')
FROM
tmp
WHERE
data > ''
)
SELECT
date, dataitem,
REPLACE(SUBSTRING(dataitem, 1, CHARINDEX('|', dataitem) - 1), 'FTP UPLOAD: ', '') AS orig_file_name,
SUBSTRING(dataitem, CHARINDEX('|', dataitem) + 1, 8000) AS file_name,
(SELECT TOP 1 counts FROM DB1
WHERE action LIKE 'FILTER' AND date > tmp.date
AND msg LIKE SUBSTRING(tmp.dataitem, CHARINDEX('|', dataitem ) + 1, 8000) + '%'
ORDER BY date) AS filter_counts,
FROM
tmp
ORDER BY
date
Considering this works but will error due to a mismatching datatypes:
;WITH tmp(date, dataitem , data) AS
(
SELECT
date, LEFT(msg, CHARINDEX(';', msg + ';') - 1),
STUFF(msg, 1, CHARINDEX(';', msg + ';'), '')
FROM
DB1
WHERE
action LIKE 'FILE UPLOAD FTP'
AND date BETWEEN '06/01/2016' AND '07/05/2017'
UNION ALL
SELECT
date, CHARINDEX(';', data + ';'),
STUFF(data, 1, CHARINDEX(';', Data + ';'), '')
FROM
tmp
WHERE
data > ''
)
SELECT
date, dataitem,
REPLACE(SUBSTRING(dataitem, 1, CHARINDEX('|', dataitem) - 1), 'FTP UPLOAD: ', '') AS orig_file_name,
SUBSTRING(dataitem, CHARINDEX('|', dataitem) + 1, 8000) AS file_name,
(SELECT TOP 1 counts FROM DB1
WHERE action LIKE 'FILTER'
AND date> tmp.date
AND msg LIKE SUBSTRING(tmp.dataitem, CHARINDEX('|', dataitem ) + 1, 8000) + '%'
ORDER BY date) AS filter_counts,
FROM
tmp
ORDER BY
date
You can, of course, use CAST in a CTE, only in the SELECT list, not in the column list:
WITH tmp(date, dataitem , data) AS (
SELECT date,
CAST (LEFT(msg, CHARINDEX(';',msg+';')-1) AS VARCHAR(255)),
STUFF(msg, 1, CHARINDEX(';',msg+';'), '')
FROM DB1
...

sql split 5 values on delimiter

So i have this code that splits exactly 4 values on double delimiter , it works perfectly , but i cant get around how to split 5 values
Here is my string
#string = 'Value1|Value2|Value3|Value4|Value5, OtherVal1|OtherVal2|OtherVal3|OtherVal4|OtherVal5'
Here is code i have for spliting 4 values
WITH cte
AS (SELECT Replace(Rtrim(Ltrim(split.a.value('.', 'VARCHAR(100)'))), '|','.') AS split_data
FROM (SELECT Cast ('<M>' + Replace(#string, ',', '</M><M>')
+ '</M>' AS XML) AS Data) AS A
CROSS apply data.nodes ('/M') AS Split(a))
SELECT COLUMN2= Parsename(split_data, 4),
COLUMN3=Parsename(split_data, 3),
COLUMN4= Parsename(split_data, 2),
COLUMN5=Parsename(split_data, 1)
FROM cte
How do i split it on 5 vals ?
On the first look...
Seems, you have to use CTE twice. First one - to get "rows" (split data by ,) and the second one - to get "colums" (split data by |). Then you have to PIVOT data to display them in tabular form ;)
Have a look at example:
DECLARE #string VARCHAR(MAX) = 'Value1|Value2|Value3|Value4|Value5, OtherVal1|OtherVal2|OtherVal3|OtherVal4|OtherVal5'
;WITH MyRows AS
(
SELECT 1 AS RowNo, LEFT(#string, CHARINDEX(', ', #string)-1) AS MyVals, RIGHT(#string, LEN(#string) - CHARINDEX(', ', #string)) AS Remainder
WHERE CHARINDEX(', ', #string)>0
UNION ALL
SELECT RowNo + 1 AS RowNo, LEFT(Remainder, CHARINDEX(', ', Remainder)) AS MyVals, RIGHT(Remainder, LEN(Remainder) - CHARINDEX(', ', Remainder)) AS Remainder
FROM MyRows
WHERE CHARINDEX(', ', Remainder)>0
UNION ALL
SELECT RowNo + 1 AS RowNo, Remainder AS MyVals, NULL AS Remainder
FROM MyRows
WHERE CHARINDEX(', ', Remainder)=0
),
MyCols AS
(
SELECT RowNo, 1 AS ColNumber, LEFT(MyVals, CHARINDEX('|', MyVals)-1) AS SingleValue, RIGHT(MyVals, LEN(MyVals) - CHARINDEX('|', MyVals)) AS Remainder
FROM MyRows
WHERE CHARINDEX('|', MyVals)>0
UNION ALL
SELECT RowNo, ColNumber + 1 AS ColNumber, LEFT(Remainder, CHARINDEX('|', Remainder)-1) AS SingleValue, RIGHT(Remainder, LEN(Remainder) - CHARINDEX('|', Remainder)) AS Remainder
FROM MyCols
WHERE CHARINDEX('|', Remainder)>0
UNION ALL
SELECT RowNo, ColNumber + 1 AS ColNumber, Remainder AS SingleValue, NULL AS Remainder
FROM MyCols
WHERE CHARINDEX('|', Remainder)=0
)
SELECT RowNo, [1],[2],[3],[4],[5]
FROM (
SELECT ColNumber, SingleValue, RowNo
FROM MyCols ) AS DT
PIVOT(Max(SingleValue) FOR ColNumber IN([1],[2],[3],[4],[5])) AS PT
Result:
RowNo 1 2 3 4 5
1 Value1 Value2 Value3 Value4 Value5
2 OtherVal1 OtherVal2 OtherVal3 OtherVal4 OtherVal5