SQL中多个表和动态列的枢轴函数

时间:2017-03-15 10:49:00

标签: sql-server pivot

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

例如AccountManager Table 2来自Table 3 Jack / Kate 类似地,其他列及其值是

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

enter image description here

我想要像这样的结果

enter image description here

我尝试使用Pivot Function并使用以下查询:

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

但是查询给出了结果:

enter image description here

我需要更改什么来实现我想要的输出?

1 个答案:

答案 0 :(得分:9)

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

首先,让我们将您的表结构和示例数据放入可重用的脚本中。您似乎只需要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查询。您的最终结果仅包含3列CTypeIdValueColumnName中的值,因此查询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只会返回一行:

+---------+-----------------+---------+------------+------------+---------------+
| 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 |
+---------+-----------------+---------+------------+------------+---------------+

这不是你想要的,所以你需要做一些事情以允许多行。如果查看子查询返回的数据样本:

+---------+-------------+------------------+
| 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的灵活性。