How to split varchar variable to multiple rows and columns based on two delimeter

  • Dear All ,

    declare @var varchar(8000)

    set @var='Name1~50~20~50@Name2~25.5~50~63@Name3~30~80~43@Name4~60~80~23'

    ---------------------

    Create table #tmp(id int identity(1,1),Name varchar(20),Value1 float,Value2 float,Value3 float)

    Insert into #tmp (Name,Value1,Value2,Value3)

    Values ('Name1',50,20,50 ), ('Name2',25.5,50,63 ), ('Name3',30,80,43 ), ('Name4',60,80,23)

    select * from #tmp

    I want to convert to @var to same like #tmp table ..

    "@" - delimiter goes to rows

    "~" - delimiter goes to columns

    Kindly advise , Thanks in advance

  • Not very pretty, but it will work as long as you don't have any apostrophes in your string. There is a good chance that other inputs should also be handled, but I think that this can show you the direction of how to do it without looping through the string.

    declare @var varchar(8000)

    set @var='Name1~50~20~50@Name2~25.5~50~63@Name3~30~80~43@Name4~60~80~23'

    select @var = 'INSERT INTO #tmp (Name, Value1, Value2, Value3) Values (''' +

    replace(replace(@var,'~',''','''),'@', '''); INSERT INTO #tmp (Name, Value1, Value2, Value3) Values (''')

    + ''')'

    select @var

    ---------------------

    Create table #tmp(id int identity(1,1),Name varchar(20),Value1 float,Value2 float,Value3 float)

    --Insert into #tmp (Name,Value1,Value2,Value3)

    --Values ('Name1',50,20,50 ), ('Name2',25.5,50,63 ), ('Name3',30,80,43 ), ('Name4',60,80,23)

    exec (@var)

    select * from #tmp

    Adi

    --------------------------------------------------------------
    To know how to ask questions and increase the chances of getting asnwers:
    http://www.sqlservercentral.com/articles/Best+Practices/61537/

    For better answers on performance questions, click on the following...
    http://www.sqlservercentral.com/articles/SQLServerCentral/66909/

  • Hi , Thanks for you update.

    I am looking for like a function to insert data like below instead of creating insert statement in your query.

    select Name,Value1,Value2,Value3 from functionSplitter(@var, '@','~')

    Thanks,

  • s.chandrahasan (8/5/2015)


    Hi , Thanks for you update.

    I am looking for like a function to insert data like below instead of creating insert statement in your query.

    select Name,Value1,Value2,Value3 from functionSplitter(@var, '@','~')

    Thanks,

    You could easily turn this into a splitter function:

    DECLARE @var VARCHAR(8000)

    SET @var = 'Name1~50~20~50@Name2~25.5~50~63@Name3~30~80~43@Name4~60~80~23'

    ;WITH

    n1 AS (SELECT * FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d (n)),

    n2 AS (SELECT n = 0 FROM n1, n1 x),

    iTally AS (SELECT n = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM n2, n2 x)

    ,

    MyRowChopper AS ( -- find the position of each '@'

    SELECT

    Start = ISNULL(LAG(n,1) OVER(ORDER BY n),0),

    [End] = n,

    MyString

    FROM (SELECT MyString = @var) d

    INNER JOIN iTally t ON SUBSTRING(MyString,n,1) = '@'

    AND t.n <= LEN(@var)

    )

    SELECT

    --Start,

    --[End],

    MyString,

    r.MyRow,

    Col1 = SUBSTRING(r.MyRow,1,p1.n-1),

    Col2 = SUBSTRING(r.MyRow,p1.n+1,p2.n-p1.n-1),

    Col3 = SUBSTRING(r.MyRow,p2.n+1,p3.n-p2.n-1),

    Col4 = SUBSTRING(r.MyRow,p3.n+1,8000)

    FROM MyRowChopper

    CROSS APPLY (SELECT MyRow = SUBSTRING(MyString, start+1, [end]-start-1)) r

    CROSS APPLY (SELECT n = CHARINDEX('~',MyRow,1)) p1

    CROSS APPLY (SELECT n = CHARINDEX('~',MyRow,p1.n+1)) p2

    CROSS APPLY (SELECT n = CHARINDEX('~',MyRow,p2.n+1)) p3

    β€œWrite the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • ChrisM@Work (8/5/2015)


    s.chandrahasan (8/5/2015)


    Hi , Thanks for you update.

    I am looking for like a function to insert data like below instead of creating insert statement in your query.

    select Name,Value1,Value2,Value3 from functionSplitter(@var, '@','~')

    Thanks,

    You could easily turn this into a splitter function:

    DECLARE @var VARCHAR(8000)

    SET @var = 'Name1~50~20~50@Name2~25.5~50~63@Name3~30~80~43@Name4~60~80~23'

    ;WITH

    n1 AS (SELECT * FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d (n)),

    n2 AS (SELECT n = 0 FROM n1, n1 x),

    iTally AS (SELECT n = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM n2, n2 x)

    ,

    MyRowChopper AS ( -- find the position of each '@'

    SELECT

    Start = ISNULL(LAG(n,1) OVER(ORDER BY n),0),

    [End] = n,

    MyString

    FROM (SELECT MyString = @var) d

    INNER JOIN iTally t ON SUBSTRING(MyString,n,1) = '@'

    AND t.n <= LEN(@var)

    )

    SELECT

    --Start,

    --[End],

    MyString,

    r.MyRow,

    Col1 = SUBSTRING(r.MyRow,1,p1.n-1),

    Col2 = SUBSTRING(r.MyRow,p1.n+1,p2.n-p1.n-1),

    Col3 = SUBSTRING(r.MyRow,p2.n+1,p3.n-p2.n-1),

    Col4 = SUBSTRING(r.MyRow,p3.n+1,8000)

    FROM MyRowChopper

    CROSS APPLY (SELECT MyRow = SUBSTRING(MyString, start+1, [end]-start-1)) r

    CROSS APPLY (SELECT n = CHARINDEX('~',MyRow,1)) p1

    CROSS APPLY (SELECT n = CHARINDEX('~',MyRow,p1.n+1)) p2

    CROSS APPLY (SELECT n = CHARINDEX('~',MyRow,p2.n+1)) p3

    Very impressive

    Adi

    --------------------------------------------------------------
    To know how to ask questions and increase the chances of getting asnwers:
    http://www.sqlservercentral.com/articles/Best+Practices/61537/

    For better answers on performance questions, click on the following...
    http://www.sqlservercentral.com/articles/SQLServerCentral/66909/

  • s.chandrahasan (8/5/2015)


    Dear All ,

    declare @var varchar(8000)

    set @var='Name1~50~20~50@Name2~25.5~50~63@Name3~30~80~43@Name4~60~80~23'

    ---------------------

    Create table #tmp(id int identity(1,1),Name varchar(20),Value1 float,Value2 float,Value3 float)

    Insert into #tmp (Name,Value1,Value2,Value3)

    Values ('Name1',50,20,50 ), ('Name2',25.5,50,63 ), ('Name3',30,80,43 ), ('Name4',60,80,23)

    select * from #tmp

    I want to convert to @var to same like #tmp table ..

    "@" - delimiter goes to rows

    "~" - delimiter goes to columns

    Kindly advise , Thanks in advance

    Jeff Moden has written the definitive string splitter function, called DelimitedSplit8K, and it has it's own article on this site, where the words "Tally Oh" are in the title. Look up that article and read it, here[/url]:

    It can easily be used on your example data, and if you know the number of columns in advance, and generic column names are ok, you can use the following approach that makes use of that function:

    DECLARE @var varchar(8000) = 'Name1~50~20~50@Name2~25.5~50~63@Name3~30~80~43@Name4~60~80~23';

    WITH ALL_DATA AS (

    SELECT R.ItemNumber AS ROW_NUM, 'COL' + CAST(C.ItemNumber AS varchar(2)) AS COLUMN_NAME, C.Item AS Value

    FROM dbo.fnDelimitedSplit8K(@var, '@') AS R

    CROSS APPLY dbo.fnDelimitedSplit8K(R.Item, '~') AS C

    )

    SELECT ROW_NUM, [COL1], [COL2], [COL3], [COL4]

    FROM ALL_DATA

    PIVOT (MAX(Value) FOR COLUMN_NAME IN ([COL1], [COL2], [COL3], [COL4])) AS PVT

    Here's the results:

    ROW_NUMCOL1COL2COL3COL4

    ------- ------- ------- ------- -------

    1Name1502050

    2Name225.55063

    3Name3308043

    4Name4608023

    Steve (aka sgmunson) πŸ™‚ πŸ™‚ πŸ™‚
    Rent Servers for Income (picks and shovels strategy)

  • A slight twist on Steve's:

    declare @var varchar(8000)

    set @var='Name1~50~20~50@Name2~25.5~50~63@Name3~30~80~43@Name4~60~80~23'

    SELECT ds1.ItemNumber AS RowNumber,

    MAX(CASE WHEN ds2.ItemNumber = 1 THEN ds2.Item ELSE NULL END) AS Col1,

    MAX(CASE WHEN ds2.ItemNumber = 2 THEN ds2.Item ELSE NULL END) AS Col2,

    MAX(CASE WHEN ds2.ItemNumber = 3 THEN ds2.Item ELSE NULL END) AS Col3,

    MAX(CASE WHEN ds2.ItemNumber = 4 THEN ds2.Item ELSE NULL END) AS Col4

    FROM dbo.DelimitedSplit8K(@var, '@') ds1

    CROSS APPLY dbo.DelimitedSplit8K(ds1.Item, '~') ds2

    GROUP BY ds1.ItemNumber;

    Wayne
    Microsoft Certified Master: SQL Server 2008
    Author - SQL Server T-SQL Recipes


    If you can't explain to another person how the code that you're copying from the internet works, then DON'T USE IT on a production system! After all, you will be the one supporting it!
    Links:
    For better assistance in answering your questions
    Performance Problems
    Common date/time routines
    Understanding and Using APPLY Part 1 & Part 2

Viewing 7 posts - 1 through 6 (of 6 total)

You must be logged in to reply to this topic. Login to reply