如何在开始日期和结束日期之间循环/转动以显示每月费用

时间:2013-02-19 03:26:25

标签: sql sql-server-2008 tsql pivot

我有一个包含PROJECTNAME,ACTUALCOST,PROJECTSTART和PROJECTEND列的表格,如下所示:

PROJECTNAME          ACTUAL COST          PROJECTSTART          PROJECTEND
abc                  1500                 2011-12-01            2012-07-31
prj1                 1170                 2012-01-09            2012-06-30  
xyz                  5350                 2012-01-30            2012-03-30

我想获得如下输出:

PRJNAME     DEC11     JAN12     FEB12     MAR12     APR12     MAY12     JUN12     JUL12 ...
abc         187.5     187.5     187.5     187.5     187.5     187.5     187.5     187.5
prj1                  117       195       195       195       195       195

我需要每个项目名称的每月费用细分,具体取决于每个月剩余的天数。

1 个答案:

答案 0 :(得分:1)

您可以使用PIVOT功能获取结果。

我建议这样做的方法是首先编写一个硬编码版本,然后将其转换为动态sql。

注意:我通过将Actual Cost除以projectstart / projectend之间的天数来确定每月的费用。这至少应该让您开始实际报告。

静态版本与此类似:

;with cte as
(
  select projectname, [ACTUAL COST], [PROJECTSTART], [PROJECTEND]
  from yourtable
  union all
  select projectname, [ACTUAL COST], 
    dateadd(d, 1, PROJECTSTART),
    PROJECTEND
  from cte
  where dateadd(d, 1, PROJECTSTART) <= ProjectEnd
) 
select *
from
(
  select 
    my.projectname,
    my.monthyear,
    my.totaldayspermonth * a.perdaycost AmountPerMonth
  from
  (
    select projectname,
    left(datename(m, projectstart), 3) + cast(year(projectstart) as varchar(4)) monthyear,
    count(*) TotalDaysPerMonth
    from cte
    group by projectname,
      [actual cost],
      left(datename(m, projectstart), 3) + cast(year(projectstart) as varchar(4))
  ) my
  cross apply
  (
    select projectname, 
      round([actual cost] / (datediff(d, projectstart, projectend) *1.0), 2) PerDayCost
    from yourtable a
    where my.projectname = a.projectname
  ) a
) src
pivot
(
  max(AMOUNTPERMONTH)
  for monthyear in (Dec2011, Jan2012, Feb2012, Mar2012,
                    Apr2012, May2012, Jun2012, Jul2012, Aug2012)
) piv
OPTION(MAXRECURSION 0);

请参阅SQL Fiddle with Demo

一旦拥有静态版本,就可以更轻松地将其转换为动态SQL。动态SQL将是:

;with cte as
(
  select projectname, [ACTUAL COST], [PROJECTSTART], [PROJECTEND]
  from yourtable
  union all
  select projectname, [ACTUAL COST], 
    dateadd(d, 1, PROJECTSTART),
    PROJECTEND
  from cte
  where dateadd(d, 1, PROJECTSTART) <= ProjectEnd
) 
select *
into #dates
from cte
OPTION(MAXRECURSION 0)

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(left(datename(m, projectstart), 3) + cast(year(projectstart) as varchar(4))) 
                    from #dates
                    group by datename(m, projectstart), year(projectstart), month(projectstart)
                    order by year(projectstart), month(projectstart)
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = ';with cte as
              (
                select projectname, [ACTUAL COST], [PROJECTSTART], [PROJECTEND]
                from yourtable
                union all
                select projectname, [ACTUAL COST], 
                  dateadd(d, 1, PROJECTSTART),
                  PROJECTEND
                from cte
                where dateadd(d, 1, PROJECTSTART) <= ProjectEnd
              ) 
              select projectname, '+@cols+' 
              from
              (
                select 
                  my.projectname,
                  my.monthyear,
                  my.totaldayspermonth * a.perdaycost AmountPerMonth
                from
                (
                  select projectname,
                  left(datename(m, projectstart), 3) + cast(year(projectstart) as varchar(4)) monthyear,
                  count(*) TotalDaysPerMonth
                  from cte
                  group by projectname,
                    [actual cost],
                    left(datename(m, projectstart), 3) + cast(year(projectstart) as varchar(4))
                ) my
                cross apply
                (
                  select projectname, 
                    round([actual cost] / (datediff(d, projectstart, projectend) *1.0), 2) PerDayCost
                  from yourtable a
                  where my.projectname = a.projectname
                ) a
              ) src
              pivot
              (
                max(AMOUNTPERMONTH)
                for monthyear in ('+@cols+')
              )piv
              OPTION(MAXRECURSION 0)'

execute(@query)

请参阅SQL Fiddle with Demo

两个查询的结果是:

| PROJECTNAME | DEC2011 | JAN2012 | FEB2012 | MAR2012 | APR2012 | MAY2012 | JUN2012 | JUL2012 |
-----------------------------------------------------------------------------------------------
|         abc |  191.27 |  191.27 |  178.93 |  191.27 |   185.1 |  191.27 |   185.1 |  191.27 |
|        prj1 |  (null) |  155.48 |  196.04 |  209.56 |   202.8 |  209.56 |   202.8 |  (null) |
|         xyz |  (null) |  178.34 | 2585.93 |  2675.1 |  (null) |  (null) |  (null) |  (null) |