SQL 中多个表和动态列的数据透视函数

Pivot Function on Multiple Tables and Dynamic Columns in SQL

我在 Table 2 中有 3 个表,我们有包含 columnName 字段的列,那时它们可以动态增长,我们每个只有 5 列 CTypeId 它们可以是 6 或 10等。在 Table3 中,我们有列值。

例如 AccountManager 来自 Table 2 的值在 Table 3 Jack / Kate 同样,其他列及其值是

ColumnName |  Values
Channel    |  PS
StartDate  |  06/03/2017

我想要这样的结果

我已尝试将数据透视函数用于以下查询:

Declare @Columns nvarchar(max) 
Declare @a nvarchar(max)
Set @Columns = (select STUFF((select ',' + '[' + Convert(varchar(200), ColumnName) + ']' from CharityTypeInformationDynamicFields FOR XML PATH('')), 1,1, ''))

    Declare @sql nvarchar(max) 
       = 'Select * 
          from
          (select cd.Id, cd.Value, ci.ColumnName 
           from Table3 cd 
           Inner Join Table2         ci 
               on ci.Id = cd.DynamicFieldID
          ) as s 
          Pivot(MAX(Value) ForColumnName IN ('+@columns+')) as pvt'
    Select @sql

但是查询给出结果:

我需要更改什么才能获得所需的输出?

为了获得您想要的结果,您需要解决一些问题。但是在尝试查询的动态 sql 版本之前,我总是建议您先尝试通过编写硬编码或静态版本来获得最终结果。这使您可以在没有错误的情况下获得所需的结果,然后将其转换为动态 sql 作为最终查询。

首先,让我们将您的 table 结构和示例数据放入可重用的脚本中。看来您只需要 table2table3 即可获得最终结果:

create table #table2
(
  id int,
  ctypeid int,
  columnname varchar(50)
)

insert into #table2
values
(1, 20, 'Account Manager'), (2, 20, 'Channel'),
(3, 20, 'Start Date'), (4, 20, 'End Date'),
(5, 20, 'Gross Annual'), (6, 6, 'Account Manager'),
(7, 6, 'Channel'), (8, 6, 'Start Date'),
(9, 6, 'End Date'), (10, 6, 'Gross Annual');

create table #table3
(
  id int,
  table2id int,
  value varchar(50)
)

insert into #table3
values
(1, 1, 'Jack / Kate'), (2, 2, 'PS'), (3, 3, '06/03/2017'), 
(4, 4, '07/03/2017'), (5, 5, '2500'), (6, 6, 'Ollie'), 
(7, 7, 'D2D'), (8, 8, '06/03/2017'), (9, 9, '06/03/2017'), 
(10, 10, '5232'), (11, 1, 'Jack'), (12, 2, 'PSP'), 
(13, 3, '06/03/2017'), (14, 4, '07/03/2017'), (15, 5, '7000'),
(16, 1, 'Jack Sparrow'), (17, 2, 'PS Sparrow'), (1, 3, '06/03/2017'), 
(19, 4, '07/03/2017'), (20, 5, '3000'), (21, 6, 'John'), 
(22, 7, 'JEDF'), (23, 8, '06/03/2017'), (24, 9, '06/03/2017'), 
(25, 10, '5232');

接下来,您需要编写 PIVOT 查询。您的最终结果仅包括来自 CTypeIdValueColumnName 3 列的值,因此查询 PIVOT 的开始将是:

select 
  CTypeId,
  [Account Manager], [Channel], [Start Date], 
  [End Date], [Gross Annual]
from 
(
   select ci.CTypeId, cd.Value, ci.ColumnName
   from #Table3 cd 
   Inner Join #Table2 ci 
     on ci.Id = cd.Table2Id
) d
pivot
(
  max(Value)
  for ColumnName in ([Account Manager], [Channel], [Start Date], 
                      [End Date], [Gross Annual])
) piv

