Reputation: 41
I am looking to split a string into words in columns in SQL Server 2014. I have found a few solutions but all of them are giving the results in rows. How can I break the below string into columns?
"First Second Third Fourth Fifth"
Upvotes: 0
Views: 2512
Reputation: 6622
You can use a SQL split string function to seperate the string into words and using the order of the word in the original string, you can use CASE statements like a PIVOT query and display as columns
Here is a sample
declare @string varchar(max) = 'First Second Third Fourth Fifth'
;with cte as (
select
case when id = 1 then val end as Col1,
case when id = 2 then val end as Col2,
case when id = 3 then val end as Col3,
case when id = 4 then val end as Col4,
case when id = 5 then val end as Col5
from dbo.split( @string,' ')
)
select
max(Col1) as Col1,
max(Col2) as Col2,
max(Col3) as Col3,
max(Col4) as Col4,
max(Col5) as Col5
from cte
If you cannot create a UDF, you can use the logic in your SQL code as follows
Please note that if you have your data in a database table column, you can simply replace column content in the first SQL CTE expression
declare @string varchar(max) = 'First Second Third Fourth Fifth'
;with cte1 as (
select convert(xml, N'<root><r>' + replace(@string,' ','</r><r>') + '</r></root>') as rawdata
), cte2 as (
select
ROW_NUMBER() over (order by getdate()) as id,
r.value('.','varchar(max)') as val
from cte1
cross apply rawdata.nodes('//root/r') as records(r)
)
select
max(Col1) as Col1,
max(Col2) as Col2,
max(Col3) as Col3,
max(Col4) as Col4,
max(Col5) as Col5
from (
select
case when id = 1 then val end as Col1,
case when id = 2 then val end as Col2,
case when id = 3 then val end as Col3,
case when id = 4 then val end as Col4,
case when id = 5 then val end as Col5
from cte2
) t
Upvotes: 2
Reputation: 67341
You can use XML and grab the elements by their position:
DECLARE @YourString VARCHAR(100)='First Second Third Fourth Fifth';
WITH StringAsXML AS
(
SELECT CAST('<x>' + REPLACE((SELECT @YourString AS [*] FOR XML PATH('')),' ','</x><x>') + '</x>' AS XML) TheXml
)
SELECT TheXml.value('x[1]/text()[1]','nvarchar(max)') AS FirstElement
,TheXml.value('x[2]/text()[1]','nvarchar(max)') AS SecondElement
,TheXml.value('x[3]/text()[1]','nvarchar(max)') AS ThirdElement
,TheXml.value('x[4]/text()[1]','nvarchar(max)') AS FourthElement
,TheXml.value('x[5]/text()[1]','nvarchar(max)') AS FifthElement
FROM StringAsXML;
You can use PIVOT
, conditional aggregation, FROM(VALUES())
or the above. but any of these approaches will need a known set of columns (a known count of elements or at least a maximum count of elements).
If you cannot rely on such a knowledge, you can use dynamically created SQL. This would mean to create one of the working statements on string base and use EXEC
for a dynamic execution.
This approach will deal with a variable number of elements
DECLARE @YourString VARCHAR(100)='First Second Third Fourth Fifth';
DECLARE @Delimiter CHAR(1)=' ';
DECLARE @countElements INT = LEN(@YourString)-LEN(REPLACE(@YourString,@Delimiter,''));
DECLARE @Statement VARCHAR(MAX)=
'WITH StringAsXML AS
(
SELECT CAST(''<x>'' + REPLACE((SELECT ''ReplaceYourString'' AS [*] FOR XML PATH('''')),'' '',''</x><x>'') + ''</x>'' AS XML) TheXml
)
SELECT ReplaceColumnList
FROM StringAsXML;';
DECLARE @columnList VARCHAR(MAX);
WITH cte AS
(
SELECT 1 AS ElementCounter
,CAST('TheXml.value(''x[1]/text()[1]'',''nvarchar(max)'') AS Element_01' AS VARCHAR(MAX)) AS ColStatement
UNION ALL
SELECT cte.ElementCounter+1
,cte.ColStatement + CAST(',TheXml.value(''x[' + CAST(cte.ElementCounter+1 AS VARCHAR(10)) + ']/text()[1]'',''nvarchar(max)'') AS Element_' + REPLACE(STR(cte.ElementCounter + 1,2),' ','0') AS VARCHAR(MAX))
FROM cte
WHERE cte.ElementCounter <= @countElements
)
SELECT @columnList=(SELECT TOP 1 cte.ColStatement FROM cte ORDER BY cte.ElementCounter DESC)
--replace the string you want to split
SET @Statement = REPLACE(@Statement,'ReplaceYourString',@YourString);
--replace the columnList
SET @Statement = REPLACE(@Statement,'ReplaceColumnList',@columnList);
EXEC(@Statement);
Try this out:
DECLARE @inp VARCHAR(200) = 'First Second Third Fourth Fifth';
DECLARE @dlmt VARCHAR(100)=' ';
;WITH
a AS (SELECT n=0, i=-1, j=0 UNION ALL SELECT n+1, j, CHARINDEX(@dlmt, @inp, j+1) FROM a WHERE j > i),
b AS (SELECT n, SUBSTRING(@inp, i+1, IIF(j>0, j, LEN(@inp)+1)-i-1) s FROM a WHERE i >= 0)
SELECT * FROM b;
And just to get it complete: The above tiny splitter combined with PIVOT
:
;WITH
a AS (SELECT n=0, i=-1, j=0 UNION ALL SELECT n+1, j, CHARINDEX(@dlmt, @inp, j+1) FROM a WHERE j > i),
b AS (SELECT n, SUBSTRING(@inp, i+1, IIF(j>0, j, LEN(@inp)+1)-i-1) s FROM a WHERE i >= 0)
SELECT p.*
FROM b
PIVOT(MAX(s) FOR n IN([1],[2],[3],[4],[5])) p;
Upvotes: 2
Reputation: 65408
You may use parsename
function as :
create table tab ( str varchar(100));
insert into tab values('First Second Third Fourth Fifth');
with t as
(
select replace(str,' ','.') as str
from tab
)
Select substring(str,1,charindex('.',str)-1) as col_first,
parsename(substring(str,charindex('.',str)+1,len(str)),4) as col_second,
parsename(substring(str,charindex('.',str)+1,len(str)),3) as col_third,
parsename(substring(str,charindex('.',str)+1,len(str)),2) as col_fourth,
parsename(substring(str,charindex('.',str)+1,len(str)),1) as col_fifth
from t;
col_first col_second col_third col_fourth col_fifth
--------- ---------- --------- ---------- ---------
First Second Third Fourth Fifth
P.S. firstly, need to split the main string into the parts with at most 3 three dot(.
) character(otherwise the function doesn't work). It's a restriction for parsename
.
Upvotes: 0