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