如何检测 MySQL DATETIME 列中的连续小时数?
Posted
技术标签:
【中文标题】如何检测 MySQL DATETIME 列中的连续小时数?【英文标题】:How can I detect consecutive hours in a MySQL DATETIME column? 【发布时间】:2014-02-19 20:11:35 【问题描述】:我们有一张这样的表:
描述 time_slots; id int(11) user_id int(11) start_time 日期时间start_time
字段始终以小时为增量(例如2013-09-04 16:00:00
)
我们的数据科学家想要查询此表,以识别每个 user_id
的连续 start_time
记录,以便她可以创建如下所示的派生表:
例如,给定这个数据:
user_id: 5, start_time: 2013-09-04 16:00:00 user_id:5,开始时间:2013-09-04 17:00:00 user_id: 5, start_time: 2013-09-04 18:00:00 user_id:6,开始时间:2013-09-04 16:00:00 user_id:6,开始时间:2013-09-04 17:00:00 user_id:6,开始时间:2013-09-04 18:00:00 user_id:6,开始时间:2013-09-04 20:00:00 user_id:6,开始时间:2013-09-04 21:00:00 user_id:6,开始时间:2013-09-04 22:00:00...我们可以得出这个输出:
user_id: 5, start_time: 2013-09-04 16:00:00, end_time: 2013-09-04 18:00:00 user_id: 6, start_time: 2013-09-04 16:00:00, end_time: 2013-09-04 18:00:00 user_id: 6, start_time: 2013-09-04 20:00:00, end_time: 2013-09-04 22:00:00对于给定的用户,每天可能有多个这些开始/结束“块”(但它们不会重叠)。
在我进入计划 B(设置非规范化数据仓库)之前,有什么想法可以在 SQL 中完成吗?
【问题讨论】:
【参考方案1】:取决于您的数据库...窗口函数可以实现这一点。生成一个表示与前一列的增量的列(因此您需要它按 user_id、startTime 排序);然后,您可以使用该增量列进行分组。由于连续块在增量中将由“1”表示,并且新块的编号会更高。
您也可以通过使用子选择进行连接并将其偏移 1 来实现这一点,例如加入 ROW_NUMBER 和 ROW_NUMBER-1,然后您可以计算时间戳之间的增量,并使用外部选择进行一些魔术以得到你想要的。关键是增量。
你可以这样做:
SET @prevUser := null;
SET @prevStartTime := 0;
SET @groupNumber := 1;
SET @groupPrevUser := null;
select
user,
groupNumber,
min(startTime),
max(endTime),
max(endTime) - min(startTime) as 'duration'
from
(SELECT
user,
startTime,
endTime,
delta,
IF(delta != 10000 || @groupPrevUser <> user, @groupNumber:=@groupNumber + 1, @groupNumber) 'groupNumber',
@groupPrevUser:=user
from
(SELECT
user,
startTime,
endTime,
IF(@prevUser <> user || @prevStartTime = 0, endTime - startTime, startTime - @prevStartTime) AS delta,
@prevUser:=user,
@prevStartTime:=startTime
FROM
queries
ORDER BY user , startTime) userData) userGroupData
group by user , groupNumber
得到这个结果:
# user, groupNumber, min(startTime), max(endTime), duration
bob, 1, 1392060000, 1392080000, 20000
bob, 2, 1392090000, 1392100000, 10000
jim, 3, 1392150000, 1392180000, 30000
使用这个基表:
# user, startTime, endTime
bob, 1392060000, 1392070000
bob, 1392070000, 1392080000
bob, 1392090000, 1392100000
jim, 1392150000, 1392160000
jim, 1392160000, 1392170000
jim, 1392170000, 1392180000
【讨论】:
mysql 中不存在窗口函数。 使用 RANK 和 RANK-1 或 user_id 进行自我加入,开始时间 - 1 小时作为键会给你同样的能力 顺便说一句,InfiniDB 是一个与 MySQL 兼容的数据库(开源 GPLv2),具有建立在标准 MySQL 函数之上的窗口函数。 看起来 MySQL 可以被骗成 RANK。请参阅sqlfiddle.com/#!2/26e10/9 然后您可以使用自联接而不是我建议的半联接。不知道过程语言处理是否会带走我期望自加入的速度提升。不想引发数据库大战,但这确实是 MySQL 的不足。 与MySQL中计算排名的方法相同,可以计算增量【参考方案2】:我的第一个建议是更改架构为 block_by_id 添加计数器。那么你的问题是一个简单的最小值-最大值。并且当创建记录时,可以通过查看 (1) 是否已经有此 user_id 的记录和 (2) 是否比新记录早一个多小时来确定块号。我想您可以将其视为非规范化的,在这种情况下,我们的想法是“即时”找出块。
SELECT user_id, MIN(start_time) AS start_time, MAX(start_time) AS start_time
FROM time_slots t1
WHERE NOT EXISTS
(SELECT 1 FROM time_slots AS t2 WHERE t1.user_id = t2.user_id
AND timestampdiff(HOUR, t1.start_time, t2.start_time)=1
/* replace with date arithmetic function of your RDBMS if need be */
)
GROUP BY user_id;
我没有任何调整 MySQL 的经验。可能是不同的 timediff 表达式将允许它使用(user_id, start_time)
上的索引。
【讨论】:
以上是关于如何检测 MySQL DATETIME 列中的连续小时数?的主要内容,如果未能解决你的问题,请参考以下文章
如何将两列中的日期和时间放入pandas to_datetime并设置为索引[重复]
如何在 Presto 中获取连续日期,其中一列中的开始日期和另一列中的结束日期