基于两列传播数据 table,SQL 服务器

Spread data table based on two columns, SQL server

我有一个 table 我想重新整形,目前看起来像这样:

ID    Year    Channel    Payments
 1    2012         HV         100
 1    2014         HV          56
 2    2012         NL       17000
 2    2012         HV         495
 3    2013         HV         565

等等...有很多行。

我需要重新调整数据的形状,以便每个 ID 一行,多列显示每年和每个渠道的付款。例如,上面的 table 看起来像这样:

ID   HV2012   HV2013   HV2014   NL2012
 1      100     NULL       56     NULL
 2      495     NULL     NULL    17000
 3     NULL      565     NULL     NULL

过去我在 R 中使用 dplyr 可以很容易地做到这一点,但现在需要使用 SQL 服务器。我一直无法找到无需指定每个新列的名称即可执行此操作的方法 - 因为我的数据涵盖许多频道和年份,这实际上并不可行。我知道它可能需要动态 SQL,但我没有使用它的经验。

谢谢

您可以使用动态数据透视查询来实现这一点。

-- data

create table testTable
(
    Id int,
    Year int,
    Channel varchar(10),
    Payments int
)

 insert into testTable values (1,2012,'HV',100)
 insert into testTable values (1,2014,'HV',56)
 insert into testTable values (2,2012,'NL',17000)
 insert into testTable values (2,2012,'HV',495)
 insert into testTable values (3,2013,'HV',565)


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

SET @cols = STUFF((SELECT distinct ',' + QUOTENAME(CONCAT(c.Channel,c.Year))
            FROM testTable c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = 'SELECT Id, ' + @cols + ' from 
            (
                select Id
                    , CONCAT(Channel,Year ) YC
                    , Payments
                from testTable
           ) x
            pivot 
            (
                 max(Payments)
                for YC in (' + @cols + ')
            ) p '

execute(@query)

您也可以在较低版本的 SQL 服务器中找到 运行 的类似查询。

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

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(Channel + Convert(Varchar(4) ,Year))
            FROM testTable c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT Id, ' + @cols + ' from 
            (
                select Id, Payments,
                Channel + Convert(Varchar(4) ,Year) NewCol
                from testTable
           ) x
            pivot 
            (
                 max(Payments)
                for NewCol in (' + @cols + ')
            ) p '

execute(@query)

您可以找到现场演示 here

编辑

要在临时文件中保存数据 table 您可以尝试以下查询。

create table MyTable
(
    Id int,
    Year int,
    Channel varchar(10),
    Payments int
)

insert into MyTable values 
               (1,2012,'HV',100),
               (1,2014,'HV',56),
               (2,2012,'NL',17000),
               (2,2012,'HV',495), 
               (3,2013,'HV',565)


Declare @SQL varchar(max) = '    
if object_id(''##TestTable'') is not null
begin
    drop table ##TestTable
end

create table ##TestTable([Id] int null, ' + 
            Stuff((Select Distinct ','+QuoteName(Channel + CONVERT(Varchar(4), Year)) + ' Varchar(20) null'            
            From [dbo].MyTable
            Order By 1 
            For XML Path('')),1,1,'')+ ')
INSERT INTO ##TestTable
Select *
 From (
        Select A.ID, A.Payments
              ,B.*
         From  [dbo].MyTable a 
         Cross Apply ( values ( Channel + CONVERT(Varchar(4), Year)
                     )) B ([Value])
      ) S
 Pivot (sum([Payments]) For [Value] in 
 (' + Stuff((Select Distinct ','+QuoteName(Channel + CONVERT(Varchar(4), Year))                                                               
                                               From myTable
                                               Order By 1 
                                               For XML Path('')),1,1,'')  + ') ) p'

--SELECT @SQL

Exec(@SQL)

SELECT * FROM ##TestTable

您可以找到演示 here