【问题标题】:How do I Pivot Vertical Data to Horizontal Data SQL with Variable Row Lengths?如何将垂直数据透视到具有可变行长的水平数据 SQL?
【发布时间】:2013-04-02 15:30:03
【问题描述】:

好的,我有下表。

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。

【问题讨论】:

    标签: sql sql-server-2005 pivot unpivot


    【解决方案1】:

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

    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 |
    

    如您所见,我在 unpivot 之前对数据应用了 row_number(),行号用于生成新的列名。 UNPIVOT 中的列也必须是相同的数据类型,我将cast 应用于子查询中的id 列,以便在数据透视之前将数据转换为varchar

    然后在 PIVOT 中使用 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 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 |
    

    【讨论】:

    • 我喜欢 fiddler 的演示。问题:这不是纯 SQL,而是有点像存储过程/程序吧?
    • @Menelaos 第二个将在存储过程中运行。
    猜你喜欢
    • 1970-01-01
    • 2013-08-09
    • 2020-02-25
    • 1970-01-01
    • 2021-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多