SQL从每月总计获得每日平均值

时间:2011-12-01 05:53:35

标签: sql group-by common-table-expression

我有一个列出月总数(目标)的表

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个工作日,那么每个工作日平均得到114/20,而其他日子则为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 ????

3 个答案:

答案 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

答案 1 :(得分: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),
  • 获取同月的工作天数
  • 并将它们分开。
  • 最后,将NULL(非工作日)转换为0。

答案 2 :(得分: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

选择语句,如果日期为“周末”,则返回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)