在业务数据分析场景中,同比和环比增长率是判断业务发展态势的核心指标,环比反映相邻周期的变化,同比反映与上年同期的变化,通过SQL的LAG窗口函数配合聚合计算可以高效实现这两类指标的计算。

核心概念说明
首先明确两个指标的计算逻辑:
- 环比增长率:当前周期数值与上个周期数值的变化比例,公式为 (当前值 - 上期值) / 上期值 * 100%
- 同比增长率:当前周期数值与上年同期数值的变化比例,公式为 (当前值 - 上年同期值) / 上年同期值 * 100%
LAG函数基础介绍
LAG是SQL中的窗口函数,用于获取当前行之前指定偏移量的行的数据,语法如下:
-- LAG函数基础语法 LAG(列名, 偏移量, 默认值) OVER (PARTITION BY 分组列 ORDER BY 排序列)
其中偏移量表示往前取第几行,默认值为1;PARTITION BY用于分组,ORDER BY用于指定排序规则,通常按时间排序。
示例数据准备
我们首先创建一张月度销售额表,包含年份、月份、销售额三个字段,插入测试数据:
-- 创建测试表
CREATE TABLE monthly_sales (
year INT,
month INT,
sales DECIMAL(10, 2)
);
-- 插入测试数据
INSERT INTO monthly_sales (year, month, sales) VALUES
(2022, 1, 10000.00),
(2022, 2, 12000.00),
(2022, 3, 11000.00),
(2023, 1, 13000.00),
(2023, 2, 15000.00),
(2023, 3, 14000.00),
(2023, 4, 16000.00);
环比增长率计算
环比只需要获取上个月的销售额即可,使用LAG函数偏移量为1,按年份和月份排序:
-- 计算月度环比增长率
SELECT
year,
month,
sales,
-- 获取上个月的销售额,没有则取NULL
LAG(sales, 1) OVER (ORDER BY year, month) AS last_month_sales,
-- 计算环比增长率,处理除数为0的情况
CASE
WHEN LAG(sales, 1) OVER (ORDER BY year, month) = 0 THEN NULL
ELSE ROUND(
(sales - LAG(sales, 1) OVER (ORDER BY year, month))
/ LAG(sales, 1) OVER (ORDER BY year, month) * 100,
2
)
END AS mom_growth_rate
FROM monthly_sales
ORDER BY year, month;
执行上述语句后,结果如下:
| year | month | sales | last_month_sales | mom_growth_rate |
|---|---|---|---|---|
| 2022 | 1 | 10000.00 | NULL | NULL |
| 2022 | 2 | 12000.00 | 10000.00 | 20.00 |
| 2022 | 3 | 11000.00 | 12000.00 | -8.33 |
| 2023 | 1 | 13000.00 | 11000.00 | 18.18 |
| 2023 | 2 | 15000.00 | 13000.00 | 15.38 |
| 2023 | 3 | 14000.00 | 15000.00 | -6.67 |
| 2023 | 4 | 16000.00 | 14000.00 | 14.29 |
同比增长率计算
同比需要获取上年同期的销售额,首先需要按月份分组,再在每个月份组内按年份排序取上一年的数据:
-- 计算月度同比增长率
SELECT
year,
month,
sales,
-- 按月份分组,获取上年同期的销售额
LAG(sales, 1) OVER (PARTITION BY month ORDER BY year) AS last_year_same_month_sales,
-- 计算同比增长率,处理除数为0的情况
CASE
WHEN LAG(sales, 1) OVER (PARTITION BY month ORDER BY year) = 0 THEN NULL
ELSE ROUND(
(sales - LAG(sales, 1) OVER (PARTITION BY month ORDER BY year))
/ LAG(sales, 1) OVER (PARTITION BY month ORDER BY year) * 100,
2
)
END AS yoy_growth_rate
FROM monthly_sales
ORDER BY year, month;
执行上述语句后,结果如下:
| year | month | sales | last_year_same_month_sales | yoy_growth_rate |
|---|---|---|---|---|
| 2022 | 1 | 10000.00 | NULL | NULL |
| 2022 | 2 | 12000.00 | NULL | NULL |
| 2022 | 3 | 11000.00 | NULL | NULL |
| 2023 | 1 | 13000.00 | 10000.00 | 30.00 |
| 2023 | 2 | 15000.00 | 12000.00 | 25.00 |
| 2023 | 3 | 14000.00 | 11000.00 | 27.27 |
| 2023 | 4 | 16000.00 | NULL | NULL |
同时计算同比和环比
可以将两个逻辑合并到一个查询中,一次性得到所有指标:
-- 同时计算同比和环比增长率
SELECT
year,
month,
sales,
-- 环比相关
LAG(sales, 1) OVER (ORDER BY year, month) AS last_month_sales,
CASE
WHEN LAG(sales, 1) OVER (ORDER BY year, month) = 0 THEN NULL
ELSE ROUND(
(sales - LAG(sales, 1) OVER (ORDER BY year, month))
/ LAG(sales, 1) OVER (ORDER BY year, month) * 100,
2
)
END AS mom_growth_rate,
-- 同比相关
LAG(sales, 1) OVER (PARTITION BY month ORDER BY year) AS last_year_same_month_sales,
CASE
WHEN LAG(sales, 1) OVER (PARTITION BY month ORDER BY year) = 0 THEN NULL
ELSE ROUND(
(sales - LAG(sales, 1) OVER (PARTITION BY month ORDER BY year))
/ LAG(sales, 1) OVER (PARTITION BY month ORDER BY year) * 100,
2
)
END AS yoy_growth_rate
FROM monthly_sales
ORDER BY year, month;
注意事项
- 使用LAG函数时,排序规则需要和实际业务的时间顺序一致,否则会取到错误的历史数据
- 计算增长率时需要处理除数为0或者上期数据为NULL的情况,避免SQL执行报错
- 如果数据存在缺失月份,LAG函数会按照排序后的行取数据,不会自动补全缺失周期,需要提前处理数据完整性
- 不同的数据库对窗口函数的支持略有差异,上述语法适用于MySQL 8.0+、PostgreSQL、SQL Server等主流数据库