sql server group columns as rows(pivot?)

时间:2013-03-21 09:39:05

标签: sql sql-server-2008 pivot unpivot

我在SQL Server 2008 R2中得到了这个结果数据

id      Size    Acted   Sum     Avg1    Avg2    A1       A2      A3
1       3921    39      690     17.69   0.18    NULL      NULL   NULL
40      11979   301     5944.26 19.75   0.5 10000.00 2000.00 1000.00
41      11714   289     5060    17.51   0.43 10000.00 3000.00 2000.00
42      11599   265     4107.98 15.5    0.35 10000.00 5000.00 500.00

我想根据id将列移动到行中,所以我会收到这个结果:

id1        id40       id41         id42
1          40         41           42
3921       11979      11714        11599
39         301        289          265
690        5944       5060         4107
17.69      19.75      17.51        15.5
0.18       0.5        0.43         0.35
           10000.00   2000.00      1000.00
           10000.00   3000.00      2000.00
           10000.00   5000.00      500.00

有办法吗? 我尝试过枢轴,但据我所知,在这种情况下我只能变换1列而不是很多。

1 个答案:

答案 0 :(得分:4)

为了获得此结果,您需要首先从列到行的数据,然后应用PIVOT函数。

由于您使用的是SQL Server 2008,因此可以使用CROSS APPLYVALUES来取消数据的显示。这将获取众多列中的值并将其转换为行:

select 'id'+cast(t.id as varchar(10)) p_id,
  c.col, 
  c.value,
  c.sort_order
from yourtable t
cross apply
(
  values 
    (1, 'id', id),
    (2, 'size', size),
    (3, 'acted', acted),
    (4, 'sum', sum),
    (5, 'avg1', avg1),
    (6, 'avg2', avg2),
    (7, 'a1', a1),
    (8, 'a2', a2),
    (9, 'a3', a3)
) c (sort_order, col, value)

SQL Fiddle with Demo。数据取消后,您可以使用id值的新列进行转动。所以完整的代码是:

select col, 
  id1, 
  id40, 
  id41, 
  id42
from
(
  select 'id'+cast(t.id as varchar(10)) p_id,
    c.col, 
    c.value,
    c.sort_order
  from yourtable t
  cross apply
  (
    values 
      (1, 'id', id),
      (2, 'size', size),
      (3, 'acted', acted),
      (4, 'sum', sum),
      (5, 'avg1', avg1),
      (6, 'avg2', avg2),
      (7, 'a1', a1),
      (8, 'a2', a2),
      (9, 'a3', a3)
  ) c (sort_order, col, value)
) src
pivot
(
  max(value)
  for p_id in (id1, id40, id41, id42)
) piv
order by sort_order;

SQL Fiddle with Demo

如果您无法使用CROSS APPLYVALUES,那么也可以使用UNPIVOT函数完成此操作:

select col, 
  id1, id40, id41, id42
from
(
  select 'id'+cast(id_piv as varchar(10)) id,
    col,
    value,
    case col
      when 'id' then 1
      when 'size' then 2
      when 'acted' then 3
      when 'sum' then 4
      when 'avg1' then 5
      when 'avg2' then 6
      when 'a1' then 7
      when 'a2' then 8
      when 'a3' then 9 end sort_order
  from
  (
    select id id_piv,
      cast(id as numeric(10, 2)) id, 
      cast(size as numeric(10, 2)) size,
      cast(acted as numeric(10, 2)) acted,
      sum, avg1, avg2, A1, A2, A3
    from yourtable
  ) d
  unpivot
  (
    value
    for col in (id, size, acted, sum, avg1, avg2, a1, a2, a3)
   ) unpiv
) src
pivot
(
  max(value)
  for id in (id1, id40, id41, id42)
) piv
order by sort_order;

请参阅SQL Fiddle with Demo

最后,如果您要将未知数量的id值转换为列,则需要使用动态sql:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME('id'+cast(id as varchar(10))) 
                    from yourtable
                    group by id
                    order by id
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT col, ' + @cols + ' 
              from 
             (
                select ''id''+cast(t.id as varchar(10)) p_id,
                  c.col, 
                  c.value,
                  c.sort_order
                from yourtable t
                cross apply
                (
                  values 
                    (1, ''id'', id),
                    (2, ''size'', size),
                    (3, ''acted'', acted),
                    (4, ''sum'', sum),
                    (5, ''avg1'', avg1),
                    (6, ''avg2'', avg2),
                    (7, ''a1'', a1),
                    (8, ''a2'', a2),
                    (9, ''a3'', a3)
                ) c (sort_order, col, value)
            ) x
            pivot 
            (
                max(value)
                for p_id in (' + @cols + ')
            ) p 
            order by sort_order'

execute(@query)

请参阅SQL Fiddle with Demo

结果的所有版本:

|   COL |    ID1 |    ID40 |  ID41 |    ID42 |
----------------------------------------------
|    id |      1 |      40 |    41 |      42 |
|  size |   3921 |   11979 | 11714 |   11599 |
| acted |     39 |     301 |   289 |     265 |
|   sum |    690 | 5944.26 |  5060 | 4107.98 |
|  avg1 |  17.69 |   19.75 | 17.51 |    15.5 |
|  avg2 |   0.18 |     0.5 |  0.43 |    0.35 |
|    a1 | (null) |   10000 | 10000 |   10000 |
|    a2 | (null) |    2000 |  3000 |    5000 |
|    a3 | (null) |    1000 |  2000 |     500 |