How to split a multi-line string into columns in tsql
Asked Answered
S

5

8

I have a database field that contains address information stored as multi-line strings.

88 Park View
Hemmingdale
London

Could anyone tell me the best way to get line 1, line 2 & line 3 as distinct fields in a select statement?

Regards Richard

Sheetfed answered 14/11, 2012 at 18:8 Comment(0)
M
7

Try something like this? Mind you this is bit vulnerable

DECLARE @S VARCHAR(500), @Query VARCHAR(1000)
SELECT @S='88 Park View
           Hemmingdale
           London'

SELECT @Query=''''+ REPLACE(@S, CHAR(13),''',''')+''''
EXEC('SELECT '+@Query)

Results

88 Park View | Hemmingdale | London
Milly answered 14/11, 2012 at 18:25 Comment(0)
R
5

Here is a solution I came up with using a CTE to recursively iterate a table of values, and split them out by new line CHAR(13), and then use a PIVOT to show the results. You can expand this to additional columns (more than 5) just by adding to these to the PIVOT.

DECLARE @Table AS TABLE(ID int, SomeText VARCHAR(MAX))

INSERT INTO @Table VALUES(1, '88 Park View
Hemmingdale
London')

INSERT INTO @Table VALUES(2, '100 Main Street
Hemmingdale
London')

INSERT INTO @Table VALUES(3, '123 6th Street
Appt. B
Hemmingdale
London')

;WITH SplitValues (ID, OriginalValue, SplitValue, Level)
AS
(
    SELECT  ID, SomeText, CAST('' AS VARCHAR(MAX)), 0 FROM @Table

    UNION ALL

    SELECT  ID
    ,   SUBSTRING(OriginalValue, CASE WHEN CHARINDEX(CHAR(13), OriginalValue) = 0 THEN LEN(OriginalValue) + 1 ELSE CHARINDEX(CHAR(13), OriginalValue) + 2 END, LEN(OriginalValue))
    ,   SUBSTRING(OriginalValue, 0, CASE WHEN CHARINDEX(CHAR(13), OriginalValue) = 0 THEN LEN(OriginalValue) + 1 ELSE CHARINDEX(CHAR(13), OriginalValue) END)
    ,   Level + 1
    FROM    SplitValues
    WHERE   LEN(SplitValues.OriginalValue) > 0
)

SELECT  ID, [1] AS Level1, [2] AS Level2, [3] AS Level3, [4] AS Level4, [5] AS Level5
FROM    (
    SELECT  ID, Level, SplitValue
    FROM    SplitValues
    WHERE   Level > 0
    ) AS p
PIVOT   (MAX(SplitValue) FOR Level IN ([1], [2], [3], [4], [5])) AS pvt

Results:

ID          Level1               Level2               Level3               Level4               Level5
----------- -------------------- -------------------- -------------------- -------------------- --------------------
1           88 Park View         Hemmingdale          London               NULL                 NULL
2           100 Main Street      Hemmingdale          London               NULL                 NULL
3           123 6th Street       Appt. B              Hemmingdale          London               NULL
Retribution answered 14/11, 2012 at 19:32 Comment(2)
Thanks @Jerad you just saved me a lot of timeGrogan
One of the best things I have come across on StackOverflow! I bow my head to a master.Croupier
F
0

Just for fun, updated with XML solution. This should handle '.' issue and can easily to extend to more lines.

DECLARE @xml XML , @S VARCHAR(500) = '88 Park View
Hemmingdale
London'

SET @xml  = CAST('<row><col>' + REPLACE(@S,CHAR(13),'</col><col>') + '</col></row>' AS XML)

SELECT  
     line.col.value('col[1]', 'varchar(1000)') AS line1
    ,line.col.value('col[2]', 'varchar(1000)') AS line2
    ,line.col.value('col[3]', 'varchar(1000)') AS line3
FROM    @xml.nodes('/row') AS line(col)
Farman answered 14/11, 2012 at 18:50 Comment(2)
Clever, but what about "88 Park View St."?Aculeus
@TimLehner, you are right. Maybe it's not a good idea to use '.' on address :)Farman
L
0

I know this is horrible but this way you have the 3 lines in 3 columns called l1,l2 and l3:

declare @input nvarchar(max) =  'line 1' + CHAR(13) + CHAR(10) + 'line 2' + CHAR(13) + CHAR(10) + 'line 3' + CHAR(13) + CHAR(10) + 'line 4' + CHAR(13) + CHAR(10) + 'line 5'
declare @delimiter nvarchar(10) = CHAR(13) + CHAR(10)

declare @outtable table (id int primary key identity(1,1), data nvarchar(1000) )

declare @pos int = 1
declare @temp nvarchar(1000)

if substring( @input, 1, len(@delimiter) ) = @delimiter
begin
    set @input = substring(@input, 1+len(@delimiter), len(@input))
end

while @pos > 0
begin

    set @pos = patindex('%' + @delimiter + '%', @input)

    if @pos > 0
        set @temp = substring(@input, 1, @pos-1)
    else
        set @temp = @input

    insert into @outtable ( data ) values ( @temp )

    set @input = substring(@input, @pos+len(@delimiter), len(@input))

end

select
    top 1 ISNULL(a.data,'') as l1, ISNULL(b.data,'') as l2, ISNULL(c.data,'') as l3
from
    @outtable a
        left outer join @outtable b on b.id = 2
        left outer join @outtable c on c.id = 3

sqlfiddle here

Loft answered 14/11, 2012 at 20:22 Comment(0)
V
0

It has been more than a decade and many things have been changed including the easiest way to do that in TSQL.

declare @data as nvarchar(max) = N'88 Park View
Hemmingdale
London';

select ordinal as LineNo_, value as perLine from string_split(@data, char(13),1);
Vouch answered 8/5, 2024 at 2:23 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.