2011-12-01 92 views
1

我有一個列出每月總計(目標)SQL從每月總得到一個平均每天

person  total     month  
----------- --------------------- ----------- 
1001  114.00    201005  
1001  120.00    201006  
1001  120.00    201007  
1001  120.00    201008  
. 
1002  114.00    201005  
1002  222.00    201006  
1002  333.00    201007  
1002  111.00    201008  
. 
. 

但一個月是一個整數(!)

我也有,有一個其它表的表工作日列表(日曆)

tran_date    day_type 
----------------------- --------------------------------- 
1999-05-01 00:00:00.000 WEEKEND 
1999-05-02 00:00:00.000 WEEKEND 
1999-05-03 00:00:00.000 WORKING_DAY 
1999-05-04 00:00:00.000 WORKING_DAY 

1999-06-01 00:00:00.000 ..... 
. 
. 
. 

我想要做的就是日期列表基於天的月份數平均爲每日裏day_type是「WORKING_DAY」 /當月總。

,所以如果我不得不說,在201005起20個工作日內,然後我會得到20分之114在每個工作日的平均,而其他日子會是0

財產以後像

person tran_date    day_avg 
------- ----------------------- --------------------------------- 
1001  2010-05-01 00:00:00.000 0 
1001  2010-05-02 00:00:00.000 0 
1001  2010-05-03 00:00:00.000 114/2 (as there are two working days) 
1001  2010-05-04 00:00:00.000 114/2 (as there are two working days) 
. 
. 
. 

它必須做的CTE,因爲這是目標系統的限制(我只能做一個語句) 我可以(日期開始到

WITH 
Dates AS 
(
    SELECT CAST('19990501' as datetime) TRAN_DATE 
    UNION ALL 
    SELECT TRAN_DATE + 1 
    FROM Dates 
    WHERE TRAN_DATE + 1 <= CAST('20120430' as datetime) 
), 
Targets as 
(
    select CAST(cast(month as nvarchar) + '01' as dateTime) mon_start, 
      DATEADD(MONTH, 1, CAST(cast(month as nvarchar) + '01' as dateTime)) mon_end, 
      total 
    from targets 
) 
select ???? 

回答

1

採樣數據(可能會有所不同):

select * into #totals from (
select '1001' as person, 114.00 as total, 199905 as month union 
select '1001', 120.00, 199906 union 
select '1001', 120.00, 199907 union 
select '1001', 120.00, 199908 

) t 

select * into #calendar from (
select cast('19990501' as datetime) as tran_date, 'WEEKEND' as day_type union 
select '19990502', 'WEEKEND' union 
select '19990503', 'WORKING_DAY' union 
select '19990504', 'WORKING_DAY' union 
select '19990505', 'WORKING_DAY' union 
select '19990601', 'WEEKEND' union 
select '19990602', 'WORKING_DAY' union 
select '19990603', 'WORKING_DAY' union 
select '19990604', 'WORKING_DAY' union 
select '19990605', 'WORKING_DAY' union 
select '19990606', 'WORKING_DAY' union 
select '19990701', 'WORKING_DAY' union 
select '19990702', 'WEEKEND' union 
select '19990703', 'WEEKEND' union 
select '19990704', 'WORKING_DAY' union 
select '19990801', 'WORKING_DAY' union 
select '19990802', 'WORKING_DAY' union 
select '19990803', 'WEEKEND' union 
select '19990804', 'WEEKEND' union 
select '19990805', 'WORKING_DAY' union 
select '19990901', 'WORKING_DAY' 
) t 

Select語句,返回0,如果這一天是「週末'或不存在於calendar表中。請注意,MAXRECURSION是介於0和32,767之間的值。

;with dates as ( 
    select cast('19990501' as datetime) as tran_date 
    union all 
    select dateadd(dd, 1, tran_date) 
    from dates where dateadd(dd, 1, tran_date) <= cast('20010101' as datetime) 
) 
select t.person , d.tran_date, (case when wd.tran_date is not null then t.total/w_days else 0 end) as day_avg 
from dates d 
left join #totals t on 
    datepart(yy, d.tran_date) * 100 + datepart(mm, d.tran_date) = t.month 
left join ( 
     select datepart(yy, tran_date) * 100 + datepart(mm, tran_date) as month, count(*) as w_days 
     from #calendar 
     where day_type = 'WORKING_DAY' 
     group by datepart(yy, tran_date) * 100 + datepart(mm, tran_date) 
) c on t.month = c.month 
left join #calendar wd on d.tran_date = wd.tran_date and wd.day_type = 'WORKING_DAY' 
where t.person is not null 
option(maxrecursion 20000) 
+0

我更新了答案以擺脫不需要的結果。感謝你 - 這是完美的! –

1

你可以計算子查詢中每月的工作日數。只有子查詢才需要使用group by。例如:

select t.person 
,  wd.tran_date 
,  t.total/m.WorkingDays as day_avg 
from  @Targets t 
join  @WorkingDays wd 
on  t.month = convert(varchar(6), wd.tran_date, 112) 
left join 
     (
     select convert(varchar(6), tran_date, 112) as Month 
     ,  sum(case when day_type = 'WORKING_DAY' then 1 end) as WorkingDays 
     from @WorkingDays 
     group by 
       convert(varchar(6), tran_date, 112) 
     ) as m 
on  m.Month = t.month 

Working example at SE Data.
爲 「魔數」 在convert 112,請參閱the MSDN page

+0

好的答案 - 我必須選擇另一個,因爲這會忽略平均不適用的日子(非工作日)。謝謝,我從這個答案中學到了新的技巧。 –

1

如果我理解正確你的問題,下面的查詢應該這樣做:

SELECT 
    *, 
    ISNULL(
     (
      SELECT total 
      FROM targets 
      WHERE 
       MONTH(tran_date) = month - ROUND(month, -2) 
       AND c1.day_type = 'WORKING_DAY' 
     )/
     (
      SELECT COUNT(*) 
      FROM calendar c2 
      WHERE 
       MONTH(c1.tran_date) = MONTH(c2.tran_date) 
       AND c2.day_type = 'WORKING_DAY' 
     ), 
     0 
    ) day_avg 
FROM 
    calendar c1 

用簡單的英語:

  • 對於calendar每一行,
  • 得到總相應的月,如果這一行是工作日(否則爲空),
  • 獲得同一個月的工作天數
  • 並將它們分開。
  • 最後,轉換NULL(非工作日)到0
+0

謝謝,但是當我擁有更多不同的人時,這種情況就會失敗。對不起我在這個問題上的錯誤我應該說清楚。不錯,儘管。我從解釋中學到了很多東西。 –