@
shadow1949 ,改写成了 MySQL ,将查询放进了一个视图中。
如果用 SQL 的话,以后一行「 select * from view_xxx 」就可得到结果了
使用前,记得将「调休」「非周末的其他假日」添加进『 holiday 表』
『「 select * from view_xxx 」结果』
date num avg_5 avg_10
————— —— ———— ————
2022-09-01 12 12.0000 12.0000
2022-09-20 11 02.2000 01.1000
2022-09-21 05 03.2000 01.6000
2022-09-22 23 07.8000 03.9000
2022-09-23 42 16.2000 08.1000
『 MySQL 语法(排版原因,记得去掉每行开头的 全角空格)』
-- 节假日数据(默认周六周日是假日。若有调休、其他假日,在此表指定)
CREATE TABLE holiday(date DATE PRIMARY KEY, is_holiday BOOL NOT NULL) AS
SELECT * FROM (
VALUES
-- 中秋放假
ROW('2022-09-12', true),
-- 国庆放假和调休
ROW('2022-10-01', true), ROW('2022-10-04', true), ROW('2022-10-07', true),
ROW('2022-10-02', true), ROW('2022-10-05', true), ROW('2022-10-08', false),
ROW('2022-10-03', true), ROW('2022-10-06', true), ROW('2022-10-09', false)
) AS v(date, is_holiday);
-- 原始数据(日期、数据)
CREATE TABLE orig_data(date DATE PRIMARY KEY, num INT NOT NULL) AS
SELECT * FROM (
VALUES
ROW('2022-09-01', 12), ROW('2022-09-03', 22),
ROW('2022-09-20', 11), ROW('2022-09-21', 5),
ROW('2022-09-22', 23), ROW('2022-09-23', 42),
ROW('2022-09-24', 11)
) AS v(date, num);
-- 原始数据的各种平均值视图
CREATE VIEW avgs_of_data AS
WITH RECURSIVE
-- 根据原始数据的日期范围,生成日历
-- (如果超过 1000 天,记得调整 cte_max_recursion_depth )
calendar(date) AS (
SELECT min(date)
FROM orig_data
UNION ALL
SELECT DATE_ADD(date, INTERVAL 1 DAY)
FROM calendar
WHERE date < (SELECT max(date) FROM orig_data)
),
-- 根据日历,生成工作日数据(日期、该天是否为工作日)
workday(date, is_workday) AS (
SELECT date, COALESCE(NOT is_holiday, weekday(date) < 5)
FROM calendar LEFT JOIN holiday USING(date)
),
-- 为每个工作日,计算最近 5 或 10 个工作日内的平均值(日期、数值、5 工作日均值、10 工作日均值)
avgs_of_workday(date, num, avg_5, avg_10) AS (
SELECT date, num,
avg(COALESCE(num, 0)) OVER (ORDER BY date ROWS 4 PRECEDING),
avg(COALESCE(num, 0)) OVER (ORDER BY date ROWS 9 PRECEDING)
FROM workday LEFT JOIN orig_data USING(date)
WHERE is_workday
)
-- 去除没有数据的工作日
SELECT *
FROM avgs_of_workday
WHERE num IS NOT NULL;
MySQL 不支持在「窗口函数」中使用「 group_concat 」,所以没有「(11+5+23) / 5 」之类的结果了。
但对你的需求无影响,只是方便查看平均值的计算过程是否正确。