字节跳动电商数据分析岗的一道经典面试题,重点考察环比与同比的计算方法。别看知识点基础,但许多人在SQL面试中因LAG函数使用不熟练而失分。本文将对这道题目进行详细拆解与讲解,帮助你彻底掌握。

一、题目背景
本题源自字节跳动电商业务线的数据分析岗位面试。环比与同比是数据分析领域最基础的指标,然而在SQL面试环节,不少候选人因对LAG函数的运用不够熟练而出现失误。
二、题目
现有月度销售汇总表 t15_zj_monthly_sales,要求计算每个月的环比增长率与同比增长率。
t15_zj_monthly_sales 表
---------- --------
|month | sales|
---------- --------
| 2024-01| 10000|
| 2024-02| 12000|
| 2024-03| 11000|
| 2024-04| 13000|
| 2024-05| 14000|
| 2024-06| 15000|
| 2024-07| 16000|
| 2024-08| 15500|
| 2024-09| 14500|
| 2024-10| 15000|
| 2024-11| 17000|
| 2024-12| 20000|
| 2025-01| 15000|
| 2025-02| 18000|
| 2025-03| 16000|
---------- --------
定义:
- 环比 (Month-over-Month) = (本月销售额 - 上月销售额) / 上月销售额 × 100%
- 同比 (Year-over-Year) = (本月销售额 - 去年同月销售额) / 去年同月销售额 × 100%
期望输出:每月销售额、环比增长率(%)、同比增长率(%)。
三、思路分析
计算环比时,可直接使用 LAG(sales, 1) 获取上一行(即上个月)的销售额,前提是数据已经按时间顺序排列。计算同比则需要 LAG(sales, 12) 获取12个月之前(即去年同月)的数据,但该方式依赖于月度数据连续排列——如果中间存在缺失月份,采用 LEFT JOIN 的方式会更加安全准确。
| 维度 | 评分 |
|---|---|
| 题目难度 | ⭐️⭐️ |
| 题目清晰度 | ⭐️⭐️⭐️⭐️⭐️ |
| 业务常见度 | ⭐️⭐️⭐️⭐️⭐️ |
四、逐步推导
步骤1:计算环比
SELECT
month,
sales,
LAG(sales, 1) OVER (ORDER BY month) AS prev_month_sales,
ROUND((sales - LAG(sales, 1) OVER (ORDER BY month)) * 100.0 / LAG(sales, 1) OVER (ORDER BY month), 1) AS mom_pct
FROM t15_zj_monthly_sales
ORDER BY month;
---------- -------- ------------------- ----------
|month | sales| prev_month_sales | mom_pct|
---------- -------- ------------------- ----------
| 2024-01| 10000| NULL | NULL |
| 2024-02| 12000| 10000 | 20.0 |
| 2024-03| 11000| 12000 | -8.3 |
| 2024-04| 13000| 11000 | 18.2 |
| 2024-05| 14000| 13000 | 7.7 |
| 2024-06| 15000| 14000 | 7.1 |
| 2024-07| 16000| 15000 | 6.7 |
| 2024-08| 15500| 16000 | -3.1 |
| 2024-09| 14500| 15500 | -6.5 |
| 2024-10| 15000| 14500 | 3.4 |
| 2024-11| 17000| 15000 | 13.3 |
| 2024-12| 20000| 17000 | 17.6 |
| 2025-01| 15000| 20000 | -25.0 |
| 2025-02| 18000| 15000 | 20.0 |
| 2025-03| 16000| 18000 | -11.1 |
---------- -------- ------------------- ----------
15 rows selected (0.905 seconds)
首行没有上月数据,因此环比为 NULL,符合业务逻辑预期。
步骤2:计算同比
SELECT
month,
sales,
LAG(sales, 12) OVER (ORDER BY month) AS last_year_sales,
ROUND((sales - LAG(sales, 12) OVER (ORDER BY month)) * 100.0 / LAG(sales, 12) OVER (ORDER BY month), 1) AS yoy_pct
FROM t15_zj_monthly_sales
ORDER BY month;
---------- -------- ------------------ ----------
|month | sales| last_year_sales | yoy_pct|
---------- -------- ------------------ ----------
| 2024-01| 10000| NULL | NULL |
| 2024-02| 12000| NULL | NULL |
| 2024-03| 11000| NULL | NULL |
| 2024-04| 13000| NULL | NULL |
| 2024-05| 14000| NULL | NULL |
| 2024-06| 15000| NULL | NULL |
| 2024-07| 16000| NULL | NULL |
| 2024-08| 15500| NULL | NULL |
| 2024-09| 14500| NULL | NULL |
| 2024-10| 15000| NULL | NULL |
| 2024-11| 17000| NULL | NULL |
| 2024-12| 20000| NULL | NULL |
| 2025-01| 15000| 10000 | 50.0 |
| 2025-02| 18000| 12000 | 50.0 |
| 2025-03| 16000| 11000 | 45.5 |
---------- -------- ------------------ ----------
15 rows selected (0.267 seconds)
2024年的所有月份同比均为 NULL,因为不存在前一年的数据进行对比。进入2025年后,同比数据便正常显示出来。
步骤3:合并环比与同比
SELECT
month,
sales,
ROUND((sales - LAG(sales, 1) OVER (ORDER BY month)) * 100.0 / LAG(sales, 1) OVER (ORDER BY month), 1) AS mom_pct,
ROUND((sales - LAG(sales, 12) OVER (ORDER BY month)) * 100.0 / LAG(sales, 12) OVER (ORDER BY month), 1) AS yoy_pct
FROM t15_zj_monthly_sales
ORDER BY month;
---------- -------- ---------- ----------
|month | sales| mom_pct | yoy_pct|
---------- -------- ---------- ----------
| 2024-01| 10000| NULL | NULL |
| 2024-02| 12000| 20.0 | NULL |
| 2024-03| 11000| -8.3 | NULL |
| 2024-04| 13000| 18.2 | NULL |
| 2024-05| 14000| 7.7 | NULL |
| 2024-06| 15000| 7.1 | NULL |
| 2024-07| 16000| 6.7 | NULL |
| 2024-08| 15500| -3.1 | NULL |
| 2024-09| 14500| -6.5 | NULL |
| 2024-10| 15000| 3.4 | NULL |
| 2024-11| 17000| 13.3 | NULL |
| 2024-12| 20000| 17.6 | NULL |
| 2025-01| 15000| -25.0 | 50.0 |
| 2025-02| 18000| 20.0 | 50.0 |
| 2025-03| 16000| -11.1 | 45.5 |
---------- -------- ---------- ----------
15 rows selected (0.325 seconds)
2025年1月的同比增长高达50%(15000 vs 10000),增长非常明显。但环比方面,1月出现了较大幅度的下滑(-25%),反映出春节前后销售额的典型波动特征。
五、常见坑点
- 坑1:LAG 依赖排序
如果表中的数据没有按照时间顺序排列,LAG 将取到随机行,导致计算结果完全错误。因此必须使用ORDER BY month确保正确排序。 - 坑2:缺失月份导致同比错位
假设 2024-07 的数据缺失,那么对 2025-07 使用LAG(sales, 12)时会错误地取到 2024-06 的值,而不是 2024-07,同比结果完全偏移。数据存在缺失时,建议使用 LEFT JOIN 方案替代 LAG 函数。 - 坑3:分母为零
如果上月的销售额为0,除法操作会直接报错。可以通过NULLIF(LAG(sales) OVER w, 0)进行容错处理,避免程序崩溃。
六、举一反三
- 累计同比:利用
SUM(sales) OVER (ORDER BY month ROWS BETWEEN 11 PRECEDING AND CURRENT ROW)计算滚动12个月的累计销售额,了解年度整体趋势。 - 移动平均:通过
AVG(sales) OVER (ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)获取3个月移动平均值,有效平滑短期数据波动。
七、知识点总结
| 考点 | 说明 |
|---|---|
| LAG(sales, 1) | 取上一行数据 = 计算环比 |
| LAG(sales, 12) | 取12行前数据 = 计算同比(适用于连续数据) |
| 数据缺失 | 使用 LEFT JOIN 替代 LAG 可保证准确性 |
八、建表语句和数据插入
CREATE TABLE IF NOT EXISTS t15_zj_monthly_sales (
month STRING,
sales BIGINT
);
INSERT INTO t15_zj_monthly_sales VALUES
('2024-01',10000),
('2024-02',12000),
('2024-03',11000),
('2024-04',13000),
('2024-05',14000),
('2024-06',15000),
('2024-07',16000),
('2024-08',15500),
('2024-09',14500),
('2024-10',15000),
('2024-11',17000),
('2024-12',20000),
('2025-01',15000),
('2025-02',18000),
('2025-03',16000);