动态列 - SQL Server - 作为列的月份

时间:2010-02-11 21:15:03

标签: sql sql-server sql-server-2005 tsql ssis

DB:SQL Server 2005

我们有一个以这种方式拥有数据的表:

Project              Year        Jan                   Feb                   Mar                   Apr                   May                   Jun                   Jul                   Aug                   Sep                   Oct                   Nov                   Dec
-------------------- ----------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- ---------------------
11-11079             2008        0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  75244.90
11-11079             2009        466.00                0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00
11-11079             2010        855.00                0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00                  0.00  
01-11052             2009        56131.00              0.00                  36962.00              -61596.00             2428.00               84.00                 0.00                  0.00                  0.00                  0.00                  0.00                  0.00

有人希望数据显示为整个项目的一行。这些列将是动态的,取决于它未来多少年。一个例子是:

Project        Jan-2009     Feb-2009     Mar-2009     Apr-2009... Dec-2009     Jan-2010
-------------- ------------ ------------ ------------ ----------- ------------ ---------
11-11079       466.00       0.00         0.00         0.00        0.00         855.00    
01-11052       56131.00     0.00         36962.00     -61596.00   2428.00      0.00

我读过许多例子,其中日期在每个条目的一列中填充,但我没有发现任何情况,其中月份是列名称,年份在行中。

带有数据透视表的动态SQL? 或者使用SQL,临时表,联接和联合进行一些相当大规模的操作? 有关使用SSIS数据透视表功能的任何想法吗?

3 个答案:

答案 0 :(得分:4)

您的数据已经过调整,但需要在不同的级别进行调整。我认为处理此问题的最佳方法是先将其取消,然后再处理正确的枢轴级别。

第1步:取消透视

您可以使用SQL 2005 UNPIVOT命令,或使用CROSS JOIN技术。以下是两者的示例。注意我在中间省了几个月以保持简单。只需添加它们即可。

-- CROSS JOIN method (also works in SQL 2000)
SELECT
   P.Project,
   Mo =
      DateAdd(mm,
         X.MonthNum,
         DateAdd(yy, P.[Year] - 1900, '19000101')
      ),
   Amount = 
      CASE X.MonthNum
         WHEN 0 THEN Jan
         WHEN 1 THEN Feb
         WHEN 11 THEN Dec
      END
FROM
   ProjectData P
   CROSS JOIN (
      SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 11
   ) X (MonthNum)

每一行重复12次,然后CASE语句每行只抽出一个月,这样就可以很好地忽略数据。

-- UNPIVOT method
SELECT
    P.Project,
    Mo =
       DateAdd(mm,
          Convert(int, P.MonthNum),
          DateAdd(yy, P.[Year] - 1900, '19000101')
       ),
    P.Amount
FROM
   (
      SELECT Project, [Year], [0] = Jan, [1] = Feb, [11] = Dec
      FROM ProjectData
   ) X UNPIVOT (Amount FOR MonthNum IN ([0], [1], [11])) P

DROP TABLE ProjectData

这两种方法都不是一个明显的表现赢家。有时一个比另一个好(取决于被转动的数据)。 UNPIVOT方法在CROSS JOIN不执行的执行计划中使用过滤器。

第2步:再次转发

现在,如何使用不透明的数据。您没有说明某人将如何使用此功能,但由于您需要将数据放入某种类型的输出文件中,我建议使用SSRS(Sql Server Reporting Services),附带SQL Server 2005,无需额外费用。

只需使用Matrix报表对象来转移上述其中一个查询即可。此对象可以很好地确定在报表运行时生成列标签的数据值,并且听起来就像您需要的那样。如果添加一个按照您喜欢的方式格式化日期的列,则可以按Mo列排序,但使用新表达式作为列标签。

SSRS还提供各种格式和日程安排选项。例如,您可以通过电子邮件发送Excel文件或将网页保存到文件共享。

如果我遗漏了任何东西,请告诉我。

对于任何想要查看上述代码的人,这里有一些创建脚本:

