Pivot multiple columns based on one column in SQL Server

后端 未结 2 1927
说谎
说谎 2020-11-30 06:14

I have the following source and destination tables in SQL Server 2008R2. How can I do pivot(s) in TSQL to transform SourceTbl into DestTbl? Hoping

相关标签:
2条回答
  • 2020-11-30 06:27

    Since you are using SQL Server there are several different ways that you can convert the rows into columns. You can use an aggregate function with a CASE expression:

    select empid,
      max(case when empindex = 1 then empstate end) empState1,
      max(case when empindex = 1 then empStDate end) empStDate1,
      max(case when empindex = 1 then empEndDate end) empEndDate1,
      max(case when empindex = 2 then empstate end) empState2,
      max(case when empindex = 2 then empStDate end) empStDate2,
      max(case when empindex = 2 then empEndDate end) empEndDate2
    from sourcetbl
    group by empid;
    

    See SQL Fiddle with Demo.

    If you want to use the PIVOT function to get the result, then I would recommend first unpivoting the columns empState, empStDate and empEndDate so you will have multiple rows first. You can use the UNPIVOT function or CROSS APPLY to convert the data the code will be:

    select empid, col+cast(empindex as varchar(10)) col,  value
    from sourcetbl
    cross apply
    (
      select 'empstate', empstate union all
      select 'empstdate', convert(varchar(10), empstdate, 120) union all
      select 'empenddate', convert(varchar(10), empenddate, 120)
    ) c (col, value);
    

    See Demo. Once the data is unpivoted, then you can apply the PIVOT function so the final code will be:

    select empid,
      empState1, empStDate1, empEndDate1,
      empState2, empStDate2, empEndDate2
    from 
    (
      select empid, col+cast(empindex as varchar(10)) col,  value
      from sourcetbl
      cross apply
      (
        select 'empstate', empstate union all
        select 'empstdate', convert(varchar(10), empstdate, 120) union all
        select 'empenddate', convert(varchar(10), empenddate, 120)
      ) c (col, value)
    ) d
    pivot
    (
      max(value)
      for col in (empState1, empStDate1, empEndDate1,
                  empState2, empStDate2, empEndDate2)
    ) piv;
    

    See SQL Fiddle with Demo.

    Th above versions will work great if you have a limited number of empindex, but if not then you can use dynamic SQL:

    DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX)
    
    select @cols = STUFF((SELECT ',' + QUOTENAME(col+cast(empindex as varchar(10))) 
                        from SourceTbl
                        cross apply
                        (
                          select 'empstate', 1 union all
                          select 'empstdate', 2 union all
                          select 'empenddate', 3
                        ) c (col, so)
                        group by col, so, empindex
                        order by empindex, so
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'')
    
    set @query = 'SELECT empid,' + @cols + ' 
                from 
                (
                    select empid, col+cast(empindex as varchar(10)) col,  value
                    from sourcetbl
                    cross apply
                    (
                      select ''empstate'', empstate union all
                      select ''empstdate'', convert(varchar(10), empstdate, 120) union all
                      select ''empenddate'', convert(varchar(10), empenddate, 120)
                    ) c (col, value)
                ) x
                pivot 
                (
                    max(value)
                    for col in (' + @cols + ')
                ) p '
    
    execute sp_executesql @query;
    

    See SQL Fiddle with Demo

    You can use these queries to INSERT INTO your DestTbl, or instead of storing the data in this format, you now have a query to get the desired result.

    These queries place the data in the format:

    | EMPID | EMPSTATE1 | EMPSTDATE1 | EMPENDDATE1 | EMPSTATE2 | EMPSTDATE2 | EMPENDDATE2 |
    ---------------------------------------------------------------------------------------
    |    10 |        AL | 2012-01-01 |  2012-12-01 |        FL | 2012-02-01 |  2013-02-01 |
    |    15 |        FL | 2012-03-20 |  2099-01-01 |    (null) |     (null) |      (null) |
    
    0 讨论(0)
  • 2020-11-30 06:30

    Wow this was more complicated than i imagined, but I did get it to work great! thanks. This was my final version. The TextKey contains the data you want to turn into columns, and the TextValue is the value that ends up inside each cell.

    DECLARE @cols AS NVARCHAR(MAX),
            @query AS NVARCHAR(MAX)
    
    
    select @cols = STUFF((SELECT distinct ', ' + QUOTENAME(TextKey) 
                        from #SourceTbl
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'')
    
    set @query = 'SELECT FromEntityID, DisplayName, ' + @cols + ' 
                  FROM 
                  (
                      select FromEntityID, DisplayName, TextKey, TextValue
                      from #SourceTbl
                  ) x
                  pivot 
                  (
                      min(TextValue)
                      for TextKey in (' + @cols + ')
                  ) p 
                  ORDER BY FromEntityID
                  '
    
    execute(@query)
    
    0 讨论(0)
提交回复
热议问题