SQL Server - 将行数据转换为列

时间:2013-11-30 06:20:04

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

我正在开展一个项目,我有以下要求

我的源表是

PeriodID     PeriodName   ProductID   ProductName  Productvalue
--------     ----------   ---------   -----------  ------------
 10           Jan          100          A            15
 20           Feb          100          A            25
 30           Mar          100          A            35
 10           Jan          200          B            12
 20           Feb          200          B            14
 30           Mar          200          B            18
 10           Jan          300          C            22
 20           Feb          300          C            23
 30           Mar          300          C            38

我希望表格的输出像这样

目的地表

PeriodID     PeriodName    A_ID   A     B_ID   B     C_ID  C
--------     ----------    ----   --    ----   --    ----  --
 10           Jan          100    15    200    12    300   22
 20           Feb          100    25    200    14    300   23
 30           Mar          100    35    200    18    300   38

我正在尝试使用sql server 2008的pivot和unpivot,但我并不熟悉这些运算符的功能。我知道这个要求看起来很奇怪,但这就是我想要的输出结果集。

请帮助我,过去几天我真的遇到麻烦来克服这个问题。

2 个答案:

答案 0 :(得分:2)

获得结果的最简单方法是使用聚合函数和CASE表达式:

select 
  periodid,
  periodname,
  max(case when productname = 'A' then productid end) A_ID,
  max(case when productname = 'A' then Productvalue end) A,
  max(case when productname = 'B' then productid end) B_ID,
  max(case when productname = 'B' then Productvalue end) B,
  max(case when productname = 'C' then productid end) C_ID,
  max(case when productname = 'C' then Productvalue end) C
from yourtable
group by periodid, periodname
order by periodid;

请参阅SQL Fiddle with Demo

但是,如果您想使用PIVOT函数来获得结果,我首先要忽略productidproductvalue列,这样您就不再有多列但只有多行此数据。有几种方法可以对数据进行取消,可以使用unpivot功能,也可以将CROSS APPLY与UNION ALL或VALUES一起使用(如果使用的是SQL Server 2008+)。由于您使用的是SQL Server 2008 R2,因此以下是使用CROSS APPLY和VALUES对多列进行取消操作的方法:

select periodid, periodname, 
  col = productname+col,
  value
from yourtable
cross apply
(
  values
    ('_ID', ProductID),
    ('', Productvalue)
) c (col, value)

SQL Fiddle with Demo。这会将您的数据转换为以下格式:

| PERIODID | PERIODNAME |  COL | VALUE |
|----------|------------|------|-------|
|       10 |        Jan | A_ID |   100 |
|       10 |        Jan |    A |    15 |
|       20 |        Feb | A_ID |   100 |
|       20 |        Feb |    A |    25 |
|       30 |        Mar | A_ID |   100 |

现在您可以轻松地将PIVOT功能应用于此数据:

select periodid, periodname,
  A_ID, A, B_ID, B, C_ID, C
from 
(
  select periodid, periodname, 
    col = productname+col,
    value
  from yourtable
  cross apply
  (
    values
      ('_ID', ProductID),
      ('', Productvalue)
  ) c (col, value)
) d
pivot
(
  max(value)
  for col in (A_ID, A, B_ID, B, C_ID, C)
) piv
order by periodid;

请参阅SQL Fiddle with Demo

最后,如果您拥有未知数量的产品名称,则可以使用动态SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(productname+col) 
                    from yourtable
                    cross apply
                    (
                      select '_ID', 1 union all
                      select '', 2
                    ) c (col, so)
                    group by col, so, productname
                    order by productname, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT periodid, periodname,' + @cols + ' 
            from 
            (
                select periodid, periodname, 
                  col = productname+col,
                  value
                from yourtable
                cross apply
                (
                  values
                    (''_ID'', ProductID),
                    ('''', Productvalue)
                ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p 
            order by periodid'

execute sp_executesql @query;

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

| PERIODID | PERIODNAME | A_ID |  A | B_ID |  B | C_ID |  C |
|----------|------------|------|----|------|----|------|----|
|       10 |        Jan |  100 | 15 |  200 | 12 |  300 | 22 |
|       20 |        Feb |  100 | 25 |  200 | 14 |  300 | 23 |
|       30 |        Mar |  100 | 35 |  200 | 18 |  300 | 38 |

答案 1 :(得分:0)

如果组合PeriodID& ProductID是唯一键,以下查询将为您提供所需的结果。

你想要吗?

 SELECT     PeriodID
      , MIN(PeriodName) AS PeriodName
      , MIN(A_ID) AS A_ID
      , MIN(A) AS A
      , MIN(B_ID) AS B_ID
      , MIN(B) AS B
      , MIN(C_ID) AS C_ID
      , MIN(C) AS C
 FROM (
    SELECT     PeriodID
             , PeriodName
             , CASE ProductID WHEN 'A' THEN ProductID
                              ELSE NULL
               END AS A_ID
             , CASE ProductID WHEN 'A' THEN Productvalue
                              ELSE NULL
               END AS A
             , CASE ProductID WHEN 'B' THEN ProductID
                              ELSE NULL
               END AS B_ID
             , CASE ProductID WHEN 'B' THEN Productvalue
                              ELSE NULL
               END AS B 
             , CASE ProductID WHEN 'C' THEN ProductID
                              ELSE NULL
               END AS C
             , CASE ProductID WHEN 'C' THEN Productvalue
                              ELSE NULL
               END AS C
      FROM     YOUR_TABLE
    ) 
    GROUP BY PeriodID