You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL条件行时间差计算:为幼犬如厕日志添加上次排便/排尿间隔列

解决幼犬排便/排尿时间差计算问题

嘿,这个需求我太熟了!LAG函数确实搞不定这种「找最近的符合特定条件历史记录」的场景,不过有几种靠谱的方法能实现,我给你整理一下:

方案一:窗口函数追踪最近符合条件的时间(推荐,性能优)

如果你的数据库支持窗口函数(比如MySQL 8.0+、PostgreSQL、SQL Server等),这个方法是最高效的。我们用MAX() OVER()窗口函数来实时追踪最近一次排便/排尿的时间,再计算时间差:

SELECT 
    d.`datetime`, 
    d.`poo`, 
    d.`pee`,
    -- 计算距上次排便的时间差(单位:分钟,可换成HOUR/SECOND等)
    TIMESTAMPDIFF(MINUTE, 
        MAX(CASE WHEN d.`poo` = 1 THEN d.`datetime` END) OVER (ORDER BY d.`datetime` ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),
        d.`datetime`
    ) AS `poo_diff`,
    -- 计算距上次排尿的时间差
    TIMESTAMPDIFF(MINUTE, 
        MAX(CASE WHEN d.`pee` = 1 THEN d.`datetime` END) OVER (ORDER BY d.`datetime` ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),
        d.`datetime`
    ) AS `pee_diff`
FROM `diary` d 
WHERE 
    d.`user_id`=3 
    AND (d.`poo`=1 OR d.`pee`=1) 
    AND d.`datetime` >= DATE_ADD(CURDATE(), INTERVAL -7 DAY) 
    AND d.`datetime` <= CURDATE()
ORDER BY d.`datetime`;

逻辑说明:

  • MAX(CASE WHEN d.poo=1 THEN d.datetime END)会在遍历每一行时,只保留poo=1的时间戳,其他行返回NULL
  • 窗口范围ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING限定为当前行之前的所有行,MAX()会自动筛选出最近的有效时间戳
  • 第一次出现的排便/排尿记录,对应的_diff列会返回NULL,这符合“没有历史记录”的逻辑

方案二:自关联+子查询(兼容旧版本数据库)

如果你的数据库不支持窗口函数,比如MySQL 5.x,用自关联子查询也能实现,逻辑更直观:

SELECT 
    d.`datetime`, 
    d.`poo`, 
    d.`pee`,
    TIMESTAMPDIFF(MINUTE, 
        -- 找到当前行之前最近的排便记录时间
        (SELECT MAX(d2.`datetime`) FROM `diary` d2 
         WHERE d2.`user_id`=d.`user_id` 
         AND d2.`datetime` < d.`datetime` 
         AND d2.`poo`=1),
        d.`datetime`
    ) AS `poo_diff`,
    TIMESTAMPDIFF(MINUTE, 
        -- 找到当前行之前最近的排尿记录时间
        (SELECT MAX(d2.`datetime`) FROM `diary` d2 
         WHERE d2.`user_id`=d.`user_id` 
         AND d2.`datetime` < d.`datetime` 
         AND d2.`pee`=1),
        d.`datetime`
    ) AS `pee_diff`
FROM `diary` d 
WHERE 
    d.`user_id`=3 
    AND (d.`poo`=1 OR d.`pee`=1) 
    AND d.`datetime` >= DATE_ADD(CURDATE(), INTERVAL -7 DAY) 
    AND d.`datetime` <= CURDATE()
ORDER BY d.`datetime`;

逻辑说明:

  • 每个行都通过子查询,在同用户、更早时间范围内,找到最大的datetime(也就是最近的符合条件的记录)
  • 这个方法兼容性好,但数据量大时性能会下降,因为每行都要执行一次子查询

方案三:LAG函数+IGNORE NULLS(简洁版,需数据库支持)

如果你的数据库支持LAG() IGNORE NULLS(比如MySQL 8.0.22+、PostgreSQL),可以用更简洁的写法:

SELECT 
    d.`datetime`, 
    d.`poo`, 
    d.`pee`,
    TIMESTAMPDIFF(MINUTE,
        LAG(CASE WHEN d.`poo`=1 THEN d.`datetime` END IGNORE NULLS) OVER (ORDER BY d.`datetime`),
        d.`datetime`
    ) AS `poo_diff`,
    TIMESTAMPDIFF(MINUTE,
        LAG(CASE WHEN d.`pee`=1 THEN d.`datetime` END IGNORE NULLS) OVER (ORDER BY d.`datetime`),
        d.`datetime`
    ) AS `pee_diff`
FROM `diary` d 
WHERE 
    d.`user_id`=3 
    AND (d.`poo`=1 OR d.`pee`=1) 
    AND d.`datetime` >= DATE_ADD(CURDATE(), INTERVAL -7 DAY) 
    AND d.`datetime` <= CURDATE()
ORDER BY d.`datetime`;

逻辑说明:

  • CASE语句把非目标行为NULL,LAG(...) IGNORE NULLS会自动跳过NULL,直接取上一个有效时间戳
  • 写法最简洁,但要确认你的数据库版本支持这个特性

内容的提问来源于stack exchange,提问作者Ben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 15:52:49