PIVOT按组设置的整个列

时间:2013-07-30 17:56:51

标签: sql sql-server sql-server-2008 sql-server-2008-r2 pivot

对于像这样的表:

COL1    COL2    COL3    COL4
item1   7/29/13 cat     blue
item3   7/29/13 fish    purple
item1   7/30/13 rat     green
item2   7/30/13 bat     grey
item3   7/30/13 bird    orange

你如何通过COL2 PIVOT获取行,所有其他列以COL1值重复作为块?

COL2    COL1    COL3    COL4    COL1    COL3    COL4    COL1    COL3    COL4
7/29/13 item1   cat     blue    item2   NULL    NULL    item3   fish    purple
7/30/13 item1   rat     green   item2   bat     grey    item3   bird    orange

2 个答案:

答案 0 :(得分:2)

为了得到这个结果,你需要做一些事情:

  1. col1col2
  2. 获取不同的值列表
  3. 取消列col1col3col4
  4. 列中的数据
  5. 从unpivot
  6. 转出结果

    要获取日期和项目的明确列表(col1和col2)以及现有表格中的值,您需要使用类似于以下内容的内容:

    select t.col1, t.col2,
      t2.col3, t2.col4,
      row_number() over(partition by t.col2
                          order by t.col1) seq
    from 
    (
      select distinct t.col1, c.col2
      from yourtable t
      cross join 
      (
        select distinct col2
        from yourtable
      ) c
    ) t
    left join yourtable t2
      on t.col1 = t2.col1
      and t.col2 = t2.col2;
    

    SQL Fiddle with Demo。获得此列表后,您将需要取消数据。有几种方法可以使用UNPIVOT函数或使用CROSS APPLY:

    select d.col2,
      col = col+'_'+cast(seq as varchar(10)),
      value
    from
    (
      select t.col1, t.col2,
        t2.col3, t2.col4,
        row_number() over(partition by t.col2
                            order by t.col1) seq
      from 
      (
        select distinct t.col1, c.col2
        from yourtable t
        cross join 
        (
          select distinct col2
          from yourtable
        ) c
      ) t
      left join yourtable t2
        on t.col1 = t2.col1
        and t.col2 = t2.col2
    ) d
    cross apply
    (
      select 'col1', col1 union all
      select 'col3', col3 union all
      select 'col4', col4 
    ) c (col, value);
    

    SQL Fiddle with Demo。这将为您提供如下数据:

    |                        COL2 |    COL |  VALUE |
    -------------------------------------------------
    | July, 29 2013 00:00:00+0000 | col1_1 |  item1 |
    | July, 29 2013 00:00:00+0000 | col3_1 |    cat |
    | July, 29 2013 00:00:00+0000 | col4_1 |   blue |
    | July, 29 2013 00:00:00+0000 | col1_2 |  item2 |
    | July, 29 2013 00:00:00+0000 | col3_2 | (null) |
    | July, 29 2013 00:00:00+0000 | col4_2 | (null) |
    

    最后,您将PIVOT函数应用于col列中的项目:

    select col2, 
      col1_1, col3_1, col4_1, 
      col1_2, col3_2, col4_2, 
      col1_3, col3_3, col4_3
    from
    (
      select d.col2,
        col = col+'_'+cast(seq as varchar(10)),
        value
      from
      (
        select t.col1, t.col2,
          t2.col3, t2.col4,
          row_number() over(partition by t.col2
                              order by t.col1) seq
        from 
        (
          select distinct t.col1, c.col2
          from yourtable t
          cross join 
          (
            select distinct col2
            from yourtable
          ) c
        ) t
        left join yourtable t2
          on t.col1 = t2.col1
          and t.col2 = t2.col2
      ) d
      cross apply
      (
        select 'col1', col1 union all
        select 'col3', col3 union all
        select 'col4', col4 
      ) c (col, value)
    ) src
    pivot
    (
      max(value)
      for col in (col1_1, col3_1, col4_1, 
                  col1_2, col3_2, col4_2, 
                  col1_3, col3_3, col4_3)
    )piv;
    

    SQL Fiddle with Demo。如果您具有未知数量的值,则可以使用动态SQL来获取结果:

    DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX)
    
    select @cols = STUFF((SELECT ',' + QUOTENAME(col+'_'+cast(seq as varchar(10))) 
                        from
                        (
                          select row_number() over(partition by col2
                                                      order by col1) seq
                          from yourtable
                        ) t
                        cross apply
                        (
                          select 'col1', 1 union all
                          select 'col3', 2 union all
                          select 'col4', 3
                        ) c (col, so)
                        group by col, seq, so
                        order by seq, so
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'')
    
    set @query = 'SELECT col2, ' + @cols + ' 
                from 
                (
                    select d.col2,
                      col = col+''_''+cast(seq as varchar(10)),
                      value
                    from
                    (
                      select t.col1, t.col2,
                        t2.col3, t2.col4,
                        row_number() over(partition by t.col2
                                            order by t.col1) seq
                      from 
                      (
                        select distinct t.col1, c.col2
                        from yourtable t
                        cross join 
                        (
                          select distinct col2
                          from yourtable
                        ) c
                      ) t
                      left join yourtable t2
                        on t.col1 = t2.col1
                        and t.col2 = t2.col2
                    ) d
                    cross apply
                    (
                      select ''col1'', col1 union all
                      select ''col3'', col3 union all
                      select ''col4'', col4 
                    ) c (col, value)
                ) x
                pivot 
                (
                    max(value)
                    for col in (' + @cols + ')
                ) p '
    
    execute sp_executesql @query;
    

    SQL Fiddle with Demo。所有版本都会给出结果:

    |                        COL2 | COL1_1 | COL3_1 | COL4_1 | COL1_2 | COL3_2 | COL4_2 | COL1_3 | COL3_3 | COL4_3 |
    ----------------------------------------------------------------------------------------------------------------
    | July, 29 2013 00:00:00+0000 |  item1 |    cat |   blue |  item2 | (null) | (null) |  item3 |   fish | purple |
    | July, 30 2013 00:00:00+0000 |  item1 |    rat |  green |  item2 |    bat |   grey |  item3 |   bird | orange |
    

答案 1 :(得分:0)

动态UNPIVOT + PIVOT方法总是很酷,当为一组已知且有限的值做这类事情后,JOIN的工作也很好(懒惰) SELECT列表):

WITH cte AS (SELECT *,ROW_NUMBER() OVER (PARTITION BY COL2 ORDER BY COL1)'RowRank'
             FROM  #Table1)
SELECT *
FROM  cte a
LEFT JOIN cte b
  ON a.COL2 = b.COL2
  AND a.RowRank = b.RowRank - 1
LEFT JOIN cte c
  ON b.COL2 = c.COL2
  AND b.RowRank = c.RowRank - 1
WHERE a.RowRank = 1

或者如果要维护字段的顺序:

WITH cte AS (SELECT a.*,b.RowRank
             FROM  #Table1 a
             JOIN (SELECT Col1,ROW_NUMBER() OVER (ORDER BY Col1)'RowRank' 
                   FROM #Table1
                   GROUP BY COL1) b
                 ON a.Col1 = b.Col1)
SELECT *
FROM  cte a
LEFT JOIN cte b
  ON a.COL2 = b.COL2
  AND a.RowRank = b.RowRank - 1
LEFT JOIN cte c
ON a.COL2 = c.COL2
  AND a.RowRank = c.RowRank - 2
WHERE a.RowRank = 1

但是如果没有“锚”值,这就会崩溃,即如果某个日期没有记录item1,那么它就不会被包含在内。