2013-04-10 39 views
0

好的,我有下表。如何将垂直数据转换为行长度可变的水平数据SQL?

Name ID Website 

Aaron | 2305 | CoolSave1 


Aaron | 8464 | DiscoWorld1 


Adriana | 2956 | NewCin1 


Adriana | 5991 | NewCin2 


Adriana | 4563 NewCin3 

我想将其转换为以下方式。

Adriana | 2956 | NewCin1 | 5991 | NewCin2 | 4563 | NewCin3 


Aaron | 2305 | CoolSave1 | 8464 | DiscoWorld | NULL | NULL  

正如你可以看到我试图从第一个表采取的第一个名字,使单排与该名称相关联的所有的ID /网站。问题是,可能与每个名称关联的网站数量不定。为了处理这个问题,我想用一个等于最大行项目的字段数制作一个表格,然后对于后续的项目,在没有足够数据的情况下插入NULL。

回答

5

为了得到结果,您需要将UNPIVOT和PIVOT功能同时应用于数据。 UNPIVOT将取得列(ID,网站)并将它们转换为行,一旦完成,您可以将数据转移回列。

的UNPIVOT代码将类似于以下内容:

select name, 
    col+'_'+cast(col_num as varchar(10)) col, 
    value 
from 
(
    select name, 
    cast(id as varchar(11)) id, 
    website, 
    row_number() over(partition by name order by id) col_num 
    from yt 
) src 
unpivot 
(
    value 
    for col in (id, website) 
) unpiv; 

SQL Fiddle with Demo。这给出了一个结果:

| NAME |  COL |  VALUE | 
------------------------------------- 
| Aaron |  id_1 |  2305 | 
| Aaron | website_1 | CoolSave1 | 
| Aaron |  id_2 |  8464 | 
| Aaron | website_2 | DiscoWorld1 | 

正如你可以看到我的逆转置施加row_number()的数据之前,行号是用来生成新的列名。 UNPIVOT中的列也必须具有相同的数据类型,我在子查询中将cast应用于id列,以便在数据透视之前将数据转换为varchar

然后在PIVOT中使用col值。一旦数据已经unpivot操作,在应用旋转功能:

select * 
from 
(
    select name, 
    col+'_'+cast(col_num as varchar(10)) col, 
    value 
    from 
    (
    select name, 
     cast(id as varchar(11)) id, 
     website, 
     row_number() over(partition by name order by id) col_num 
    from yt 
) src 
    unpivot 
    (
    value 
    for col in (id, website) 
) unpiv 
) d 
pivot 
(
    max(value) 
    for col in (id_1, website_1, id_2, website_2, id_3, website_3) 
) piv; 

SQL Fiddle with Demo

上面的版本很有用,如果你有一个有限或已知数量的值。但是,如果行数是未知的,那么你就需要使用动态SQL生成结果:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(col+'_'+cast(col_num as varchar(10))) 
        from 
        (
         select row_number() over(partition by name order by id) col_num 
         from yt 
        ) t 
        cross apply 
        (
         select 'id' col union all 
         select 'website' 
        ) c 
        group by col, col_num 
        order by col_num, col 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 

set @query = 'SELECT name,' + @cols + ' 
      from 
      (
       select name, 
       col+''_''+cast(col_num as varchar(10)) col, 
       value 
       from 
       (
       select name, 
        cast(id as varchar(11)) id, 
        website, 
        row_number() over(partition by name order by id) col_num 
       from yt 
      ) src 
       unpivot 
       (
       value 
       for col in (id, website) 
      ) unpiv 
      ) x 
      pivot 
      (
       max(value) 
       for col in (' + @cols + ') 
      ) p ' 

execute(@query); 

SQL Fiddle with Demo。两个版本都给出了结果:

| NAME | ID_1 | WEBSITE_1 | ID_2 | WEBSITE_2 | ID_3 | WEBSITE_3 | 
------------------------------------------------------------------------ 
| Aaron | 2305 | CoolSave1 | 8464 | DiscoWorld1 | (null) | (null) | 
| Adriana | 2956 | NewCin1 | 4563 |  NewCin3 | 5991 | NewCin2 | 
+0

我喜欢带提琴手的演示。问题:这不是纯粹的SQL,但稍微像存储过程/程序吧? – 2013-04-10 17:14:16

+0

@Menelaos第二个将在存储过程中运行。 – Taryn 2013-04-10 17:15:40