跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

SQL如何计算年度同比和环比数据_使用LAG函数与比例计算

LAG函数必须配合PARTITION BY和ORDER BY才能正确取上期值,如LAG(sales,1) OVER (PARTITION BY region ORDER BY year_month),否则因缺失排序或分区导致值错乱;需用NULLIF和COALESCE处理除零与NULL,避免计算中断或截断。 LAG函数怎么写才能正确取上期值 直接用
LAG(value)
默认取前1行,但时间序列里“上期”不一定是物理上一行——比如数据有缺失月份、或按年/季度聚合后顺序乱。必须配合
ORDER BY
明确时间维度排序,且推荐用带
PARTITION BY
的写法隔离不同业务线或产品线。 常见错误是漏掉
ORDER BY date
,导致
LAG
随机取值;或者没加
PARTITION BY product_id
,让A产品的12月和B产品的1月被错误关联。 正确写法:
LAG(sales, 1) OVER (PARTITION BY region ORDER BY year_month)
year_month
必须是可排序的格式(如
'2023-01'
字符串或
DATE
类型),不能是
'Jan-2023'
这类无序字符串 如果要算同比,
LAG(sales, 12)
对月度数据才对应去年同月;环比则用
LAG(sales, 1)
同比和环比的分母为零怎么处理 当上期值为0(比如新业务首月上线),直接除会报错或得
NULL
/
Infinity
,数据库行为不一致:
PostgreSQL
报错,
MySQL
返回
NULL
SQL Server
可能抛异常。必须显式判断。 别用
WHERE last_period != 0
过滤——这会丢掉整条记录,导致同比结果断层。应该在计算列里用条件表达式兜底。 安全写法:
CASE WHEN LAG(sales, 1) OVER (...) = 0 THEN NULL ELSE (sales - LAG(sales, 1) OVER (...)) * 1.0 / LAG(sales, 1) OVER (...) END
更简洁(支持大多数引擎):
NULLIF(LAG(sales, 1) OVER (...), 0)
替换分母,再配合
COALESCE(..., 0)
控制最终显示 注意乘
1.0
或用
CAST
避免整数除法截断(如
5/10 = 0
) 为什么用窗口函数比自连接更可靠 有人用
LEFT JOIN t t2 ON t.year_month = DATE_SUB(t2.year_month, INTERVAL 1 MONTH)
算环比,看似直观,但实际埋雷:一旦某个月份数据完全缺失(如2月没销售记录),自连接就断开,该月环比变成
NULL
,而用户可能误以为是数据异常而非自然空缺。 窗口函数基于排序位置取值,只要当前行存在,
LAG
就按逻辑偏移找——即使中间缺3个月,
LAG(sales, 1)
仍取紧邻的上一个有值的月份(除非指定
ROWS BETWEEN
精确控制)。这对监控类报表尤其关键。 自连接依赖完整时间轴补全(需先
GENERATE_SERIES
或日期维表),LAG 不需要 LAG 在大数据量下通常更快:单次扫描 + 排序;自连接是 O(n²) 级别关联 但注意:
ORDER BY
字段若有重复值(如多笔同秒订单),需加唯一字段(如
id
)保序,否则
LAG
行为不确定 不同数据库对LAG的兼容性差异
LAG
是 SQL:2003 标准,主流数据库都支持,但细节有坑:
MySQL 8.0+
支持,但
ORDER BY
必须包含确定性字段,否则警告;低版本不支持,只能用变量模拟(极难维护)
PostgreSQL
允许
LAG(value, offset, default)
第三个参数设默认值(如
0
),避免
NULL
传播
SQL Server
Oracle
同样支持三参数,但
default
值类型必须和
value
严格一致(比如不能用
'0'
填数字列)
ClickHouse
要求
ORDER BY
SELECT
外显式声明(即不能只在窗口函数里写) 真正容易被忽略的是时区和日期解析——比如把字符串
'202301'
直接
ORDER BY
,多数数据库当字符串比('202301' '20231' 就错乱。统一转成
DATE
或固定宽度字符串最稳。

相关文章