基于两列的SQL Server传播数据表

时间:2019-01-25 11:01:53

标签: sql-server pivot dynamic-sql

我有一张要重塑的桌子,目前看起来像这样:

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一行一行,并在多列中分别显示各年和每个频道的付款。例如,上表将如下所示:

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

过去我可以在R中使用dplyr来轻松实现此目的,但现在需要使用SQL Server。我无法找到一种无需指定每个新列名称的方法-由于我的数据涵盖了许多渠道和年份,因此这实际上是不可行的。我了解它可能需要动态SQL,但是我对此没有经验。

谢谢

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

您可以找到类似的查询,该查询也将在较低版本的SQL Server中运行。

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

编辑

要将数据保存在临时表中,您可以尝试以下查询。

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