标题可能会留下一些问题,所以我会详细解释。
我有一个包含聊天消息的(MySQL)表,这些消息都包含添加日期的日期时间列。
现在我想要实现的是我获取已经拥有的会话数量。
现在,什么是会话?自上一条消息起持续30分钟或更长时间后,新会话开始。
例如,数据:
2014-01-01 00:00:01
2014-01-01 00:20:01
2014-01-01 00:40:01
2014-01-01 00:60:01
将是一个会议
2014-01-01 00:00:01 <--
2014-01-01 00:32:01 <--
2014-01-01 00:35:01
2014-01-01 01:00:01
2014-01-01 02:00:01 <--
2014-01-01 02:20:01
将重新开始三个会话,我将箭头放在开头。
我不需要DQL示例MySQL会没事的,希望有人可以帮我解决这个问题。
编辑:下面给出的答案似乎适用于小提琴,但不适用于运行5.5.4的MySQL服务器,想知道这是某个设置还是sqlFiddle没有按预期工作。
答案 0 :(得分:1)
无论如何,我说我们需要假设一个LAG()
函数,但实际上我们可以比较增加一个分组计数器,所以我们实际上可以删除一个步骤,排序:
SELECT sentAt,
@Session := IF(sentAt < @SessionBoundary, @Session, @Session + 1) AS session,
@SessionBoundary := ADDTIME(sentAt, '00:30:00') AS sessionBoundary
FROM Message
JOIN (SELECT @Session := 0) n
ORDER BY sentAt
重要的是,请注意,对于正向连续范围类型(如date/time/timestamps)使用“独占上限”(<
)是一种好习惯,它是 30th < / strong>分钟实际开始你的新会话。也就是说,13:00的初始消息表示下一个会话在13:30开始(没有其他消息)。这有一个很好的特性,可以很好地排列一切,并且我不必担心我可能没有指定的小数秒的奇怪行为。
无论如何,这会返回如下结果:
sentAt session sessionBoundary
2014-01-01 00:00:01 1 2014-01-01 00:30:01
2014-01-01 00:32:01 2 2014-01-01 01:02:01
2014-01-01 00:35:01 2 2014-01-01 01:05:01
2014-01-01 01:00:01 2 2014-01-01 01:30:01
2014-01-01 02:00:01 3 2014-01-01 02:30:01
2014-01-01 02:20:01 3 2014-01-01 02:50:01
现在,既然你想要的只是一个简单的数量session
,你可以把它包装成子查询:
SELECT MAX(session)
FROM (SELECT sentAt,
@Session := IF(sentAt < @SessionBoundary, @Session, @Session + 1) AS session,
@SessionBoundary := ADDTIME(sentAt, '00:30:00') AS sessionBoundary
FROM Message
JOIN (SELECT @Session := 0) n
ORDER BY sentAt) MessageSession
SQL Fiddle Example
(注意:由于某些原因我不明白,使用初始工作作为子查询会导致小提琴从0开始而不是之前的1。请在您的服务器上测试,因为您可能需要初始化@Session = 1
代替0,或使用类似COUNT(DISTINCT session)
的内容。
......我们已经完成了。
虽然您只列出了想要计数,但是一旦您进行了会话分组,您就可以获得各种有趣的数据。现在获得MAX(sentAt)
/ MIN(sentAt)
每组,计算组中的消息数量等等是微不足道的。例如,您可以通过以下方式说“查找所有长时间运行的会话”:
SELECT session,
MIN(sentAt) AS firstMessageAt, MAX(sentAt) AS lastMessageAt, COUNT(*) AS messages
FROM (SELECT sentAt,
@Session := IF(sentAt < @SessionBoundary, @Session, @Session + 1) AS session,
@SessionBoundary := ADDTIME(sentAt, '00:30:00') AS sessionBoundary
FROM Message
JOIN (SELECT @Session := 0) n
ORDER BY sentAt) MessageSession
GROUP BY session
HAVING ADDTIME(MIN(sentAt), '24:00:00') < MAX(sentAt)
(查找已运行至少24小时的所有会话)
答案 1 :(得分:0)
对于我的特定情况,我使用如下查询解决了它:
SELECT *
FROM chat c
WHERE NOT EXISTS
(
SELECT *
FROM chat c2
WHERE c2.date_add <= (c.date_add + INTERVAL 30 MINUTE)
AND c2.date_add > c.date_add
)
这里唯一的问题是我无法看到会话何时开始,但这对我的特定情况来说已经足够了。我很乐意接受更好的答案!
答案 2 :(得分:0)
困难的部分是,当问题是程序性时,SQL是一种非过程语言。
您的提案是一个合理的查询,可以在纯SQL中完成。如果你想更进一步,我建议你使用一个程序脚本,可以用多种语言编写,Python,Perl,Ruby ......
在伪语言中,它可能是:
long last=-1800 // time of previous line
long beg = -1 // begin time for current session
CREATE_QUERY_FOR : SELECT UNIX_TIMESTAMP(dat) FROM chat ORDER BY dat
LOOP_PER_LINE_FETCHED getting dat
if (dat - last > 30 * 60)
then
if last != -1 // we had a session
then NOTE SESSION begin at beg and ending at last
endif
beg = dat // start a new session
endif
last = dat
END_LOOP
恕我直言,这是开始和结束所有会话的唯一方式(或者至少是最简单和最有效的方式) - 但我必须承认它可能不是你所要求的......
答案 3 :(得分:0)
SELECT distinct *
FROM chat c
WHERE NOT EXISTS
(
SELECT *
FROM chat c2
WHERE c2.date_add > (c.date_add - INTERVAL 30 MINUTE)
AND c2.date_add < c.date_add
)