复购间隔天数本质是同一用户两次购买的时间差,须用LEAD()按user_id分区、order_time升序计算,跨用户或错序会导致结果失真;最后一单LEAD返回NULL属正常,需显式过滤或处理。
复购间隔天数的本质是“同一用户两次购买之间的时间差”
直接用
是最自然的解法:对每个用户的订单按时间排序,取下一笔订单的下单时间,减去当前订单时间即可。关键不在于函数多难,而在于是否正确分区、排序和处理空值。
必须按 user_id 分区 + order_time 升序排序
漏掉
会导致跨用户计算,比如用户 A 的最后一单和用户 B 的第一单相减,结果完全失真;排序不用
或写成
会让
拿到错误的“下一笔”。
常见错误写法:
—— 缺少分区,全局排序,毫无业务意义。
正确结构应为:
注意:
必须是可比较的时间类型(如
或
),字符串格式如
在多数数据库中也能隐式转换,但若含时分秒且精度不一致(如
vs
),可能影响排序稳定性。
LEAD() 返回 NULL 时需显式过滤或处理
每个用户的最后一笔订单,
必然返回
,直接参与减法会得到
结果。这不是 bug,是预期行为——复购间隔只存在于“有下一笔”的场景。
实操建议:
若只想看有复购的记录,
最干净
若需保留所有订单并标记“首次购买”,可用
,但注意
是业务约定,不是通用解法
避免写成
后再
—— 部分数据库(如旧版 MySQL)不支持在
中直接引用窗口函数别名,得套一层子查询
不同数据库的 DATEDIFF 和时间减法语法差异要盯紧
行为基本一致,但算天数的方式五花八门:
MySQL:
PostgreSQL:
(返回天数整数)
SQL Server:
BigQuery:
,注意它要求输入是
类型,传
会报错
最容易被忽略的一点:有些数据库(如 Hive)不支持在窗口函数里直接做日期运算,必须先用
提取时间字段,再在外层计算差值——否则语法报错或结果异常。
LEAD()PARTITION BY user_idASCDESCLEAD()LEAD(order_time) OVER (ORDER BY order_time)LEAD(order_time) OVER (
PARTITION BY user_id
ORDER BY order_time ASC
)order_timeTIMESTAMPDATE'2023-05-01''2023-05-01 10:30:00''2023-05-01'LEAD()NULLNULLWHERE next_order_time IS NOT NULLCOALESCE(DATEDIFF(next_order_time, order_time), -1)-1DATEDIFF(LEAD(...), order_time)WHERE ... IS NOT NULLWHERELEAD()DATEDIFF(LEAD(order_time) OVER (...), order_time)(LEAD(order_time) OVER (...) - order_time)::INTEGERDATEDIFF(day, order_time, LEAD(order_time) OVER (...))DATE_DIFF(LEAD(date_col) OVER (...), date_col, DAY)DATETIMESTAMPLEAD()