在MySQL 8.0版本中,窗口函数的引入让很多复杂的统计需求可以用更简洁的语法实现,其中SUM OVER窗口函数是计算累计销售额的常用方案,相比传统的自连接或者用户变量方式,代码更易维护,执行效率也更高。
SUM OVER窗口函数基础语法
SUM OVER窗口函数的核心作用是在指定的窗口范围内对数值进行求和,基础语法结构如下:
SUM(求和字段) OVER (
[PARTITION BY 分组字段]
[ORDER BY 排序字段 [ASC|DESC]]
[ROWS BETWEEN 窗口范围起始 AND 窗口范围结束]
) AS 别名
其中各个部分的含义如下:
- PARTITION BY:可选参数,用于指定分组的字段,相同分组内的数据会单独计算累计值,不同分组之间互不影响。
- ORDER BY:可选参数,用于指定窗口内数据的排序规则,累计计算会按照这个排序顺序依次累加。
- ROWS BETWEEN:可选参数,用于指定窗口的范围,如果不指定,默认是
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,也就是从窗口的第一行到当前行,刚好符合累计计算的需求。
准备测试数据表
我们首先创建一个销售记录表,插入一些测试数据,方便后续演示累计销售额的计算。表结构包含销售日期、客户ID、销售金额三个核心字段:
-- 创建销售记录表
CREATE TABLE sales_record (
id INT PRIMARY KEY AUTO_INCREMENT,
sale_date DATE NOT NULL,
customer_id INT NOT NULL,
sale_amount DECIMAL(10,2) NOT NULL
);
-- 插入测试数据
INSERT INTO sales_record (sale_date, customer_id, sale_amount) VALUES
('2024-01-01', 1, 100.50),
('2024-01-02', 1, 200.00),
('2024-01-03', 1, 150.75),
('2024-01-01', 2, 300.00),
('2024-01-02', 2, 250.50),
('2024-01-04', 2, 180.00),
('2024-01-02', 3, 120.00),
('2024-01-03', 3, 90.50);
按日期计算全量累计销售额
如果需要计算所有销售记录按日期排序的累计销售额,不需要指定PARTITION BY,只需要在ORDER BY中指定sale_date升序即可,代码如下:
SELECT
id,
sale_date,
customer_id,
sale_amount,
SUM(sale_amount) OVER (ORDER BY sale_date) AS total_cumulative_amount
FROM sales_record
ORDER BY sale_date;
执行上述查询后,结果会按照销售日期升序排列,total_cumulative_amount字段就是截止到当前日期的累计销售额,相同日期的记录的累计值相同,都是截止到该日期的总销售额。
按客户分组计算累计销售额
实际业务中更常见的需求是按客户维度计算累计销售额,也就是每个客户单独统计自己的累计消费金额,这时候就需要使用PARTITION BY指定customer_id作为分组字段:
SELECT
id,
sale_date,
customer_id,
sale_amount,
SUM(sale_amount) OVER (
PARTITION BY customer_id
ORDER BY sale_date
) AS customer_cumulative_amount
FROM sales_record
ORDER BY customer_id, sale_date;
查询结果中,每个客户的数据会单独成组,customer_cumulative_amount字段就是该客户截止到当前销售日期的累计消费金额,不同客户的累计值不会互相干扰。
自定义窗口范围计算累计销售额
如果需要计算最近3笔销售的累计销售额,而不是从第一行开始累计,可以通过ROWS BETWEEN指定窗口范围,示例如下:
SELECT
id,
sale_date,
customer_id,
sale_amount,
SUM(sale_amount) OVER (
PARTITION BY customer_id
ORDER BY sale_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS recent_3_cumulative_amount
FROM sales_record
ORDER BY customer_id, sale_date;
这里的ROWS BETWEEN 2 PRECEDING AND CURRENT ROW表示窗口范围是从当前行的前2行到当前行,也就是最近3条记录(如果前面不足2行,就从第一条开始计算),这样可以得到每个客户最近3笔销售的累计金额。
使用注意事项
在使用SUM OVER窗口函数计算累计销售额时,需要注意以下几点:
- 排序字段的选择要符合业务需求,累计计算的结果和ORDER BY的顺序直接相关,升序是正向累计,降序是反向累计。
- 如果销售金额字段存在NULL值,SUM函数会自动忽略NULL值,不会将其计入累计总和,如果需要将NULL视为0,可以先用COALESCE函数处理字段。
- 窗口函数不能直接用在WHERE子句中,如果需要筛选累计值的结果,可以把查询作为子查询,在外层进行WHERE条件过滤。
- MySQL 8.0之前的版本不支持窗口函数,如果使用的是低版本MySQL,需要改用自连接或者用户变量的方式实现累计计算。
累计销售额的计算场景非常广泛,除了销售数据,也可以用于用户积分累计、订单量累计等类似需求,只需要替换对应的求和字段和分组、排序规则即可。