0

我有两张桌子:

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

a.column1是外键,如b.column6.

一行 fromtable a有时匹配 3 行table b,有时 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 中,您可能有可变数量的列……并且 column7 和 column8 必须以该顺序重复出现。

我怎样才能做到这一点?谢谢。

4

1 回答 1

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 |
于 2013-09-04T15:45:10.620 回答