如何计算非连续行之间经过的时间?

Posted

技术标签:

【中文标题】如何计算非连续行之间经过的时间?【英文标题】:How to calculate time elapsed between non-consecutive rows? 【发布时间】:2020-07-25 22:35:08 【问题描述】:

我有一张如下表:

CREATE TABLE useraudit(
    `id` INT NOT NULL AUTO_INCREMENT,
    `event` INT(1) unsigned,
    `datetime` DATETIME,
    `computer` VARCHAR(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
    `username` VARCHAR(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
    `session` VARCHAR(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
    `server` VARCHAR(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `useraudit` (`id`, `event`, `datetime`, `computer`, `username`, `session`, `server`) VALUES
(1, 0, '2020-07-24 20:21:04', 'DC1', 'testuser', '', '\\\\DC1 '),
(2, 0, '2020-07-24 20:21:04', 'DC1', 'testuser', 'Console', '\\\\DC1 '),
(3, 1, '2020-07-24 20:49:19', 'DC1', 'testuser', 'Console', '\\\\DC1 '),
(4, 1, '2020-07-24 21:19:33', 'TEST-PC-2', 'testuser', 'Console', '\\\\DC1 '),
(5, 0, '2020-07-24 21:21:35', 'TEST-PC-2', 'testuser', '', '\\\\DC1 '),
(6, 1, '2020-07-24 21:22:28', 'TEST-PC-2', 'testuser', 'Console', '\\\\DC1 '),
(7, 0, '2020-07-24 21:25:48', 'TEST-PC-2', 'testuser', '', '\\\\DC1 '),
(8, 1, '2020-07-24 21:29:16', 'TEST-PC-2', 'testuser', 'Console', '\\\\DC1 '),
(9, 1, '2020-07-24 21:29:18', 'TEST-PC-2', 'testuser', 'Console', '\\\\DC1 '),
(10, 0, '2020-07-24 21:30:06', 'TEST-PC-2', 'testuser', '', '\\\\DC1 '),
(11, 0, '2020-07-24 21:30:07', 'TEST-PC-2', 'testuser', 'Console', '\\\\DC1 '),
(12, 1, '2020-07-24 21:30:56', 'TM-PC', 'testuser', 'Console', '\\\\DC1 '),
(13, 1, '2020-07-24 21:31:07', 'TM-PC', 'testuser', 'Console', '\\\\DC1 '),
(14, 0, '2020-07-24 21:33:30', 'TM-PC', 'testuser', '', '\\\\DC1 '),
(15, 0, '2020-07-24 21:33:31', 'TM-PC', 'testuser', 'Console', '\\\\DC1 '),
(16, 1, '2020-07-25 14:32:25', 'TEST-PC-1', 'testuser', 'RDP-Tcp#7', '\\\\DC1 '),
(17, 1, '2020-07-25 14:32:25', 'TEST-PC-1', 'testuser', 'RDP-Tcp#7', '\\\\DC1 '),
(18, 0, '2020-07-25 14:32:37', 'TEST-PC-1', 'testuser', '', '\\\\DC1 '),
(19, 0, '2020-07-25 14:32:38', 'TEST-PC-1', 'testuser', 'RDP-Tcp#7', '\\\\DC1 '),
(20, 1, '2020-07-25 14:39:46', 'TEST-PC-1', 'testuser', 'RDP-Tcp#8', '\\\\DC1 '),
(21, 1, '2020-07-25 14:39:46', 'TEST-PC-1', 'testuser', 'RDP-Tcp#8', '\\\\DC1 '),
(22, 0, '2020-07-25 15:02:10', 'TEST-PC-1', 'testuser', '', '\\\\DC1 '),
(23, 0, '2020-07-25 15:02:11', 'TEST-PC-1', 'testuser', 'RDP-Tcp#8', '\\\\DC1 '),
(24, 0, '2020-07-25 15:02:28', 'DC1', 'testuser', '', '\\\\DC1 '),
(25, 0, '2020-07-25 15:02:28', 'DC1', 'testuser', 'Console', '\\\\DC1 ');

我想做的是能够计算登录时间 - 每个会话和每个用户的累计登录时间。这有点复杂,因为某些事件似乎会生成 2 个事件(尤其是发生远程会话时)。

我很难解决这个问题;我的第一个倾向是GROUP BY datetime,除了其中一些重复事件不会在同一秒发生,而是在第二个之前或之后发生,所以这不太行。

目前,我认为通过按datetime ASC 排序,然后为每个event = 1 排序(代表登录而不是注销),搜索直到我找到下一个event = 0 与相同的computerusername。从技术上讲,这些重复的日志是 FILO(先进后出),但由于它们彼此相差几秒钟,这并不重要。

如果我可以查询这个,我想过滤掉具有基本相同秒数的结果并不难;除此之外,我将如何处理这种查询?我基本上希望得到类似的东西:

username | computer | logon_time           | duration`
testuser   DC1         2020-07-24 20:49:19  (seconds between 2020-07-24 20:49:19 and 2020-07-25 15:02:28)
testuser  TEST-PC-2    2020-07-24 21:19:19   2 minutes, 2 seconds
testuser  TEST-PC-2    2020-07-24 21:22:28   3 minutes, 20 seconds
testuser  TEST-PC-2    2020-07-24 21:29:16   50 seconds
testuser  TEST-PC-2    2020-07-24 21:29:18   49 seconds
testuser  TM-PC        2020-07-24 21:30:56   2 minutes, 24 seconds
etc.

我在这里使用人类友好的时间,但实际上这些时间都是以秒为单位的持续时间。

(并非所有行都可以使用;这里的前两条记录是 0,因此可以忽略。)

由于我们不得不这样做,因此可以假设特定用户/计算机在登录事件之后的下一个注销事件与该事件匹配(在这种情况下,不要再次将其用于不同的登录事件)。换句话说,一些数据可能是我们必须忽略的“噪音”,并且登录/注销对不能“重复使用”。

我开始在 php 中执行此操作,但这意味着我失去了对数据进行排序和聚合的能力,这并不理想。不确定这是否以某种方式在尖叫窗口,但我使用的是 MariaDB 10.1,它不能很好地/根本不支持它们。

【问题讨论】:

这是一个孤岛问题。在没有窗口函数的情况下求解 iy 确实具有挑战性。有升级到 MariaDB 10.3 或更高版本的计划吗? @GMB 最终希望如此,但不幸的是,不会在不久的将来。 @GMB 如果这是成败的事情,如果 10.1 不起作用,MariaDB 10.3 解决方案总比没有好。在这种情况下,我可以看到我能做些什么。 【参考方案1】:

这将是要使用的正确查询(除非您还有不同的服务器,否则您必须扩展您的连接条件)。

SELECT lon.`username`, 
       lon.`computer`, 
       lon.`session` ,
       lon.`datetime`                  AS logontime, 
       lof.`datetime`                  AS logofftime, 
       lof.`datetime` - lon.`datetime` AS duration 
FROM   `useraudit` lon 
       INNER JOIN `useraudit` lof 
               ON lon.`computer` = lof.`computer` 
                  AND lon.`username` = lof.`username` 
                  AND lon.`session` = lof.`session` 
                  AND lon.`id` < lof.`id` 
WHERE  lon.`event` = 1 
       AND lof.`event` = 0 
       AND NOT EXISTS (SELECT lon1.id 
                       FROM   `useraudit` lon1 
                       WHERE  lon1.`event` = 1 
                          AND lon1.`computer` = lon.`computer` 
                          AND lon1.`username` = lon.`username` 
                          AND lon1.`session` = lon.`session` 
                              AND lon1.`id` < lon.`id` 
                              AND NOT EXISTS (SELECT lof1.id 
                                              FROM   `useraudit` lof1 
                                              WHERE  lof1.`event` = 0 
                                                     AND lof1.`computer` = lon.`computer` 
                                                     AND lof1.`username` = lon.`username` 
                                                     AND lof1.`session` = lon.`session` 
                                                     AND lon1.`id` < 
                                                         lof1.`id` 
                                                     AND lof1.`id` < 
                                                         lon.`id`))
        AND NOT EXISTS (SELECT lof2.id 
                       FROM   `useraudit` lof2 
                       WHERE  lof2.`event` = 0 
                          AND lof2.`computer` = lon.`computer` 
                          AND lof2.`username` = lon.`username` 
                          AND lof2.`session` = lon.`session` 
                              AND lon.`id` < lof2.`id` AND lof2.`id` < lof.`id`) 
             
ORDER  BY lon.`datetime`; 

【讨论】:

等等,还不正确,我忘记匹配某些字段了...稍等... 这太棒了!你能解决 14 分钟前的问题吗? 好吧,前提是结果符合您的预期。顺便说一句,我现在要睡觉了。现在是荷兰凌晨2:00... 如果它不起作用,它可能是一个很好的开始,让你做一些适合你的东西......

以上是关于如何计算非连续行之间经过的时间?的主要内容,如果未能解决你的问题,请参考以下文章

识别 Pandas 中的非连续行

每个客户的连续行之间的Haversine距离

计算同一列之间的差异,在python中由另一列分组的连续行

SQL 查询 - 计算值大于 X 的连续行数

计算每天 Ms-Sql 总行中的最大连续行

SQL:检测具有相同键的连续行的连续块