跳到主要内容

字节跳动大数据面试SQL-每月销售额与上月的同比环比

一、题目背景

这道题来自字节跳动电商业务的数据分析岗面试。环比和同比是数据分析中最基础的指标,但 SQL 面试中很多人因为 LAG 函数用不熟而翻车。

业务场景:月度经营分析会上,GMV 报表必须包含"环比增长"和"同比增长"两列。环比看短期趋势(这个月比上个月好还是差),同比消除季节因素(今年1月跟去年1月比)。

二、题目

现有一张月度销售汇总表 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 |
+----------+--------+

定义

  • 环比 = (本月 - 上月) / 上月 × 100%
  • 同比 = (本月 - 去年同月) / 去年同月 × 100%

期望输出:每个月的销售额、环比增长率(%)、同比增长率(%)。

三、思路分析

  1. 环比LAG(sales, 1) 取上一行(上个月),适用于时间序列有序数据
  2. 同比LAG(sales, 12) 取 12 行前(去年同月),前提是数据按月连续排列
  3. 如果数据有缺失月份,同比用 LEFT JOIN 更安全
维度评分
题目难度⭐️⭐️
题目清晰度⭐️⭐️⭐️⭐️⭐️
业务常见度⭐️⭐️⭐️⭐️⭐️

四、逐步推导

步骤1:环比

Spark SQL

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)(https://www.dwsql.com)

第一行没有上月数据,环比为 NULL。

步骤2:同比

Spark SQL

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)(https://www.dwsql.com)

24年数据为空,因为24年没有上一年的数据

步骤3:合并环比和同比

Spark SQL

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;

最终结果(完整数据25行,截取关键月份):

+----------+--------+----------+----------+
| 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)(https://www.dwsql.com)

2025-01 的同比增长 50%(15000 vs 10000),增长显著。

五、常见坑点

坑1:LAG 依赖排序

如果表不按时间排序,LAG 取到的是随机行。必须 ORDER BY month

坑2:缺失月份导致同比错位

如果 2024-07 数据缺失,LAG(sales, 12) 对 2025-07 取到的是 2024-06 而非 2024-07,同比完全错误!数据有缺失时必须用 LEFT JOIN 方案替代 LAG。

坑3:分母为零

如果上个月销售额为 0,除法会报错。加 NULLIF(LAG(sales) OVER w, 0) 兜底。

六、举一反三

  1. 累计同比SUM(sales) OVER (ORDER BY month ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) 计算滚动12个月的累计销售额
  2. 移动平均AVG(sales) OVER (ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) 3个月移动平均,平滑波动

七、知识点总结

考点说明
LAG(sales, 1)取上一行 = 环比
LAG(sales, 12)取12行前 = 同比(连续数据)
数据缺失用 LEFT JOIN 替代 LAG 保证准确性

八、建表语句和数据插入

点击展开 DDL & DML
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);
📱关注公众号

「数据仓库技术」文章同步更新,不错过每一篇干货

微信公众号二维码
💬加群交流

备注「数据仓库技术」加入社群,每日一道大厂SQL真题

交流微信二维码

你可能还想看