SQL 中分区的加权移动平均线
Weighted moving average with partitions in SQL
我有一个简单的 table,其中包含按日期 (YYYYMMDD) 销售的不同产品。天数多,品多。 table 看起来像这样:
date product sales
...
20210101 Apples 112
20210101 Bananas 13
20210102 Apples 12
20210102 Bananas 101
20210103 Apples 18
20210103 Bananas 155
20210101 Lemons 14
...
我需要计算每个产品的 3 天加权移动平均值。主要棘手的事情是分区。我试图在这段代码中压缩它:
SELECT table.date,
max(table.sales) as sales,
CASE
WHEN table.date - (select min(date) from table) >1
THEN sum(weighting * ma_table.sales)
END as WMA
FROM table
JOIN table AS ma_table ON table.date - ma_table.date BETWEEN 0 AND 2
JOIN (SELECT id_1, 1/(2 + 1.) as weighting FROM (VALUES (0, 1, 2)) as t(id_1)) weights ON
id_1 = table.date - ma_table.date
GROUP BY table.date
ORDER BY table.date
是否可以使用此代码完成任何操作以在其中包含分区?
下面是SQL计算每个产品的3天移动平均线,
使用 window 函数。
这是你想要的吗?
with
a as ( -- serial numbers upto N (=3)
select row_number() over () as i
from TBL
limit 3
),
b as (
select
TBL.date, TBL.product, TBL.sales,
lag("sales", cast(a.i - 1 as integer))
over(partition by TBL.product, a.i order by date) as sales2,
(3 - a.i + 1) as weight
from TBL
cross join a
)
select date, product, sales,
sum(weight*sales2) / (3*(3+1)/2) as avg
from b
group by date, product, sales
order by product, date
所以我猜是这样的
create table yourtable (
id int primary key,
"date" date,
product varchar(30),
sales decimal(18,2)
)
insert into yourtable
(id, "date" , product, sales) values
(1, '2021-01-01', 'Apples', 112)
, (2, '2021-01-02', 'Apples', 12)
, (3, '2021-01-03', 'Apples', 18)
, (4, '2021-01-01', 'Bananas', 13)
, (5, '2021-01-02', 'Bananas', 101)
, (6, '2021-01-03', 'Bananas', 155)
, (7, '2021-01-01', 'Lemons', 14)
select "date", product, sales
, cast((prev2+prev1*2+sales*3)/(1+2+3) as decimal(18,2)) as WMA
from (
select "date", product, sales
, lag(sales, 2, 0) over (partition by product order by "date") as prev2
, lag(sales, 1, 0) over (partition by product order by "date") as prev1
from yourtable
) q
order by product, "date"
date | product | sales | wma
:--------- | :------ | -----: | -----:
2021-01-01 | Apples | 112.00 | 56.00
2021-01-02 | Apples | 12.00 | 43.33
2021-01-03 | Apples | 18.00 | 31.67
2021-01-01 | Bananas | 13.00 | 6.50
2021-01-02 | Bananas | 101.00 | 54.83
2021-01-03 | Bananas | 155.00 | 113.33
2021-01-01 | Lemons | 14.00 | 7.00
db<>fiddle here
无限加权滚动平均值的数字可能如下所示。
select "date", product, sales
, cast(sum(cnt * sales)
over (partition by product ROWS BETWEEN UNBOUNDED PRECEDING AND current row)
/ sum(cnt)
over (partition by product ROWS BETWEEN UNBOUNDED PRECEDING AND current row
) as decimal(18,2)) as UnboundedWeightedMovingAverage
from (
select "date", product, sales
, count(*) over (partition by product order by "date" asc) as cnt
from yourtable
) q
order by product, "date"
date | product | sales | unboundedweightedmovingaverage
:--------- | :------ | -----: | -----------------------------:
2021-01-01 | Apples | 112.00 | 112.00
2021-01-02 | Apples | 12.00 | 45.33
2021-01-03 | Apples | 18.00 | 31.67
2021-01-01 | Bananas | 13.00 | 13.00
2021-01-02 | Bananas | 101.00 | 71.67
2021-01-03 | Bananas | 155.00 | 113.33
2021-01-01 | Lemons | 14.00 | 14.00
db<>fiddle here
我有一个简单的 table,其中包含按日期 (YYYYMMDD) 销售的不同产品。天数多,品多。 table 看起来像这样:
date product sales
...
20210101 Apples 112
20210101 Bananas 13
20210102 Apples 12
20210102 Bananas 101
20210103 Apples 18
20210103 Bananas 155
20210101 Lemons 14
...
我需要计算每个产品的 3 天加权移动平均值。主要棘手的事情是分区。我试图在这段代码中压缩它:
SELECT table.date,
max(table.sales) as sales,
CASE
WHEN table.date - (select min(date) from table) >1
THEN sum(weighting * ma_table.sales)
END as WMA
FROM table
JOIN table AS ma_table ON table.date - ma_table.date BETWEEN 0 AND 2
JOIN (SELECT id_1, 1/(2 + 1.) as weighting FROM (VALUES (0, 1, 2)) as t(id_1)) weights ON
id_1 = table.date - ma_table.date
GROUP BY table.date
ORDER BY table.date
是否可以使用此代码完成任何操作以在其中包含分区?
下面是SQL计算每个产品的3天移动平均线,
使用 window 函数。
这是你想要的吗?
with
a as ( -- serial numbers upto N (=3)
select row_number() over () as i
from TBL
limit 3
),
b as (
select
TBL.date, TBL.product, TBL.sales,
lag("sales", cast(a.i - 1 as integer))
over(partition by TBL.product, a.i order by date) as sales2,
(3 - a.i + 1) as weight
from TBL
cross join a
)
select date, product, sales,
sum(weight*sales2) / (3*(3+1)/2) as avg
from b
group by date, product, sales
order by product, date
所以我猜是这样的
create table yourtable ( id int primary key, "date" date, product varchar(30), sales decimal(18,2) ) insert into yourtable (id, "date" , product, sales) values (1, '2021-01-01', 'Apples', 112) , (2, '2021-01-02', 'Apples', 12) , (3, '2021-01-03', 'Apples', 18) , (4, '2021-01-01', 'Bananas', 13) , (5, '2021-01-02', 'Bananas', 101) , (6, '2021-01-03', 'Bananas', 155) , (7, '2021-01-01', 'Lemons', 14)
select "date", product, sales , cast((prev2+prev1*2+sales*3)/(1+2+3) as decimal(18,2)) as WMA from ( select "date", product, sales , lag(sales, 2, 0) over (partition by product order by "date") as prev2 , lag(sales, 1, 0) over (partition by product order by "date") as prev1 from yourtable ) q order by product, "date"
date | product | sales | wma :--------- | :------ | -----: | -----: 2021-01-01 | Apples | 112.00 | 56.00 2021-01-02 | Apples | 12.00 | 43.33 2021-01-03 | Apples | 18.00 | 31.67 2021-01-01 | Bananas | 13.00 | 6.50 2021-01-02 | Bananas | 101.00 | 54.83 2021-01-03 | Bananas | 155.00 | 113.33 2021-01-01 | Lemons | 14.00 | 7.00
db<>fiddle here
无限加权滚动平均值的数字可能如下所示。
select "date", product, sales , cast(sum(cnt * sales) over (partition by product ROWS BETWEEN UNBOUNDED PRECEDING AND current row) / sum(cnt) over (partition by product ROWS BETWEEN UNBOUNDED PRECEDING AND current row ) as decimal(18,2)) as UnboundedWeightedMovingAverage from ( select "date", product, sales , count(*) over (partition by product order by "date" asc) as cnt from yourtable ) q order by product, "date"
date | product | sales | unboundedweightedmovingaverage :--------- | :------ | -----: | -----------------------------: 2021-01-01 | Apples | 112.00 | 112.00 2021-01-02 | Apples | 12.00 | 45.33 2021-01-03 | Apples | 18.00 | 31.67 2021-01-01 | Bananas | 13.00 | 13.00 2021-01-02 | Bananas | 101.00 | 71.67 2021-01-03 | Bananas | 155.00 | 113.33 2021-01-01 | Lemons | 14.00 | 14.00
db<>fiddle here