datediff(day, order_date, getdate()) as days_since_today 直接计算每笔订单距今天的天数,无需窗口函数;错误写法是给datediff加over子句,因其非窗口函数。

用 DATEDIFF + GETDATE() 计算订单距今天数(SQL Server)
核心是把当前时间与订单日期做差,窗口函数本身不直接参与时间差计算,但能配合排序或分组动态生成“最近一笔”“上一笔”等参照点。若只是单纯算每笔订单到今天的天数,根本不需要窗口函数——直接用标量函数更高效。
常见错误是写成 SELECT *, DATEDIFF(day, order_date, GETDATE()) OVER (PARTITION BY customer_id ORDER BY order_date),这会报错:因为 DATEDIFF 不是窗口函数,不能加 OVER。
-
DATEDIFF必须放在SELECT列表中独立调用,如:DATEDIFF(day, order_date, GETDATE()) AS days_since_today - 如果数据库是 PostgreSQL,改用
CURRENT_DATE - order_date(返回整数天),或(CURRENT_DATE - order_date) AS days_since_today - MySQL 用
DATEDIFF(CURDATE(), order_date);注意参数顺序是DATEDIFF(较晚日期, 较早日期) - SQLite 用
julianday('now') - julianday(order_date),结果带小数,需CAST(... AS INTEGER)取整
用 LAG 算相邻两笔订单的间隔(跨行时间差)
这才是窗口函数真正发挥价值的场景:不是算“距今天”,而是算“距上一笔订单”。必须明确排序逻辑,否则 LAG 返回的是任意前一行,毫无业务意义。
典型错误是漏写 ORDER BY,比如只写 LAG(order_date) OVER (PARTITION BY customer_id) —— SQL Server 允许编译通过,但结果不可靠;PostgreSQL 会直接报错。
- 正确写法:
LAG(order_date) OVER (PARTITION BY customer_id ORDER BY order_date) - 再套一层
DATEDIFF得间隔天数:DATEDIFF(day, LAG(order_date) OVER (PARTITION BY customer_id ORDER BY order_date), order_date) - 首笔订单的
LAG返回NULL,DATEDIFF对NULL操作也返回NULL,符合预期 - 注意时区:如果
order_date是datetimeoffset或带时区的类型,LAG不改变时区语义,但DATEDIFF会按本地会话时区解释,可能产生偏差
用 ROW_NUMBER 标记最新/最旧订单再关联计算
当需要“每个客户最新一笔订单距今几天”,就不能只靠 LAG,得先定位最新记录。这时 ROW_NUMBER 配合子查询或 CTE 是最稳妥的方式。
容易踩的坑是误用 RANK() 或 DENSE_RANK():如果两个订单同一天,RANK() 会给它们相同序号,导致取 rn = 1 时返回多行,后续 JOIN 或 WHERE 出错。
- 务必用
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC),确保每客户只有一行rn = 1 - 若订单时间精度到秒,且存在完全相同的
order_date,建议追加主键排序防歧义:ORDER BY order_date DESC, order_id DESC - 别在
WHERE中直接写ROW_NUMBER() = 1——窗口函数不能出现在WHERE,必须嵌套进子查询或 CTE
性能和兼容性关键点
窗口函数本身不慢,但排序开销大。如果表没在 (customer_id, order_date) 上建复合索引,PARTITION BY + ORDER BY 会触发大量临时排序,百万级数据可能秒变分钟级。
- PostgreSQL 和 SQL Server 支持索引加速窗口排序;MySQL 8.0+ 也支持,但要求索引列顺序严格匹配
PARTITION BY和ORDER BY字段 - SQLite 窗口函数性能较弱,大数据量慎用;优先考虑用自连接或子查询模拟
- 所有数据库对
NULL的处理一致:LAG/LEAD默认返回NULL,但可指定第三个参数设默认值,如LAG(order_date, 1, '1970-01-01') ... - 别忽略
order_date字段是否允许为NULL——若存在空值,ORDER BY order_date在不同数据库中排序位置不同(SQL Server 默认NULLS FIRST,PostgreSQL 默认NULLS LAST),会影响LAG结果
实际业务里,“距今间隔”的需求背后往往藏着“是否超期”“复购周期”“沉睡用户识别”等逻辑。这些场景真正难的不是函数怎么写,而是日期字段的语义是否清晰——比如 order_date 是下单时间、支付成功时间,还是库存扣减时间?差几个小时就可能让分析结论翻车。