Demo。但是由于您在 Value 列中聚合字符串值,因此每个 CTypeId:

只会 return 一行
+---------+-----------------+---------+------------+------------+---------------+
| CTypeId | Account Manager | Channel | Start Date |  End Date  | Gross Annual  |
+---------+-----------------+---------+------------+------------+---------------+
|       6 | Ollie           | JEDF    | 06/03/2017 | 06/03/2017 |          5232 |
|      20 | Jack Sparrow    | PSP     | 06/03/2017 | 07/03/2017 |          7000 |
+---------+-----------------+---------+------------+------------+---------------+

这不是你想要的,所以你需要做一些事情来允许多行。如果您查看由子查询 return 编辑的数据示例:

+---------+-------------+------------------+
| CTypeId |    Value    | ColumnName       |
+---------+-------------+------------------+
|      20 | Jack / Kate | Account Manager  |
|      20 | PS          | Channel          |
|      20 | 06/03/2017  | Start Date       |
|      20 | 07/03/2017  | End Date         |
|      20 | 2500        | Gross Annual     |
|       6 | Ollie       | Account Manager  |
|       6 | D2D         | Channel          |
|       6 | 06/03/2017  | Start Date       |
|       6 | 06/03/2017  | End Date         |
|       6 | 5232        | Gross Annual     |
+---------+-------------+------------------+

您会看到您拥有 CTypeIdColumnName 值组合的唯一数据,因此您可以使用窗口函数 row_number 在您的可用于对数据进行唯一分组的子查询。通过将上面的 PIVOT 代码更改为:

select 
  CTypeId,
  [Account Manager], [Channel], [Start Date], 
  [End Date], [Gross Annual]
from 
(
   select ci.CTypeId, cd.Value, ci.ColumnName,
     rn = row_number() over(partition by ci.CTypeId, ci.ColumnName order by cd.Value)
   from #Table3 cd 
   Inner Join #Table2 ci 
     on ci.Id = cd.Table2Id
) d
pivot
(
  max(Value)
  for ColumnName in ([Account Manager], [Channel], [Start Date], 
                      [End Date], [Gross Annual])
) piv
order by CTypeId

See demo,你得到了想要的结果:

+---------+-----------------+------------+------------+------------+---------------+
| CTypeId | Account Manager |  Channel   | Start Date |  End Date  | Gross Annual  |
+---------+-----------------+------------+------------+------------+---------------+
|       6 | John            | D2D        | 06/03/2017 | 06/03/2017 |          5232 |
|       6 | Ollie           | JEDF       | 06/03/2017 | 06/03/2017 |          5232 |
|      20 | Jack            | PS         | 06/03/2017 | 07/03/2017 |          2500 |
|      20 | Jack / Kate     | PS Sparrow | 06/03/2017 | 07/03/2017 |          3000 |
|      20 | Jack Sparrow    | PSP        | 06/03/2017 | 07/03/2017 |          7000 |
+---------+-----------------+------------+------------+------------+---------------+

获得所需的最终结果后,很容易将查询转换为动态查询 SQL:

Declare @Columns nvarchar(max) 
Declare @a nvarchar(max)
Set @Columns = stuff((select distinct ',' + quotename(ColumnName) 
                   from #table2
                   for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');

Declare @sql nvarchar(max) 
 = 'Select CTypeId, '+@Columns+'
    from
    (
      select ci.CTypeId, cd.Value, ci.ColumnName,
        rn = row_number() over(partition by  ci.CTypeId, ci.ColumnName order by cd.Value)
      from #Table3 cd 
      Inner Join #Table2         ci 
        on ci.Id = cd.Table2Id
    ) as s 
    Pivot(MAX(Value) For ColumnName IN ('+@columns+')) as pvt
    order by CTypeId'


execute(@sql);

See Demo。这给出了与具有动态 sql 灵活性的硬编码版本相同的结果。