SQL Server 2012中复杂的不规则透视

时间:2013-09-04 15:30:25

标签: sql tsql sql-server-2012 pivot

我有两张桌子:

a (column1, column2, column3)
b (column6, column7, column8)

a.column1b.column6中的外键。

table a中的一行有时匹配table b中的3行,有时是5行,有时是1行....没有确定的返回行数。

我有业务要求将表b中的所有相应列翻转成一行..像这样:

a.column1, a.column2, a.column3, b.column7, b.column8, b.column7, b.column8
a.column1, a.column2, a.column3, b.column7, b.column8
a.column1, a.column2, a.column3, b.column7, b.column8, b.column7, b.column8, b.column7, b.column8
a.column1, a.column2, a.column3, b.column7, b.column8, b.column7, b.column8, b.column7, b.column8b.column7, b.column8, b.column7, b.column8  

你看,表a中每行的列数总是3 ...但是从表b开始,你可能有一个可变数量的列......而且第7列和第8列必须按顺序重复出现。

我该怎么做?感谢。

1 个答案:

答案 0 :(得分:1)

听起来您需要取消隐藏然后转动数据。如果你有一个未知数量的值,你将不得不使用动态SQL,但我首先建议先编写一个硬代码或静态版本的查询,然后将其转换为动态SQL。

取消数据取消的过程将在tableB中占用您的多个列,并将其转换为多行。由于您使用的是SQL Server 2012,因此可以使用CROSS APPLY来取消数据的删除:

select column1, column2, column3,
  col = col + '_' + cast(seq as varchar(10)),
 value
from
(
  select a.column1, a.column2, a.column3,
    b.column6, b.column7, b.column8,
    row_number() over(partition by a.column1
                      order by a.column1) seq
  from tablea a
  inner join tableb b
    on a.column1 = b.column6
) d
cross apply
(
  select 'column6', column6 union all
  select 'column7', column7 union all
  select 'column8', column8
) c (col, value);

SQL Fiddle with Demo。这将给你一个类似于:

的结果
| COLUMN1 | COLUMN2 | COLUMN3 |       COL | VALUE |
|       1 |       2 |       3 | column6_1 |     1 |
|       1 |       2 |       3 | column7_1 |    18 |
|       1 |       2 |       3 | column8_1 |    56 |
|       1 |       2 |       3 | column6_2 |     1 |
|       1 |       2 |       3 | column7_2 |    25 |
|       1 |       2 |       3 | column8_2 |    89 |

正如您所看到的,您现在有多行可以轻松应用枢轴功能。 PIVOT代码将是:

select column1, column2, column3,
  column6_1, column7_1, column8_1,
  column6_2, column7_2, column8_2,
  column6_3, column7_3, column8_3
from
(
  select column1, column2, column3,
    col = col + '_' + cast(seq as varchar(10)),
    value
  from
  (
    select a.column1, a.column2, a.column3,
      b.column6, b.column7, b.column8,
      row_number() over(partition by a.column1
                        order by a.column1) seq
    from tablea a
    inner join tableb b
      on a.column1 = b.column6
  ) d
  cross apply
  (
    select 'column6', column6 union all
    select 'column7', column7 union all
    select 'column8', column8
  ) c (col, value)
) src
pivot
(
  max(value)
  for col in (column6_1, column7_1, column8_1,
              column6_2, column7_2, column8_2,
              column6_3, column7_3, column8_3)
) piv;

SQL Fiddle with Demo。由于您声明tableB中可能包含未知或动态数量的条目,因此您需要使用动态SQL。这将生成一个sql字符串,将执行该字符串以获得最终结果:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(col +'_'+cast(seq as varchar(10))) 
                    from
                    (
                      select row_number() over(partition by column6
                                                order by column6) seq
                      from tableB
                    ) t
                    cross apply
                    (
                      select 'column6', 1 union all
                      select 'column7', 2 union all
                      select 'column8', 3
                    ) c (col, so)
                    group by col, so, seq
                    order by seq, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT column1, column2, column3,' + @cols + '
            from 
            (
              select column1, column2, column3,
                col = col + ''_'' + cast(seq as varchar(10)),
                value
              from
              (
                select a.column1, a.column2, a.column3,
                  b.column6, b.column7, b.column8,
                  row_number() over(partition by a.column1
                                    order by a.column1) seq
                from tablea a
                inner join tableb b
                  on a.column1 = b.column6
              ) d
              cross apply
              (
                select ''column6'', column6 union all
                select ''column7'', column7 union all
                select ''column8'', column8
              ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute sp_executesql @query;

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

| COLUMN1 | COLUMN2 | COLUMN3 | COLUMN6_1 | COLUMN7_1 | COLUMN8_1 | COLUMN6_2 | COLUMN7_2 | COLUMN8_2 | COLUMN6_3 | COLUMN7_3 | COLUMN8_3 |
|       1 |       2 |       3 |         1 |        18 |        56 |         1 |        25 |        89 |    (null) |    (null) |    (null) |
|       2 |       4 |       6 |         2 |        78 |       245 |    (null) |    (null) |    (null) |    (null) |    (null) |    (null) |
|       3 |       8 |       9 |         3 |        10 |        15 |         3 |        45 |       457 |         3 |        89 |        50 |