Retrieve email ids from multiple full Email Address using SQL query - sql

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.

Related

Msg 240, Types don't match between the anchor and the recursive part in column

I am trying to split a row when the ';' character appears. I have a list with projects, where one of the columns states which district the project belongs to. However some projects appears in multiple districts and is therefore written like "1;2;3" (District 1, 2 and 3). I want to create three rows form this and split on the ';'.
The error message says:
Msg 240, Level 16, State 1, Line 13
Types don't match between the anchor and the recursive part in column "DataItem" of recursive query "tmp".
I tried the split_string, but discovered my server is 2014 and lacks compatibility.
WITH tmp(Oppdragsnr, Kommune, DataItem, Kommunenr) AS
(
SELECT
Oppdragsnr,
Kommune,
LEFT(Kommunenr, CHARINDEX(';', Kommunenr + ';') - 1),
STUFF(Kommunenr, 1, CHARINDEX(';', Kommunenr + ';'), '')
FROM
oppdragene
UNION all
SELECT
Oppdragsnr,
Kommune,
LEFT(Kommunenr, CHARINDEX(';', Kommunenr + ';') - 1),
STUFF(Kommunenr, 1, CHARINDEX(';', Kommunenr + ';'), '')
FROM
tmp
WHERE
Kommunenr > ''
)
SELECT
Oppdragsnr,
Kommune,
DataItem
FROM
tmp
ORDER BY
Oppdragsnr
I would like the output to be a new table with new rows for every project that appears in multiple districts.
You should probably CAST to INT the DataItem column in the base and recursive part of the query, the following query should work for you
;WITH tmp(Oppdragsnr, Kommune, DataItem, Kommunenr) AS
(
SELECT
Oppdragsnr,
Kommune,
CAST(LEFT(Kommunenr, CHARINDEX(';', Kommunenr + ';') - 1) AS INT),
STUFF(Kommunenr, 1, CHARINDEX(';', Kommunenr + ';'), '')
FROM
oppdragene
UNION all
SELECT
Oppdragsnr,
Kommune,
CAST(LEFT(Kommunenr, CHARINDEX(';', Kommunenr + ';') - 1) AS INT),
STUFF(Kommunenr, 1, CHARINDEX(';', Kommunenr + ';'), '')
FROM
tmp
WHERE
Kommunenr > ''
)
SELECT
Oppdragsnr,
Kommune,
DataItem
FROM
tmp
ORDER BY
Oppdragsnr

How to split names into first/middle/last if there are people who typed last/first/middle? Order known

