SQL Pivot 具有不同数量的行和复合键来形成列

标签 sql sql-server sql-server-2008 tsql pivot

SQL 数据透视问题,请引用下表(注意年份和月份是分开的):

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

Raw Data

进入此:

Expected Output

请注意 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)

最佳答案

您已经非常接近最终答案了。您可以使用 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)

这将创建您在执行时所需的列列表。

关于SQL Pivot 具有不同数量的行和复合键来形成列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11742415/

相关文章:

SQL 主键

sql - 选择期间内加上最后一个期间之前的内容

sql - Oracle 插入失败 : not a valid month

sql - 查找不包含任何 NULL 值的组

sql - 如何从 SQL Server 中的表中获取 50% 的记录?

sql-server - 如何进行形态学搜索?

sql-server - 如何正确检索表 ID

sql-server - 这个表达式怎么能达到NULL表达式呢?

sql-server - sql server 2008 中的架构

sql-server - SQL Server 2008 生成一系列日期时间