TSQL Pivot数据

时间:2014-06-19 17:57:30

标签: sql-server-2008 tsql pivot

数据:

id  data
1   1
1   4
2   2
2   1
3   3
3   2
4   4
4   1
5   5
5   2

我需要转动上述数据,以便得到以下结果:

id  Col1   Col2
1   1        4
2   2        1
3   3        2
4   4        1
5   5        2

我正在尝试以下查询,但列始终为空:

DECLARE @TEMP TABLE (id int,data varchar(200))

INSERT INTO @TEMP VALUES (1,'1')
INSERT INTO @TEMP VALUES (1,'4')
INSERT INTO @TEMP VALUES (2,'2')
INSERT INTO @TEMP VALUES (2,'1')
INSERT INTO @TEMP VALUES (3,'3')
INSERT INTO @TEMP VALUES (4,'2')
INSERT INTO @TEMP VALUES (4,'4')
INSERT INTO @TEMP VALUES (4,'1')
INSERT INTO @TEMP VALUES (5,'5')
INSERT INTO @TEMP VALUES (5,'2')

SELECT * FROM @TEMP 
    PIVOT
    (
      MAX(Data)
      FOR Data IN ([Col1],[Col2])
    )
    AS p

结果:

id  Col1    Col2
1   NULL    NULL
2   NULL    NULL
3   NULL    NULL
4   NULL    NULL
5   NULL    NULL

2 个答案:

答案 0 :(得分:9)

您的查询存在的问题是,您的Data列中没有任何名为Col1Col2的值。

当您编写PIVOT查询时,查询的关键部分是:

pivot
(
  max(data)  -- < these are the values that you need displayed in the new columns
  for col in (Col1, Col2)  -- < this is the new column names from existing values
) p

您的查询正在使用Col1Col2,但这些内容未在任何地方定义,因此您将返回null

您目前没有任何可用作新列名称的内容。为了获得结果,您需要使用类似于row_number的窗口函数来为每个id生成唯一序列。基本语法为:

select id, Col1, Col2
from
(
  select id, data,
    col =
      'Col'+
        cast(row_number() over(partition by id
                                order by data) as varchar(2))
  from yourtable
) d
pivot
(
  max(data)
  for col in (Col1, Col2)
) p;

SQL Fiddle with Demo。您会注意到,在子查询中,我使用窗口函数来创建新列名Col1Col2等。然后在轴中使用它。

现在针对您的情况,如果您将拥有未知数量的可能列,那么您将需要使用动态SQL。这将创建一个随后将执行的SQL字符串:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(col) 
                    from
                    (
                      select col =
                        'Col'+
                          cast(row_number() over(partition by id
                                                  order by data) as varchar(2))
                      from yourtable
                    ) d
                    group by Col
                    order by col
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT id, ' + @cols + ' 
            from 
            (
              select id, data,
                col =
                  ''Col''+
                    cast(row_number() over(partition by id
                                            order by data) as varchar(2))
              from yourtable
            ) x
            pivot 
            (
                max(Data)
                for col in (' + @cols + ')
            ) p '

exec sp_executesql @query;

请参阅SQL Fiddle with Demo

最后,通过使用带有CASE表达式的聚合函数,也可以在不使用PIVOT的情况下完成此操作:

select id, 
  Col1 = max(case when seq = 1 then data end), 
  Col2 = max(case when seq = 2 then data end)
from
(
  select id, data,
    seq = row_number() over(partition by id
                            order by data)
  from yourtable
) d
group by id;

SQL Fiddle with Demo。所有版本都给出相同的结果:

| ID | COL1 | COL2 |
|----|------|------|
|  1 |    1 |    4 |
|  2 |    1 |    2 |
|  3 |    2 |    3 |
|  4 |    1 |    4 |
|  5 |    2 |    5 |

答案 1 :(得分:3)

您可以在没有PIVOT的情况下执行此操作:

;WITH CTE AS   
(
 SELECT 
 *,
 ROW_NUMBER() OVER (PARTITION BY id ORDER BY id) RN 
 FROM @TEMP)

SELECT DISTINCT t.id, c1.data, c2.data
FROM @temp t
INNER JOIN CTE c1 ON c1.id = t.id AND c1.rn = 1
INNER JOIN CTE c2 ON c2.id = t.id AND c2.rn = 2

请注意,这将适用于此特定情况。但是,您应该尝试理解@ bluefeet的答案,以便彻底了解PIVOT如何工作以及如果您在另一个场景中有多个列,您将如何使用它。