SQL从月总数中获取每日平均值

发布于 2024-12-18 16:38:34 字数 1850 浏览 2 评论 0原文

我有一个表列出了月份总计(目标),

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 ????

I have a table that lists month totals (targets)

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      
.
.

but month is an integer(!)

I also have another table that has a list of working days (calendar)

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 .....
.
.
.

What I want to do is get a list of dates with the average for that day based on the number of days in the month where day_type is 'WORKING_DAY' / the month's total.

so if I had say 20 working days in 201005 then I'd get an average of 114/20 on each working day, while the other days would be 0.

somthing like

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

It has to be done as a CTE as this is a limitation of the target system (I can only do one statement)
I can start off with (Dates to

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 ????

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

策马西风 2024-12-25 16:38:34

示例数据(可能有所不同):

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 语句,如果当天是“周末”或者 calendar 表中不存在,则返回 0。请记住,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) 

Sample data (may vary):

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 statement, it returns 0 if the day is 'weekend' or not exists in calendar table. Please keep in mind that MAXRECURSION is a value between 0 and 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) 
在巴黎塔顶看东京樱花 2024-12-25 16:38:34

您可以在子查询中计算每月的工作日数。只有子查询必须使用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

SE Data 的工作示例。
有关 convert 中的“幻数”112,请参阅 MSDN 页面

You could calculate the number of working days per month in a subquery. Only the subquery would have to use group by. For example:

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.
For the "magic number" 112 in convert, see the MSDN page.

剩一世无双 2024-12-25 16:38:34

如果我正确理解你的问题,下面的查询应该这样做:

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

用简单的英语:

  • 对于日历中的每一行,
  • 如果该行是工作日,则获取相应月份的总计(否则获取 NULL) ,
  • 获取当月的工作日数
  • 并除以它们。
  • 最后将NULL(非工作日)转为0。

If I understood your question correctly, the following query should do it:

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

In plain English:

  • For each row in calendar,
  • get the total of the corresponding month if this row is a working day (otherwise get NULL),
  • get the number of working days in the same month
  • and divide them.
  • Finally, convert the NULL (of non-working days) into 0.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文