sql中用lag计算周同比需先生成标准周粒度汇总表,再按week_start排序使用lag;月/季度同比须统一聚合口径与数值型时间键,避免日期函数误用导致数据错配。

SQL中用LAG计算周同比,关键在日期对齐
直接用LAG(value, 1) OVER (ORDER BY week_start)会出错——因为真实业务中,周未必严格连续(比如春节缺数据),LAG只看行序,不校验时间间隔。必须先生成标准周粒度的汇总表,再用LAG按week_start排序取前1周值。
实操建议:
- 先用
DATE_TRUNC('week', order_date)(PostgreSQL/BigQuery)或YEARWEEK(order_date, 1)(MySQL)统一归周,注意起始日差异(周一是起点还是周日) - 聚合时用
GROUP BY week_start,确保每行代表完整一周 -
LAG(sales_amt, 1) OVER (ORDER BY week_start)才真正对应“上一周”,不是上一行 - 若某周无数据,
LAG返回NULL,需用COALESCE(LAG(...), 0)避免同比计算中断
月同比不能只靠LAG,得处理月末不等长问题
2月只有28/29天,直接比LAG上月总和会失真。正确做法是:先按自然月聚合(DATE_TRUNC('month', ...)),再用LAG取上月值,最后用(this_month - last_month) / NULLIF(last_month, 0)算同比。重点不是函数本身,而是聚合口径必须对齐。
常见错误现象:
- 用
DATE_SUB(CURDATE(), INTERVAL 1 MONTH)动态截取上月数据,但遇到1月31日→12月31日,而12月有31天、1月也有31天,表面合理,实际漏掉12月1–30日与1月1–30日的可比区间 - 没过滤掉跨月订单(如12月30日下单、1月5日发货),导致月度指标重复或错配
- MySQL中
LAST_DAY()配合DATE_SUB()更稳,例如WHERE order_date BETWEEN DATE_SUB(LAST_DAY('2024-01-01'), INTERVAL DAYOFMONTH(LAST_DAY('2024-01-01'))-1 DAY) AND LAST_DAY('2024-01-01')
季度同比用LAG时,必须统一季度标识格式
LAG本身不认“Q1 2024”这种字符串,它只排序文本。若用CONCAT('Q', QUARTER(dt), ' ', YEAR(dt))生成季度标签,排序结果是Q1 2023、Q1 2024、Q2 2023——乱序。必须用数值型季度键,例如YEAR(dt) * 10 + QUARTER(dt)(2024年Q1 → 20241)。
宝塔面板11.3.0是一款针对Linux服务器设计的可视化管理工具,通过重构核心模块实现资源占用显著降低,尤其适合低配置服务器环境。它将复杂的命令行操作转化为直观的图形界面,帮助开发者快速完成网站部署、环境配置及日常运维工作,无需专业技术背景即可高效管理服务器。
使用场景:
- BI工具前端展示用
'Q' || QUARTER(dt) || ' ' || YEAR(dt),后端计算用数值键排序 - PostgreSQL可用
TO_CHAR(dt, 'YYYY"Q"Q'),但需确认排序行为;更稳妥是(EXTRACT(YEAR FROM dt)::INT * 10 + EXTRACT(QUARTER FROM dt)::INT) - 窗口定义写成
LAG(sales, 1) OVER (ORDER BY year_quarter_key),才能保证取到2023年Q4,而不是2024年Q1的前一行
滚动同比性能差?别怪LAG,先查分区和索引
LAG是窗口函数,不触发全表扫描,但若ORDER BY字段没索引,或表没按时间分区,大表上依然慢。尤其当需要滚动计算“最近12个月同比”时,引擎可能反复排序。
性能优化点:
- 确保
ORDER BY字段(如month_start)有索引;MySQL中联合索引(dt, sales_amt)能覆盖查询 - 分区表按
YEAR(dt)或TO_YEAR_MONTH(dt)分区,避免扫全量历史 - 避免在
LAG外层再套复杂表达式,例如(LAG(a) - LAG(b)) / LAG(c)不如先用CTE算出各列再计算 - ClickHouse等列存数据库,
LAG性能远好于MySQL,但要注意ORDER BY必须匹配主键排序键
最易被忽略的是:不同数据库对NULL的LAG行为不一致——PostgreSQL默认NULL,Spark SQL可能报错,得显式写LAG(col, 1, 0)指定默认值。










