Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何将垂直数据转换为具有可变行长的水平数据SQL?_Sql_Sql Server 2005_Pivot_Unpivot - Fatal编程技术网

如何将垂直数据转换为具有可变行长的水平数据SQL?

如何将垂直数据转换为具有可变行长的水平数据SQL?,sql,sql-server-2005,pivot,unpivot,Sql,Sql Server 2005,Pivot,Unpivot,好的,我有下表。 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

好的,我有下表。

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/网站列成一行。问题是,可能有大量网站与每个名称关联。为了处理这个问题,我只想创建一个表,表中的字段数与max行项目相同,然后对于后续的行项目,在没有足够数据的地方插入一个NULL。

为了得到结果,需要对数据应用UNPIVOT和PIVOT函数。UNPIVOT将获取列(ID、website)并将它们转换为行,完成后,您可以将数据转回到列中

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;
看。这就产生了一个结果:

|    NAME |       COL |       VALUE |
-------------------------------------
|   Aaron |      id_1 |        2305 |
|   Aaron | website_1 |   CoolSave1 |
|   Aaron |      id_2 |        8464 |
|   Aaron | website_2 | DiscoWorld1 |
如您所见,我在取消PIVOT之前对数据应用了一个
row\u number()
,该行号用于生成新的列名。UNPIVOT中的列也必须是相同的数据类型,我在子查询中的
id
列上应用了
cast
,以便在透视之前将数据转换为
varchar

然后在枢轴中使用
col
值。数据解除锁定后,应用PIVOT函数:

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生成结果:

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);
|    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 |
看。两个版本都给出了结果:

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);
|    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 |

为了得到结果,需要对数据应用UNPIVOT和PIVOT函数。UNPIVOT将获取列(ID、website)并将它们转换为行,完成后,您可以将数据转回到列中

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;
看。这就产生了一个结果:

|    NAME |       COL |       VALUE |
-------------------------------------
|   Aaron |      id_1 |        2305 |
|   Aaron | website_1 |   CoolSave1 |
|   Aaron |      id_2 |        8464 |
|   Aaron | website_2 | DiscoWorld1 |
如您所见,我在取消PIVOT之前对数据应用了一个
row\u number()
,该行号用于生成新的列名。UNPIVOT中的列也必须是相同的数据类型,我在子查询中的
id
列上应用了
cast
,以便在透视之前将数据转换为
varchar

然后在枢轴中使用
col
值。数据解除锁定后,应用PIVOT函数:

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生成结果:

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);
|    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 |
看。两个版本都给出了结果:

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);
|    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 |

我喜欢小提琴手的演示。问:这不是纯SQL,但有点像存储过程/程序,对吗?@Menelaos第二个将在存储过程中运行。我喜欢fiddler的演示。问:这不是纯SQL,但有点像存储过程/程序,对吗?@Menelaos第二个将在存储过程中运行。