Reputation: 1670
As per answer in String Split into column instead of rows, I noticed that sometimes XmlEmail.value('/Emails[1]/email[1]','varchar(100)') AS Email1
returns multiple emails that are separated either by a space (\s) or by a semi-colon (;). This is the way they were inputed- nothing wrong with query.
I was trying to only take value before that space, so this is what I came up with:
CASE CHARINDEX(' ', LTRIM(XmlEmail.value('/Emails[1]/email[1]','varchar(100)')), 1)
WHEN 0 THEN LTRIM(XmlEmail.value('/Emails[1]/email[1]','varchar(100)'))
ELSE SUBSTRING(LTRIM(XmlEmail.value('/Emails[1]/email[1]','varchar(100)')), 1, CHARINDEX(' ',LTRIM(XmlEmail.value('/Emails[1]/email[1]','varchar(100)')), 1) - 1) end as Email_1
However, I can't do that because I also have cases with ';'
.
Any idea how can I implement both?
Upvotes: 0
Views: 93
Reputation: 82010
You can still use the XML approach. The only trick is to "Clean" the string first.
This little trick Gordon demonstrated several months ago. I can't find the original link, but it will reduce multiple spaces and/or semi-colons down to one (expand, reduce, and finally eliminates). Simply brilliant, only wish I could take credit for it.
Example
Declare @t table (Id INT, email_address VARCHAR(1000) , email_new VARCHAR(100));
INSERT INTO @t VALUES
(1,'[email protected]', '[email protected]'),
(2,'[email protected];;;; [email protected]', '[email protected]' ),
(3,'[email protected] [email protected]; [email protected]','[email protected]' )
Select A.ID
,C.*
From @t A
Cross Apply (values (replace(replace(replace(replace(email_address,' ','<>'),';','<>'),'><',''),'<>',';'))
) B(CleanString)
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
From (Select Cast('<x>' + replace(CleanString,';','</x><x>')+'</x>' as xml) as xDim) as A
) C
Returns
ID Pos1 Pos2 Pos3 Pos4 Pos5
1 [email protected] NULL NULL NULL NULL
2 [email protected] [email protected] NULL NULL NULL
3 [email protected] [email protected] [email protected] NULL NULL
Upvotes: 2
Reputation: 96003
Use PATINDEX
instead. With no data to test, I can't say this'll work, however, it'll probably be something like:
CASE PATINDEX('%[ ;]%', LTRIM(XmlEmail.value('/Emails[1]/email[1]','varchar(100)')))
WHEN 0 THEN LTRIM(XmlEmail.value('/Emails[1]/email[1]','varchar(100)'))
ELSE SUBSTRING(LTRIM(XmlEmail.value('/Emails[1]/email[1]','varchar(100)')), 1, PATINDEX('%[ ;]%',LTRIM(XmlEmail.value('/Emails[1]/email[1]','varchar(100)'))) - 1) end as Email_1
OP says this doesn't work, however, it does seem to work for me:
DECLARE @Email varchar(500) = '[email protected] [email protected]';
SELECT
CASE PATINDEX('%[ ;]%', LTRIM(@Email))
WHEN 0 THEN LTRIM(@Email)
ELSE SUBSTRING(LTRIM(@Email), 1, PATINDEX('%[ ;]%',LTRIM(@Email)) - 1) end as Email_1;
Returns: [email protected]
DECLARE @Email varchar(500) = '[email protected];[email protected]';
SELECT
CASE PATINDEX('%[ ;]%', LTRIM(@Email))
WHEN 0 THEN LTRIM(@Email)
ELSE SUBSTRING(LTRIM(@Email), 1, PATINDEX('%[ ;]%',LTRIM(@Email)) - 1) end as Email_1;
Also returns: [email protected]
I can't, however, run this against an xml, as (again), I don't have any sample data of that format from the OP.
Upvotes: 1