sql server split comma separated values into columns

前端 未结 3 1007
青春惊慌失措
青春惊慌失措 2020-12-19 07:30

I trying to split the csv to individual columns

SAMPLE DATA

PAR_COLUMN  PERIOD  VALUE       mul_query
----------  ------  ---------         


        
相关标签:
3条回答
  • 2020-12-19 08:16

    Dynamically solve this problem, use DSQL to add more columns in the result accordingly.

    --create split function
    CREATE FUNCTION [dbo].[SO_Split]
    (
        @List nvarchar(2000),
        @SplitOn nvarchar(5)
    ) 
    RETURNS @RtnValue table
    (
    
        Id int identity(1,1),
        Value nvarchar(100)
    )
    AS 
    BEGIN
    While (Charindex(@SplitOn,@List)>0)
    Begin
    Insert Into @RtnValue (value)
    Select
        Value = ltrim(rtrim(Substring(@List,1,Charindex(@SplitOn,@List)-1)))
        Set @List =Substring(@List,Charindex(@SplitOn,@List)+len(@SplitOn),len(@List))
    End
        Insert Into @RtnValue (Value)
        Select Value = ltrim(rtrim(@List))
        Return
    END
    
    --below is the dynamic solution for this problem
    declare @sql nvarchar(3000) = 'select *'
    declare @cnt int = 1
    declare @rowNum int = (select max(a) from (select(select max(id) as id_max from dbo.so_split(mul_query,'*')) as a from #test) as b)
    
    while(@cnt <= @rowNum)
    begin
        set @sql = @sql + N', ISNULL((select value from dbo.so_split(mul_query,''*'') where id = '+cast(@cnt as nvarchar(5))+N'),''1'')'
        set @cnt = @cnt + 1
    end
    
    set @sql = @sql + N' from #test'
    
    exec sp_executesql @sql
    

    The result is attached below.

    0 讨论(0)
  • 2020-12-19 08:16

    You can roll your own string splitting function as detailed in great depth here by Jeff Moden.

    For posterity purposes, the final code is:

    CREATE FUNCTION [dbo].[DelimitedSplit8K]
    --===== Define I/O parameters
            (@pString VARCHAR(8000), @pDelimiter CHAR(1))
    --WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!
    RETURNS TABLE WITH SCHEMABINDING AS
     RETURN
    --===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
         -- enough to cover VARCHAR(8000)
      WITH E1(N) AS (
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                    ),                          --10E+1 or 10 rows
           E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
           E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
     cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                         -- for both a performance gain and prevention of accidental "overruns"
                     SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                    ),
    cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                     SELECT 1 UNION ALL
                     SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                    ),
    cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                     SELECT s.N1,
                            ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                       FROM cteStart s
                    )
    --===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
     SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
            Item       = SUBSTRING(@pString, l.N1, l.L1)
       FROM cteLen l
    

    Once you have this function, you can pull out the relevant data using a pivot table:

    select PAR_COLUMN
            ,PERIOD
            ,VALUE
            ,mul_query
            ,[1]
            ,[2]
            ,[3]
            ,[4]
    from(select f.PAR_COLUMN
                ,f.PERIOD
                ,f.VALUE
                ,f.mul_query
                ,s.ItemNumber
                ,s.Item
        from @finals f
            cross apply dbo.DelimitedSplit8K(f.mul_query,'*') s
        ) as d
    pivot
    (
    max(Item)
    for ItemNumber in([1],[2],[3],[4])
    ) as pvt
    
    0 讨论(0)
  • 2020-12-19 08:21

    With a little help from a CROSS APPLY and an UDF to split values

    Declare @YouTable table (PAR_COLUMN int,PERIOD int,VALUE decimal(18,6), mul_query varchar(250))
    Insert Into @YouTable values
    (1,601,10.134542,'10.134542'),
    (1,602,20.234234,'10.134542*20.234234'),
    (1,603,30.675643,'10.134542*20.234234*30.675643'),
    (1,604,40.234234,'10.134542*20.234234*30.675643*40.234234'),
    (2,601,10.345072,'10.345072'),
    (2,602,20.345072,'10.345072*20.345072'),
    (2,603,30.345072,'10.345072*20.345072*30.345072'),
    (2,604,40.345072,'10.345072*20.345072*30.345072*40.345072')
    
    Select A.PAR_COLUMN
          ,A.PERIOD
          ,A.VALUE
          ,Pos1=IsNull(B.Pos1,1)
          ,Pos2=IsNull(B.Pos2,1)
          ,Pos3=IsNull(B.Pos3,1)
          ,Pos4=IsNull(B.Pos4,1)
          ,Pos5=IsNull(B.Pos5,1)
          ,Pos6=IsNull(B.Pos6,1)
     From  @YouTable A
     Cross Apply (Select * from [dbo].[udf-Str-Parse-Row](A.mul_query,'*')) B
    

    Returns

    PAR_COLUMN  PERIOD  VALUE       Pos1        Pos2      Pos3      Pos4    Pos5    Pos6
    1           601     10.134542   10.134542   1         1         1   1   1
    1           602     20.234234   10.134542   20.234234 1         1   1   1
    1           603     30.675643   10.134542   20.234234 30.675643 1   1   1
    1           604     40.234234   10.134542   20.234234 30.675643 40.234234   1   1
    2           601     10.345072   10.345072   1   1   1 1 1
    2           602     20.345072   10.345072   20.345072 1 1   1   1
    2           603     30.345072   10.345072   20.345072 30.345072 1   1   1
    2           604     40.345072   10.345072   20.345072 30.345072 40.345072   1   1
    

    The UDF

    CREATE FUNCTION [dbo].[udf-Str-Parse-Row] (@String varchar(max),@Delimeter varchar(10))
    --Usage: Select * from [dbo].[udf-Str-Parse-Row]('Dog,Cat,House,Car',',')
    --       Select * from [dbo].[udf-Str-Parse-Row]('John Cappelletti',' ')
    --       Select * from [dbo].[udf-Str-Parse-Row]('id26,id46|id658,id967','|')
    
    Returns Table 
    
    As
    
    Return (
        SELECT Pos1 = xDim.value('/x[1]','varchar(250)')
              ,Pos2 = xDim.value('/x[2]','varchar(250)')
              ,Pos3 = xDim.value('/x[3]','varchar(250)')
              ,Pos4 = xDim.value('/x[4]','varchar(250)')
              ,Pos5 = xDim.value('/x[5]','varchar(250)')
              ,Pos6 = xDim.value('/x[6]','varchar(250)')
              ,Pos7 = xDim.value('/x[7]','varchar(250)')
              ,Pos8 = xDim.value('/x[8]','varchar(250)')
              ,Pos9 = xDim.value('/x[9]','varchar(250)')
        FROM (Select Cast('<x>' + Replace(@String,@Delimeter,'</x><x>')+'</x>' as XML) as xDim) A
    
    0 讨论(0)
提交回复
热议问题