USE tempdb

CREATE TABLE ProjectData (
    Project varchar(10),
    [Year] int,
    Jan decimal(15, 2),
    Feb decimal(15, 2),
    Dec decimal(15, 2)
)

SET NOCOUNT ON

INSERT ProjectData VALUES ('11-11079', 2008, 0.0, 0.0, 75244.90)
INSERT ProjectData VALUES ('11-11079', 2009, 466.0, 0.0, 0.0)
INSERT ProjectData VALUES ('11-11079', 2010, 855.0, 0.0, 0.0)
INSERT ProjectData VALUES ('01-11052', 2009, 56131.0, 0.0, 0.0)

答案 1 :(得分:0)

我编写了一个名为 pivot_query 的存储过程,可以帮助解决这个问题,来源是here,示例包含原始数据here

With your data:

create table ProjectData
   (
   Project                      varchar(20),
   [Year]                       Integer,
   Jan                          decimal(12,2),
   Feb                          decimal(12,2),
   Mar                          decimal(12,2),
   Apr                          decimal(12,2),
   May                          decimal(12,2),
   Jun                          decimal(12,2),
   Jul                          decimal(12,2),
   Aug                          decimal(12,2),
   Sep                          decimal(12,2),
   Oct                          decimal(12,2),
   Nov                          decimal(12,2),
   Dec                          decimal(12,2)
   );

insert into ProjectData values ('11-11079',2008, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 75244.90);
insert into ProjectData values ('11-11079',2009, 466.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00);
insert into ProjectData values ('11-11079',2010, 855.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00) ;
insert into ProjectData values ('01-11052',2009, 56131.00, 0.00, 36962.00, -61596.00, 2428.00, 84.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00);

declare @mySQL varchar(MAX)

set @mySQL = 'select * from ProjectData'

exec pivot_query @mySQL, 'Project', 'Year', 'max(Jan) Jan,max(Feb) Feb,max(Mar) Mar,max(Apr) Apr,max(Jun) Jun,max(Jul) Jul,max(Aug) Aug,max(Sep) Sep,max(Oct) Oct,max(Nov) Nov,max(Dec) Dec'

Results:
Project              2008_Jan     2008_Feb     2008_Mar     2008_Apr     2008_Jun     2008_Jul     2008_Aug     2008_Sep     2008_Oct     2008_Nov     2008_Dec     2009_Jan     2009_Feb     2009_Mar     2009_Apr     2009_Jun     2009_Jul     2009_Aug     2009_Sep     2009_Oct     2009_Nov     2009_Dec     2010_Jan     2010_Feb     2010_Mar     2010_Apr     2010_Jun     2010_Jul     2010_Aug     2010_Sep     2010_Oct     2010_Nov     2010_Dec
-------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------
01-11052             NULL         NULL         NULL         NULL         NULL         NULL         NULL         NULL         NULL         NULL         NULL         56131.00     .00          36962.00     -61596.00    84.00        .00          .00          .00          .00          .00          .00          NULL         NULL         NULL         NULL         NULL         NULL         NULL         NULL         NULL         NULL         NULL
11-11079             .00          .00          .00          .00          .00          .00          .00          .00          .00          .00          75244.90     466.00       .00          .00          .00          .00          .00          .00          .00          .00          .00          .00          855.00       .00          .00          .00          .00          .00          .00          .00          .00          .00          .00

不是完全但非常接近。 : - )

答案 2 :(得分:-1)

我认为你可以使用嵌套的while循环和一些动态SQL来实现。如果您无法保存最终表格,或者您必须每月重新生成所有列,这将是一个缓慢的解决方案。但是,如果它只是添加剂,那么它可能并不坏。无论如何,我就是这样做的:

  1. Out loop选择最早的年份。
  2. 内环选择第一个月。
  3. 内部循环 - 在表格中添加名称为的列。
  4. 内部循环 - 使用动态SQL
  5. 的新列的所有信息更新表
  6. 每月迭代内循环
  7. 每年迭代一次外循环。