4

SQL pivot 问题有一个转折,请看下表(注意年份和月份是分开的):

CREATE TABLE [dbo].[tbl_BranchTargets]
    (
      [BranchID] [varchar](4) NOT NULL ,
      [Year] [smallint] NOT NULL ,
      [Month] [smallint] NOT NULL ,
      [Target] [int] NULL ,
      CONSTRAINT [PK_tbl_BranchTargets] PRIMARY KEY CLUSTERED ( [BranchID] ASC, [Year] ASC, [Month] ASC ) WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]
    )
ON  [PRIMARY]

以及以下虚拟数据:

INSERT [dbo].[tbl_BranchTargets] ([BranchID], [Year], [Month], [Target]) VALUES (N'001', 2012, 4, 1)
INSERT [dbo].[tbl_BranchTargets] ([BranchID], [Year], [Month], [Target]) VALUES (N'001', 2012, 5, 117)
INSERT [dbo].[tbl_BranchTargets] ([BranchID], [Year], [Month], [Target]) VALUES (N'001', 2012, 6, 233)
INSERT [dbo].[tbl_BranchTargets] ([BranchID], [Year], [Month], [Target]) VALUES (N'001', 2012, 7, 386)
INSERT [dbo].[tbl_BranchTargets] ([BranchID], [Year], [Month], [Target]) VALUES (N'003', 2012, 4, 2)
INSERT [dbo].[tbl_BranchTargets] ([BranchID], [Year], [Month], [Target]) VALUES (N'003', 2012, 6, 234)
INSERT [dbo].[tbl_BranchTargets] ([BranchID], [Year], [Month], [Target]) VALUES (N'003', 2012, 7, 387)

我将如何像这样对给定的虚拟数据建模(注意年和月键列合并形成 YYYYMM):

原始数据

进入这个:

预期产出

注意 5 月分支 3 的缺失条目,这需要作为 null 处理。例如,在我们的 12 个月中,一个分支可能只有其中 1 个的目标,因此所有其他月份都需要为空。

我已经研究过 PIVOT() 和笨重的光标选项,但我正在努力寻找一种快速的最佳实践方法,我假设我需要实现一些动态 SQL + PIVOT() - 但不能完全明白我的头圆它。

我知道对于动态枢轴,您首先要识别列名(我认为),我可以这样做:

DECLARE @Columns AS NVARCHAR(MAX);
DECLARE @StrSQL AS NVARCHAR(MAX); 

SET @Columns = STUFF((SELECT DISTINCT
                            ',' + QUOTENAME(CONVERT(VARCHAR, c.YEAR) + RIGHT('00' + CONVERT(VARCHAR, c.MONTH), 2))
                   FROM     tbl_BranchTargets c
    FOR           XML PATH('') ,
                      TYPE 
            ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') 

但是您执行数据透视的方式有点超出我的范围(因为我本质上是合并到关键列以创建最终列) - 在尝试将 YYYY + MM 定义为 1 列中的值的数据透视之前,我是否需要合并数据?

(我使用的是 SQL Server 2008 R2)

4

3 回答 3

4

你很亲密。试试这个:

DECLARE @Columns AS NVARCHAR(MAX)
DECLARE @StrSQL AS NVARCHAR(MAX)

SET @Columns = STUFF((SELECT DISTINCT
                            ',' + QUOTENAME(CONVERT(VARCHAR(4), c.YEAR) + RIGHT('00' + CONVERT(VARCHAR(2), c.MONTH), 2))
                   FROM     tbl_BranchTargets c
    FOR           XML PATH('') ,
                      TYPE 
            ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') 

SET @StrSQL = '
SELECT *
FROM (SELECT BranchId, 
             CONVERT(VARCHAR(4), [YEAR]) + RIGHT(''00'' + CONVERT(VARCHAR(2), [MONTH]), 2) YearMonth,
             Target
      FROM [dbo].[tbl_BranchTargets]) T
PIVOT(MIN(Target) FOR YearMonth IN ('+@Columns+')) AS PT'

EXEC(@StrSQL)

是一个查看结果的sqlfiddle。

于 2012-07-31T14:38:23.477 回答
4

你非常接近最终答案。您可以使用PIVOT类似于以下内容(参见SQL Fiddle with Demo):

DECLARE @Columns AS NVARCHAR(MAX)
DECLARE @StrSQL AS NVARCHAR(MAX) 

SET @Columns = STUFF((SELECT DISTINCT
                            ',' + QUOTENAME(CONVERT(VARCHAR(4), c.YEAR) + RIGHT('00' + CONVERT(VARCHAR(2), c.MONTH), 2))
                   FROM     tbl_BranchTargets c
    FOR           XML PATH('') ,
                      TYPE 
            ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') 

set @StrSQL = 'SELECT branchid, ' + @Columns + ' from 
            (
                select branchid
                    , target
                    , CONVERT(VARCHAR(4), [YEAR]) + RIGHT(''00'' + CONVERT(VARCHAR(2), [MONTH]), 2) dt
                from tbl_BranchTargets
           ) x
            pivot 
            (
                sum(target)
                for dt in (' + @Columns + ')
            ) p '


execute(@StrSQL)

这将在执行时创建您想要的列列表。

于 2012-07-31T14:38:26.573 回答
3

您有两个问题 - 构建周期和旋转

这建立了周期...

declare @strPeriod nvarchar(1000)
select @strPeriod=N''
select @strPeriod = @strPeriod + ', ['+YearMonth +']'
from
(
     select distinct convert(varchar(4),tbl_BranchTargets.Year)
     + right('0'+convert(varchar(2),tbl_BranchTargets.Month),2) as YearMonth 
     from tbl_BranchTargets
) src

select @strPeriod = substring(@strPeriod, 3,LEN(@strPeriod))

这是关键

declare @sql nvarchar(4000)

select @sql = N'select * from 
     (select BranchID, convert(varchar(4),tbl_BranchTargets.Year)+right(''0''+
     convert(varchar(2),tbl_BranchTargets.Month),2) as YearMonth, Target from tbl_BranchTargets) 
     src 
     PIVOT 
     (sum(target) for YearMonth in ('+@strPeriod+'))p' 

exec (@sql)
于 2012-07-31T14:36:37.090 回答