MySQL 根据特定规则选择后行

问题描述

我有一张这样的桌子:

CREATE TABLE IF NOT EXISTS `logging` (
  `id` int(6) unsigned NOT NULL,`status` varchar(150) NOT NULL,`timestamp` DATETIME NOT NULL,PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `logging` (`id`,`status`,`timestamp`) VALUES
  ('1','logout','2021-01-01 05:01:00'),('2','login','2021-01-01 06:02:00'),('3','online','2021-01-01 06:03:00'),('4','away','2021-01-01 06:04:00'),('5','2021-01-01 06:05:00'),('6','2021-01-02 04:00:00'),('7','2021-01-02 04:05:00'),('8','2021-01-02 04:07:00'),('9','2021-01-02 04:08:00'),('10','break','2021-01-02 04:10:00'),('11','2021-01-02 04:15:00'),('12','2021-01-02 04:55:00'),('13','2021-01-02 05:04:00'),('14','2021-01-02 05:05:00'),('15','2021-01-03 05:01:00'),('16','2021-01-03 05:02:00'),('17','2021-01-03 05:04:00'),('18','2021-01-03 05:05:00'),('19','2021-01-04 03:05:00'),('20','2021-01-04 05:07:00'),('21','2021-01-04 06:00:00'),('22','2021-01-05 10:00:00'),('23','2021-01-05 11:00:00'),('24','2021-01-05 11:01:00'),('25','2021-01-06 06:01:00'),('26','2021-01-07 06:01:00'),'2021-01-07 07:01:00');
id 状态 时间戳
1 注销 2021-01-01 05:01:00
2 登录 2021-01-01 06:02:00
3 在线 2021-01-01 06:03:00
4 离开 2021-01-01 06:04:00
5 在线 2021-01-01 06:05:00
6 注销 2021-01-02 04:00:00
7 登录 2021-01-02 04:05:00
8 在线 2021-01-02 04:07:00
9 注销 2021-01-02 04:55:00

......

上面插入查询中提供的数据。

我想要一个输出

日期 A(在线) B(注销)
2021-01-01 2021-01-02 04:07:00 2021-01-02 04:55:00
2021-01-02 2021-01-02 05:05:00 2021-01-03 04:59:59
2021-01-03 2021-01-03 05:05:00 2021-01-04 03:05:00
2021-01-04 2021-01-04 06:00:00 2021-01-04 04:59:59
2021-01-05 2021-01-04 05:00:00 2021-01-05 10:00:00
2021-01-06 2021-01-04 11:00:00 2021-01-06 04:59:59

规则是,1 个日志日是从 5:00:00 -(第二天)04:59:59。 'A' 是从最后一次在线开始的时间戳(登录后,如果有的话),'B' 是从最后一次注销开始的时间戳(如果没有注销,B 设置为 04:59:59。 另一个规则是,当最后一天在 'online' 之后没有 'logout' 时,将计入下一个日志日(如果最后一个日志日的最后一条记录为 ',则下一个日志日设置为 05.00.00在线')

目前我正在使用此查询来应用日志日规则:

SELECT date(t1.timestamp) dt,(t2.timestamp) A,(t3.timestamp) B,t1.status,t2.status,t3.status
FROM logging t1
JOIN logging t2 ON t1.timestamp < t2.timestamp
JOIN logging t3 ON t2.timestamp < t3.timestamp
WHERE 
t1.status = 'login'
AND t2.status = 'online'
AND t3.status = 'logout'
AND NOT EXISTS ( SELECT NULL
               FROM loggingt4
               WHERE t1.timestamp < t4.timestamp
                 AND t4.timestamp < t2.timestamp
                 AND t4.status IN ('login','logout') )
AND NOT EXISTS ( SELECT NULL
               FROM logging t5
               WHERE t2.timestamp < t5.timestamp
                 AND t5.timestamp < t3.timestamp
                 AND t5.status IN ('login','logout'))
AND DATE(t1.timestamp - INTERVAL 6 HOUR) = DATE(t3.timestamp - INTERVAL '05:59:59' HOUR_SECOND);

解决方法

检查这个:

WITH cte AS (
SELECT DATE(t1.`timestamp` - INTERVAL 5 HOUR) `date`,MAX(t1.`timestamp`) login,MAX(t2.`timestamp`) online,MAX(t3.`timestamp`) logout
FROM logging t1
JOIN logging t2 ON t1.`timestamp` < t2.`timestamp`
JOIN logging t3 ON t2.`timestamp` < t3.`timestamp`
WHERE t1.status = 'login'
  AND t2.status = 'online'
  AND t3.status = 'logout'
  AND NOT EXISTS ( SELECT NULL
                   FROM logging t4
                   WHERE t1.`timestamp` < t4.`timestamp`
                     AND t4.`timestamp` < t2.`timestamp`
                     AND t4.status IN ('login','online','logout') )
  AND NOT EXISTS ( SELECT NULL
                   FROM logging t5
                   WHERE t2.`timestamp` < t5.`timestamp`
                     AND t5.`timestamp` < t3.`timestamp`
                     AND t5.status IN ('login','logout') )
GROUP BY `date`
)
SELECT `date`,online,CASE WHEN DATE(online - INTERVAL 5 HOUR) = DATE(logout - INTERVAL '04:59:59' HOUR_SECOND)
            THEN logout
            ELSE DATE(online + INTERVAL 19 HOUR) + INTERVAL '04:59:59' HOUR_SECOND
            END logout
FROM cte

fiddle

CTE 仅用于可见性 - 您可以将所有内容合并为一个查询。