SQL 如何实现区间累计统计?

11次阅读

区间累计统计是按动态范围(如过去7天、值在[x-10,x+10]内)对每行计算聚合,核心在于窗口边界依当前行值或时间戳动态划定;ROWS按物理行偏移,RANGE按实际值/时间划界,二者语义不同。

SQL 如何实现区间累计统计?

什么是区间累计统计?

区间累计统计不是简单地 SUM() 整列,而是对每个数据点,计算它在某个动态范围(比如“过去7天”“前3行”“值在 [x-10, x+10] 内”)内的聚合结果。核心在于窗口边界不固定,依赖当前行的值或时间戳动态划定。

ROWS BETWEENRANGE BETWEEN 的关键区别

很多人写错就卡在这儿:用 ROWS 想实现数值区间,结果得到的是物理行数偏移,和业务逻辑脱节。

  • ROWS BETWEEN 2 PRECEDING AND CURRENT ROW:只看位置,不管 order by 列的值是否连续
  • RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROWpostgresql/oracle)或 RANGE BETWEEN 10 PRECEDING AND CURRENT ROWmysql 8.0+,要求 ORDER BY 列是数字):按实际值划界,才叫“区间”
  • sqlite 不支持 RANGE 的数值/时间表达式,只能退化为 ROWS + 子查询

MySQL 8.0 实现「过去30天销量累计」的写法

假设表 salessale_date DATEamount DECIMAL,要为每条记录算“截至当天、过去30天内(含)的总销量”:

SELECT   sale_date,   amount,   SUM(amount) OVER (     ORDER BY sale_date     RANGE BETWEEN INTERVAL 29 DAY PRECEDING AND CURRENT ROW   ) AS cum_30d FROM sales;

注意:INTERVAL 29 DAY PRECEDING 是因为 CURRENT ROW 自身已包含当天,往前推29天才能凑满30天整。如果用 30 DAY PRECEDING,实际是31天。

数据库不支持 RANGE 时间偏移时怎么办?

比如 SQL Server 2019 或旧版 MySQL,RANGE 只接受数字字面量,不认 INTERVAL。这时得用自连接或相关子查询:

SELECT   s1.sale_date,   s1.amount,   (SELECT SUM(s2.amount)    FROM sales s2    WHERE s2.sale_date BETWEEN DATE_SUB(s1.sale_date, INTERVAL 29 DAY) AND s1.sale_date) AS cum_30d FROM sales s1;

性能会明显下降,尤其数据量大时。必须确保 sale_date 有索引;如果允许近似结果,可考虑先按天聚合再窗口计算,减少行数。

真正难的不是语法,是想清楚“区间”到底由什么定义——是日历天数?自然月?还是数值带宽?选错语义,再对的 SQL 也跑不出正确结果。

text=ZqhQzanResources