lag()本身不计算时间差,只取上一行时间值;必须用当前行时间减去lag()结果,且时间字段须为timestamp/datetime类型,不可为字符串,否则减法失败或返回null/0。

直接说结论:LAG() 本身不计算时间差,它只取上一行的值;时间差必须用当前行时间减去 LAG() 返回的时间值,且必须确保时间字段是 TIMESTAMP 或 DATE 类型(不能是字符串)。
为什么直接写 LAG(created_at) 不会得到时间差?
LAG() 是一个窗口函数,作用只是“把上一行的某个字段值拿下来”,它不执行任何运算。你拿到的是一个时间值,不是差值。要算差,得显式做减法——而且数据库对时间相减的行为因引擎而异:
- PostgreSQL 支持
current_time - LAG(created_at) OVER (ORDER BY created_at),结果是INTERVAL类型(如1 day 02:30:15) - MySQL 8.0+ 需用
TIMESTAMPDIFF(SECOND, LAG(created_at) OVER (ORDER BY created_at), created_at)指定单位,否则直接相减可能触发隐式转换或报错 - SQL Server 要用
DATEDIFF(second, LAG(created_at) OVER (ORDER BY created_at), created_at),不支持直接减法
常见错误:字符串时间字段导致 NULL 或 0 结果
如果 created_at 是 VARCHAR(比如存成 '2024-03-15 14:22:08'),LAG() 虽能取值,但后续减法大概率失败或返回 0/NULL——因为数据库无法自动识别字符串为时间。
- 检查类型:
SELECT pg_typeof(created_at) FROM logs LIMIT 1(PostgreSQL)或DESCRIBE logs(MySQL) - 临时修复(仅调试):
LAG(CAST(created_at AS TIMESTAMP)) OVER (ORDER BY created_at),但长期应改列类型 - 注意时区:若字段是
TIMESTAMP WITHOUT TIME ZONE,跨时区排序可能错乱,优先用TIMESTAMP WITH TIME ZONE
正确写法示例(以 PostgreSQL 为例)
SELECT id, created_at, LAG(created_at) OVER (ORDER BY created_at) AS prev_created_at, created_at - LAG(created_at) OVER (ORDER BY created_at) AS diff_interval, EXTRACT(EPOCH FROM (created_at - LAG(created_at) OVER (ORDER BY created_at))) AS diff_seconds FROM events ORDER BY created_at;
关键点:
-
ORDER BY必须明确,否则LAG()行为不可预测 - 第一行没有“上一行”,
LAG()返回NULL,对应差值也是NULL,这是正常行为 - 用
EXTRACT(EPOCH FROM ...)转成秒数便于排序或聚合;若只要天数,可用EXTRACT(DAY FROM ...)
最常被忽略的是排序依据和数据类型一致性——哪怕 LAG() 语法写对了,只要 ORDER BY 字段有重复值(比如多条记录同一秒创建),数据库可能任意决定“上一行”是谁,差值就不可靠。