I am trying to split names into first, middle, last, based on an indicated order. I am at a lost on how to do this, and any help would be much appreciated. I am using sql server 2008 for work.
I attached an example dataset and the ideal dataset I would like to create.
ID ORDER NAME
1 first, middle, last Bruce, Batman, Wayne
2 middle, last, first Superman, Kent, Clark
3 last, first, middle Prince, Diana, Wonderwoman
INTO:
ID ORDER NAME
1 first Bruce
1 middle Batman
1 last Wayne
2 middle Superman
2 last Kent
2 first Clark
3 last Prince
3 first Diana
3 middle Wonderwoman
SQL Server does not have very good string processing functions. You can do this using a recursive CTE, though:
with cte as (
select id,
convert(varchar(max), left(ord, charindex(',', ord) - 1)) as ord,
convert(varchar(max), left(name, charindex(',', name) - 1)) as name,
convert(varchar(max), stuff(ord, 1, charindex(',', ord) + 1, '')) as ord_rest,
convert(varchar(max), stuff(name, 1, charindex(',', name) + 1, '')) as name_rest,
1 as lev
from t
union all
select id,
convert(varchar(max), left(ord_rest, charindex(',', ord_rest + ',') - 1)) as ord,
convert(varchar(max), left(name_rest, charindex(',', name_rest + ',') - 1)) as name,
convert(varchar(max), stuff(ord_rest, 1, charindex(',', ord_rest + ',') + 1, '')) as ord_rest,
convert(varchar(max), stuff(name_rest, 1, charindex(',', name_rest + ',') + 1, '')) as name_rest,
lev + 1
from cte
where ord_rest <> '' and lev < 10
)
select id, ord, name
from cte
order by id, lev
Here is a db<>fiddle.
With the help of a parse/split function that returns the sequence, this becomes a small matter using a CROSS APPLY
Example
Select A.ID
,B.*
From YourTable A
Cross Apply (
Select [Order] = B1.RetVal
,[Name] = B2.RetVal
From [dbo].[tvf-Str-Parse]([ORDER],',') B1
Join [dbo].[tvf-Str-Parse]([NAME] ,',') B2 on B1.RetSeq=B2.RetSeq
) B
Returns
ID Order Name
1 first Bruce
1 middle Batman
1 last Wayne
2 middle Superman
2 last Kent
2 first Clark
3 last Prince
3 first Diana
3 middle Wonderwoman
The Function 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 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From ( values (cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.'))) as A(x)
Cross Apply x.nodes('x') AS B(i)
);
I found the other answers a bit hard to follow - they're neat tricks for sure but I think anyone coming to maintain them might be like "whaaaat?". Here I work out the indexes of the commas (the first comma string index goes in o1/n1, the second comma goes in o2/n2) in the first cte, cut the string up (substring between 1 and first comma, substring between first and second comma, substring after third comma) in the second cte and then use a couple of unions to turn the results from 7 columns into 3
WITH idxs AS
(
SELECT
id,
order,
name,
CHARINDEX(',', [order]) as o1,
CHARINDEX(',', [order], CHARINDEX(',', [order]) + 1) as o2,
CHARINDEX(',', name) as n1,
CHARINDEX(',', name, CHARINDEX(',', name) + 1) as n2
FROM
t
),
cuts as (
SELECT
id,
SUBSTRING([order], 1, o1-1) as ord1,
SUBSTRING([order], o1+1, o2-o1-1) as ord2,
SUBSTRING([order], o2+1, 4000) as ord3,
SUBSTRING(name, 1, n1-1) as nam1,
SUBSTRING(name, n1+1, n2-n1-1) as nam2,
SUBSTRING(name, n2+1, 4000) as nam3
FROM
idxs
)
SELECT id, ord1 as [order], nam1 as name FROM cuts
UNION ALL
SELECT id, ord2, nam2 FROM cuts
UNION ALL
SELECT id, ord3, nam3 FROM cuts
Note that if your data sometimes has spaces in and sometimes does not you'll benefit from using either LTRIM/RTRIM in the output
if the spaces are always there after a comma, you could also adjust the substring indexes to cut the spaces out (any start index that is x+1 would be x+2 and the length would hence have to be -2)

SQL Server : to get last 4 character in first column and get the first letter of the words in 2nd column but ignore non alphabets

Can I check will it be possible to run SQL with this requirement? I trying to get a new value for new column from these 2 existing columns ID and Description.
For ID, simply retrieve last 4 characters
For Description, would like to get the first alphabets for each word but ignore the numbers & symbols.
SQL Server has lousy string processing capabilities. Even split_string() doesn't preserve the order of the words that it finds.
One approach to this uses a recursive CTE to split the strings and accumulate the initials:
with t as (
select v.*
from (values (2004120, 'soccer field 2010'), (2004121, 'ruby field')) v(id, description)
),
cte as (
select id, description, convert(varchar(max), left(description, charindex(' ', description + ' '))) as word,
convert(varchar(max), stuff(description, 1, charindex(' ', description + ' ') , '')) as rest,
1 as lev,
(case when description like '[a-zA-Z]%' then convert(varchar(max), left(description, 1)) else '' end) as inits
from t
union all
select id, description, convert(varchar(max), left(rest, charindex(' ', rest + ' '))) as word,
convert(varchar(max), stuff(rest, 1, charindex(' ', rest + ' ') , '')) as rest,
lev + 1,
(case when rest like '[a-zA-Z]%' then convert(varchar(max), inits + left(rest, 1)) else inits end) as inits
from cte
where rest > ''
)
select id, description, inits + right(id, 4)
from (select cte.*, max(lev) over (partition by id) as max_lev
from cte
) cte
where lev = max_lev;
Here is a db<>fiddle.
To get the last 4 numbers of the ID you could use:
SELECT Id%10000 as New_Id from Tablename;
To get the starting of each Word you could use(letting the answer be String2):
LEFT(Description,1)
This is equivalent to using SUBSTRING(Description,1,1)
This helps you get the first letter of each word.
To concatenate both of them you could use the CONCAT function:
SELECT CONCAT(String2,New_Id)
See more on the CONCAT function here

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.

How to combine SQL queries for same column?

I have searched but not found any examples for my particular problem.
I am trying to strip some unwanted text from a column containing department names. I am trying to combine 2 queries to do this.
This first query strips all characters after the colon in the name:
SELECT
CASE WHEN CHARINDEX(':', DB.Table.DEPT)>0
THEN
LEFT(DB.Table.DEPT, CHARINDEX(':', DB.Table.DEPT)-1)
ELSE
DB.Table.DEPT
END
FROM
DB.Table
The second query strips the prefix from the name:
SELECT
REPLACE(
REPLACE(
REPLACE (DB.Table.DEPT,'[NA1] ','')
,'[NA2] ', '')
,'[NA3] ', '')
FROM
DB.Table
Both of these work great independent of each other, but when I try to combine them it fails.
SELECT
CASE WHEN CHARINDEX(':', DB.Table.DEPT)>0
THEN
LEFT(DB.Table.DEPT, CHARINDEX(':', DB.Table.DEPT)-1)
ELSE
DB.Table.DEPT
END
FROM
(SELECT
REPLACE(
REPLACE(
REPLACE (DB.Table.DEPT,'[NA1] ','')
,'[NA2] ', '')
,'[NA3] ', '')
FROM
DB.Table)
I could really use some guidance with this.
Thanks in advance.
Your query is syntactically incorrect, because you need an alias for the subquery and for the expression result:
SELECT (CASE WHEN CHARINDEX(':', DEPT)>0
THEN LEFT(DEPT, CHARINDEX(':', DEPT)-1)
ELSE DEPT
END)
FROM (SELECT REPLACE(REPLACE(REPLACE(t.DEPT,'[NA1] ',''
), '[NA2] ', ''
), '[NA3] ', ''
) as DEPT
FROM DB.Table t
) t;
EDIT:
To see both the original and new department:
SELECT (CASE WHEN CHARINDEX(':', new_DEPT) > 0
THEN LEFT(new_DEPT, CHARINDEX(':', newj_DEPT)-1)
ELSE new_DEPT
END),
Orig_DEPT
FROM (SELECT REPLACE(REPLACE(REPLACE(t.DEPT,'[NA1] ',''
), '[NA2] ', ''
), '[NA3] ', ''
) as new_DEPT,
t.DEPT as orig_DEPT
FROM DB.Table t
) t
You should always name your subquerys.
Try this:
SELECT
CASE WHEN CHARINDEX(':', x.DEPT)>0
THEN
LEFT(x.DEPT, CHARINDEX(':', x.DEPT)-1)
ELSE
x.DEPT
END AS DEPT
FROM
(
SELECT
REPLACE(REPLACE(REPLACE (DEPT,'[NA1] ','') ,'[NA2] ', ''),'[NA3] ', '') AS DEPT
FROM
DB.Table
) x