I have an email column with 3-4 emails in each row which i want to split into one email per column:
Current columns looks like this:
Email_column
1. drone#gmail.com bob#yahoo.com drake#gmail.com
Expected output should be:
Email_1 Email_2 Email_3
1. drone#email.com bob#yahoo.com drake#gmail.com
With a CROSS APPLY and a little XML
Example
Declare #YourTable table (ID int,Email_column varchar(max))
Insert Into #YourTable values
(1,'drone#gmail.com bob#yahoo.com drake#gmail.com')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select Pos1 = n.value('/x[1]','varchar(max)')
,Pos2 = n.value('/x[2]','varchar(max)')
,Pos3 = n.value('/x[3]','varchar(max)')
,Pos4 = n.value('/x[4]','varchar(max)')
From (Select Cast('<x>' + replace(A.Email_column,' ','</x><x>')+'</x>' as xml) as n) X
) B
Returns
ID Pos1 Pos2 Pos3 Pos4
1 drone#gmail.com bob#yahoo.com drake#gmail.com NULL
Related
I have a bunch of rows that have a field called "name" that have values like this, delimited by a _:
ServerNumber_BrandNumber_BrandName_JobName
Sometimes, the job name will be spread out over two deliminations, like this:
ServerNumber_BrandNumber_BrandName_JobName_JobNamePart2
I want to break out each of those into their own field in a select statement like this:
SELECT
name[0] as ServerNumber,
name[1] as BrandNumber,
name[2] as BrandName,
name[3] as JobName
from table
If I do something like this it will work if job name is only part of one delmiter, but it will return nothing if it's using two:
REVERSE(PARSENAME(REPLACE(REVERSE(name), '_', '.'), 1))
How can I do all of this?
Working example
Declare #YourTable Table ([Name] varchar(150)) Insert Into #YourTable Values
('ServerNumber_BrandNumber_BrandName_JobName')
,('ServerNumber_BrandNumber_BrandName_JobName_JobNamePart2')
Select Pos1 = JSON_VALUE(JS,'$[0]')
,Pos2 = JSON_VALUE(JS,'$[1]')
,Pos3 = JSON_VALUE(JS,'$[2]')
,Pos4 = concat(JSON_VALUE(JS,'$[3]'),'_'+JSON_VALUE(JS,'$[4]'))
From #YourTable A
Cross Apply (values ('["'+replace(replace(string_escape([Name],'json'),' ','_'),'_','","')+'"]') ) B(JS)
Results
Pos1 Pos2 Pos3 Pos4
ServerNumber BrandNumber BrandName JobName
ServerNumber BrandNumber BrandName JobName_JobNamePart2
XML Approach (2012)
Select Pos1 = xDim.value('/x[1]','varchar(150)')
,Pos2 = xDim.value('/x[2]','varchar(150)')
,Pos3 = xDim.value('/x[3]','varchar(150)')
,Pos4 = concat(xDim.value('/x[4]','varchar(150)'),'_'+xDim.value('/x[5]','varchar(150)'))
From #YourTable A
Cross Apply ( values (cast('<x>' + replace((Select replace(NAME,'_','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml))) B(xDim)
I am trying to do this in SQL: data in column is like this need to separate them into new columns.
Create table #TEST4 ( NAME VARCHAR(25) )
INSERT INTO #TEST4
VALUES ( 'a,b,c,d,e')
,( 'ax,bde,c,ded,es')
select name from #TEST4
Expecting result like this, any suggestions will be appreciated.
enter image description here
Using a bit of JSON in concert with a CROSS APPLY
Select Name1 = JSON_VALUE(JS,'$[0]')
,Name2 = JSON_VALUE(JS,'$[1]')
,Name3 = JSON_VALUE(JS,'$[2]')
,Name4 = JSON_VALUE(JS,'$[3]')
From #TEST4 A
Cross Apply (values ( '["'+replace(string_escape(NAME,'json'),',','","')+'"]' ) ) B(JS)
Results
Name1 Name2 Name3 Name4
a b c d
ax bde c ded
Update: XML Approach
Select Name1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)'))) -- choose the proper datatype
,Name2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
,Name3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
,Name4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
From #Test4 A
Cross Apply ( values (cast('<x>' + replace((Select replace(NAME,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml))) B(xDim)
I've got a text field on a table that I'm trying to dissect into two separate columns in a select statement. I swear this worked for me last time I used it, but now it's throwing an error "Invalid length parameter". What am I doing wrong?
Splitting the data from a single column which is like this:
"CORP - DIVISION - REGION - TEAM - SUPERVISOR"
Into two columns like:
SUPERVISOR | TEAM
Here's what I had that I swear used to work, but it doesn't anymore and I can't figure it out!
Reverse(Left(Reverse(table.column),CHARINDEX(' ', Reverse(table.column))-1)) AS 'SUPERVISOR'
,LTRIM(LEFT(Substring(table.column,18,150),CHARINDEX(' - ', Substring(table.column,18,150))-1)) AS 'TEAM'
If you have a known or maximum number of items, consider a little XML. Perhaps a little easier to read and maintain.
Also, you could eliminate Pos1,Pos2,Pos3 if you are only interested in Team & Supervisor.
Example
Declare #YourTable Table ([ID] varchar(50),[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'CORP - DIVISION - REGION - TEAM - SUPERVISOR')
Select A.ID
,B.*
From #YourTable A
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)')))
,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(100)')))
,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(100)')))
,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(100)')))
From (Select Cast('<x>' + replace(SomeCol,'-','</x><x>')+'</x>' as xml) as xDim) as A
) B
Returns
ID Pos1 Pos2 Pos3 Pos4 Pos5 Pos6 Pos7 Pos8 Pos9
1 CORP DIVISION REGION TEAM SUPERVISOR NULL NULL NULL NULL
EDIT
If you have non XML safe characters (<,>,,...) use
...
From ( values (cast('<x>' + replace((Select replace(SomeCol,'-','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml))) A(xDim)
...
From SQL Server 2016 you can use STRING_SPLIT() to do it.
The STRING_SPLIT() can help normalize the data by splitting these
multi-valued columns.
I also used TRIM() function (introduced with SQL Server 2017) in order to remove the spaces, CTE, ROW_NUMBER() and PIVOT.
Below the script:
—-1 Create a test table
CREATE TABLE #TestTable
(
TestColumn varchar(100)
)
—-2 Inserting your string into table
INSERT INTO #TestTable
VALUES ('0 - CORP - DIVISION - REGION - TEAM - SUPERVISOR')
--3 Final query
;WITH CTE_Table AS (
SELECT
TestColumn = TRIM(TestColumn)
FROM
#TestTable
)
,CTE_Table2 AS (
SELECT
S.Value
FROM
CTE_Table
CROSS APPLY STRING_SPLIT([TestColumn],'-') AS S
)
,CTE_FinalTable AS (
SELECT TOP 5
Value
,ROW_NUMBER() OVER (ORDER BY Value) AS RowNumber
FROM
CTE_Table2
ORDER BY
Value
)
SELECT
[1],[2],[3],[4],[5]
FROM
CTE_FinalTable
PIVOT
(MAX([value])
The FOR [RowNumber] IN ([1],[2],[3],[4],[5])
) AS P
I'm trying to select the text between the second and third occurance of a delimeter (-) in SQL server.
For example, if I have the string aaa-bbbb-cccc-dddd I would like to return cccc, but I can't understand how to make a substring work when I have more than 2 of the delimeters.
Thanks for any help
If you always the same number of elements you could leverage PARSENAME like this.
select parsename(replace('aaa-bbbb-cccc-dddd', '-', '.'), 2)
But if your real data is not that consistent you need to use a real splitter.
If parsename() (+1) is not a valid option, perhaps a little XML.
Here are two illustrations, both return the same results
Example
Declare #YourTable table (SomeCol varchar(500))
Insert Into #YourTable values
('aaa-bbbb-cccc-dddd')
Select SomeCol
,Pos2 = cast('<x>' + replace(A.SomeCol,'-','</x><x>')+'</x>' as xml).value('/x[2]','varchar(50)')
,Pos3 = cast('<x>' + replace(A.SomeCol,'-','</x><x>')+'</x>' as xml).value('/x[3]','varchar(50)')
From #YourTable A
Select SomeCol
,B.*
From #YourTable A
Cross Apply (
Select Pos2 = XMLData.value('/x[2]','varchar(50)')
,Pos3 = XMLData.value('/x[3]','varchar(50)')
From (values (cast('<x>' + replace(A.SomeCol,'-','</x><x>')+'</x>' as xml))) B1(XMLData)
) B
Returns
SomeCol Pos2 Pos3
aaa-bbbb-cccc-dddd bbbb cccc
I am trying to separate strings into different columns which are separated by commas. I tried all the article that is on stackoverflow but not successful.
Example:
Column1
mouse,monitor,keyboard
cable,mouse
headset,desk,cable,monitor,usb,charger
Expected results:
Column1 |Column2 |Column3 |Column4 |Column5 |Column6
mouse |monitor |keyboard | NULL | NULL | NULL
cable |mouse |NULL | NULL | NULL | NULL
headset |desk |cable | monitor | usb | charger
Please note that the strings under Column1 can be as many as 10 strings and the strings are different every week so they are undefined.
This is one of the code I tried:
Declare #TblName (id int, Column1 varchar(max))
Insert into #TblName
Select A.Column1
,B.*
From #TblNameK A
Cross Apply (
Select Pos1 = xDim.value('/x[1]','varchar(max)')
,Pos2 = xDim.value('/x[2]','varchar(max)')
,Pos3 = xDim.value('/x[3]','varchar(max)')
,Pos4 = xDim.value('/x[4]','varchar(max)')
,Pos5 = xDim.value('/x[5]','varchar(max)')
,Pos6 = xDim.value('/x[6]','varchar(max)')
,Pos7 = xDim.value('/x[7]','varchar(max)')
,Pos8 = xDim.value('/x[8]','varchar(max)')
,Pos9 = xDim.value('/x[9]','varchar(max)')
From (Select Cast('<x>' + Replace(A.Column1,',','</x><x>')+'</x>' as XML) as xDim) A
) B
You can use XML method below :
DECLARE
#t TABLE (keywords VARCHAR(MAX) )
INSERT INTO #t VALUES
('mouse,monitor,keyboard'),
('cable,mouse'),
('headset,desk,cable,monitor,usb,charger'),
('M&M,Hot&Cold,sneakers')
SELECT
ROW_NUMBER() OVER(ORDER BY keywords DESC) ID
, keywords
FROM (
SELECT
LTRIM(RTRIM(m.n.value('.[1]','VARCHAR(8000)'))) keywords
FROM (
SELECT CAST('<Root><Keyword>' + REPLACE(REPLACE(keywords,'&','&') ,',','</Keyword><Keyword>') + '</Keyword></Root>' AS XML) keywords
FROM #t
) D
CROSS APPLY keywords.nodes('/Root/Keyword')m(n)
) C
This will put each keyword in a row. From there you can count the number of keywords and do further stuff on them (like getting the distinct values, pivot them ..etc).
Since you are using SQL Server 2016, you can use the built-in string_split() function:
declare #t table (Value varchar(max));
insert into #t (Value)
values
('mouse,monitor,keyboard'),
('cable,mouse'),
('headset,desk,cable,monitor,usb,charger')
;
select *
from #t t
cross apply string_split(t.Value, ',') ss;
Having all the values in one column will be especially handy if you are actually going to get some aggregated statistics out of them.