LAG比较状态时首行返回NULL,直接用!=会导致漏判;PostgreSQL用IS DISTINCT FROM,MySQL/SQL Server需显式处理NULL;GROUP BY COUNT(*)仅统计频次,无法捕获相邻行变化;漏写PARTITION BY或ORDER BY将导致跨组混比或排序错乱。

LAG比较当前行与上一行状态时,NULL怎么处理?
第一行没有“上一行”,LAG(status)返回NULL,直接写status != LAG(status)会导致整行判断为FALSE(因为NULL != 'active'结果是UNKNOWN,被WHERE过滤掉),从而漏掉首次变化。PostgreSQL支持IS DISTINCT FROM安全比较,MySQL和SQL Server必须显式处理:COALESCE(status, '') != COALESCE(LAG(status), '')或status IS DISTINCT FROM LAG(status)(PostgreSQL)/ (status LAG(status)) OR (status IS NULL) (LAG(status) IS NULL)(兼容写法)。
为什么GROUP BY + COUNT(*)算不出状态变化次数?
状态变化是**相邻行之间的动态行为**,不是静态分组统计。GROUP BY status只会告诉你每个状态出现多少次,完全无法反映“从pending变paid”这类序列事件。真正要捕获变化,必须把当前行和它的前一行拉到同一行做比较——这正是LAG()存在的意义。常见错误是先GROUP BY group_id, status再计数,结果只是各状态频次,和“变化次数”毫无关系。
PARTITION BY和ORDER BY漏写任意一个会怎样?
PARTITION BY group_id定义“谁跟谁比”:不写就跨所有数据找上一行,用户A的订单可能和用户B的订单错连;ORDER BY created_at定义“哪行在前哪行在后”:不写则数据库自由排序,LAG()结果不可预测,甚至报错(SQL Server/PostgreSQL强制要求)。实操中如果created_at有重复,务必追加唯一字段如id作为二级排序,否则相同时间戳下的行序不确定,变化检测就会随机失效。
旧版MySQL(5.7及更早)没有LAG怎么办?
自连接性能差但语义清晰,适合小数据量;用户变量@prev :=写法看似简洁,但MySQL官方明确警告其执行顺序不可靠——尤其在有JOIN、子查询或优化器介入时极易出错。最稳妥的替代方案是导出数据用Python脚本处理:按group_id分组,对每组按时间排序后逐行比对status,逻辑可控且调试直观。不要为了“纯SQL”硬套变量方案,线上环境出错难排查。
状态变化检测的核心从来不是函数本身,而是排序的确定性、分组的隔离性、以及NULL比较的严谨性。这三个点任一出错,结果就全盘失真。


















