SQL将月份值传播到几周

时间:2014-05-10 07:29:28

标签: sql sql-server date

我有一个表格,我按月分配值,我希望按周分配这些值,考虑到分散到两个月的周需要参考每个月的价值和数量的重量每月对应的天数。

例如,我每月都有不同钢材价格的表

Product         Month       Price
------------------------------------
Steel   1/Jan/2014    100
Steel   1/Feb/2014    200
Steel   1/Mar/2014    300

我需要将其转换为数周,如下所示

Product Week        Price
-------------------------------------------
Steel   06-Jan-14   100
Steel   13-Jan-14   100
Steel   20-Jan-14   100
Steel   27-Jan-14   128.57
Steel   03-Feb-14   200
Steel   10-Feb-14   200
Steel   17-Feb-14   200

如上所述,1月和2月之间重叠的一周需要计算如下

(100*5/7)+(200*2/7)

考虑到27日的一周有5天到1月和2到2月。

有没有办法在SQL中创建一个可以实现此目的的查询?

我尝试了以下

首次尝试:

select
WD.week,
PM.PRICE,
DATEADD(m,1,PM.Month),
SUM(PM.PRICE/7) * COUNT(*)
from
(   select '2014-1-1' as Month, 100 as PRICE
    union
    select '2014-2-1' as Month, 200 as PRICE
)PM
join
(   select '2014-1-20' as week
    union
    select '2014-1-27' as week
    union
    select '2014-2-3' as week
)WD
ON WD.week>=PM.Month
AND WD.week < DATEADD(m,1,PM.Month)
group by
WD.week,PM.PRICE, DATEADD(m,1,PM.Month)

这给了我以下

week    PRICE   
2014-1-20   100 2014-02-01 00:00:00.000 14
2014-1-27   100 2014-02-01 00:00:00.000 14
2014-2-3    200 2014-03-01 00:00:00.000 28

我也试过以下

;with x as (
    select price, 
        datepart(week,dateadd(day, n.n-2, t1.month)) wk, 
        dateadd(day, n.n-1, t1.month) dt
    from 
    (select '2014-1-1' as Month, 100 as PRICE
    union
    select '2014-2-1' as Month, 200 as PRICE) t1
    cross apply (
        select datediff(day, t.month, dateadd(month, 1, t.month)) nd
        from 
        (select '2014-1-1' as Month, 100 as PRICE
        union
        select '2014-2-1' as Month, 200 as PRICE)
        t
        where t1.month = t.month) ndm
    inner join 
        (SELECT (a.Number * 256) + b.Number AS N FROM
        (SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255) a (Number), 
        (SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255) b (Number)) n --numbers 
    on n.n <= ndm.nd
)
select min(dt) as week, cast(sum(price)/count(*) as decimal(9,2)) as price
from x
group by wk
having count(*) = 7
order by wk

这就是我的以下

week                        price
2014-01-07 00:00:00.000 100.00
2014-01-14 00:00:00.000 100.00
2014-01-21 00:00:00.000 100.00
2014-02-04 00:00:00.000 200.00
2014-02-11 00:00:00.000 200.00
2014-02-18 00:00:00.000 200.00    

由于

2 个答案:

答案 0 :(得分:1)

实际上,您需要在几天内将其分解,然后按周计算平均值。为了获得这些日子,我们将使用Numbers table

;with x as (
    select product, price, 
        datepart(week,dateadd(day, n.n-2, t1.month)) wk, 
        dateadd(day, n.n-1, t1.month) dt
    from #t t1
    cross apply (
        select datediff(day, t.month, dateadd(month, 1, t.month)) nd
        from #t t
        where t1.month = t.month and t1.product = t.product) ndm
    inner join numbers n on n.n <= ndm.nd
)
select product, min(dt) as week, cast(sum(price)/count(*) as decimal(9,2)) as price
from x
group by product, wk
having count(*) = 7
order by product, wk

datepart(week,dateadd(day, n.n-2, t1.month))表达式的结果取决于SET DATEFIRST,因此您可能需要相应调整。

答案 1 :(得分:1)

如果你有一个日历表,它只是一个简单的连接:

SELECT 
   product, 
   calendar_date - (day_of_week-1) AS week,
   SUM(price/7) * COUNT(*)
FROM prices AS p
JOIN calendar AS c
  ON c.calendar_date >= month 
 AND c.calendar_date < DATEADD(m,1,month)
GROUP BY product,
   calendar_date - (day_of_week-1) 

这可以进一步简化为仅加入星期一,然后在CASE中进行更多日期算术以获得7天或更短的时间。

修改

您的上一次查询已于2月31日返回,您需要从=删除on n.n < ndm.nd。当你似乎使用ISO周时,你最好更改DATEPART以避免不同的DATEFIRST设置出现问题。

根据您上次的查询,我创建了fiddle

;with x as (
    select price, 
        datepart(isowk,dateadd(day, n.n, t1.month)) wk, 
        dateadd(day, n.n-1, t1.month) dt
    from 
    (select '2014-1-1' as Month, 100.00 as PRICE
    union
    select '2014-2-1' as Month, 200.00 as PRICE) t1
    cross apply (
        select datediff(day, t.month, dateadd(month, 1, t.month)) nd
        from 
        (select '2014-1-1' as Month, 100.00 as PRICE
        union
        select '2014-2-1' as Month, 200.00 as PRICE)
        t
        where t1.month = t.month) ndm
    inner join 
        (SELECT (a.Number * 256) + b.Number AS N FROM
        (SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255) a (Number), 
        (SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255) b (Number)) n --numbers 
    on n.n < ndm.nd
) select min(dt) as week, cast(sum(price)/count(*) as decimal(9,2)) as price
from x
group by wk
having count(*) = 7
order by wk

当然,日期可能是多年,因此您也需要年度GROUP BY。