【问题标题】:Spread data table based on two columns, SQL server基于两列的展开数据表,SQL server
【发布时间】:2019-06-19 05:05:11
【问题描述】:

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

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 服务器。我无法找到一种方法来执行此操作,而无需指定每个新列的名称——因为我的数据涵盖了许多频道和年份,这实际上并不可行。我知道它可能需要动态 SQL,但我没有使用它的经验。

谢谢

【问题讨论】:

  • 你为什么觉得你需要这样做?这不是一个很好的设计模式,每年您都必须向表中添加列(并删除旧的?)。你的桌子看起来很好。
  • Honeybadger - 我知道这似乎不太明智,但对于需要这种形状的数据的下游分析来说,它肯定是必需的。不幸的是,更改所有后续分析需要很长时间。
  • @rw2 我已更新答案以将结果保存在临时表中。如果您认为它有用且正确,请您接受。

标签: sql-server pivot dynamic-sql


【解决方案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

【讨论】:

  • 这也很棒。正如我问 an33sh - 有没有办法将这个 INTO 的输出保存到另一个表中?
【解决方案2】:

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

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

【讨论】:

  • 谢谢 - 这只是我想要的。我将如何将输出保存到一个表中(例如一个名为 #testTable2 的表)?我尝试在“from testTable”上方放置一条 INTO 语句,但出现了不正确的语法错误。
  • @rw2,我希望你已经用 Suraj 的回答解决了这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-28
相关资源
最近更新 更多