我有一个具有挑战性的SQL问题:假设您有一个浏览量表,如下所示:
CREATE TABLE pageviews (
id INT(11) NOT NULL AUTO_INCREMENT,
user_id INT(11) NOT NULL,
timestamp DATETIME NOT NULL,
PRIMARY KEY (id)
)
在此表中,您有大量记录(> 1亿)。从这些数据中,您希望生成另一个如下所示的表:
CREATE TABLE sessions (
id INT(11) NOT NULL AUTO_INCREMENT,
user_id INT(11) NOT NULL,
started_at DATETIME NOT NULL,
ended_at DATETIME NOT NULL,
PRIMARY KEY (id)
)
规则是会话是任意数量的综合浏览量的任意序列,不包含任何大于30分钟的差距。
现在我已经设法使用存储过程生成此表,该存储过程使用循环来获取会话:
DELIMITER |
CREATE PROCEDURE generate_sessions()
BEGIN
TRUNCATE sessions;
INSERT INTO sessions
SELECT NULL, p.user_id, p.timestamp, p.timestamp FROM pageviews p
LEFT JOIN pageviews2 p2 ON p2.user_id = p.user_id AND p2.timestamp > p.timestamp AND p2.timestamp < DATE_ADD(p.timestamp, INTERVAL 30 MINUTE)
WHERE p2.id IS NULL;
REPEAT
UPDATE sessions s
LEFT JOIN pageviews p ON p.user_id = s.user_id AND p.timestamp < s.started_at AND p.timestamp > DATE_SUB(s.started_at, INTERVAL 30 MINUTE)
SET s.started_at = p.timestamp
WHERE p.id IS NOT NULL;
UNTIL ROW_COUNT() = 0 END REPEAT;
END |
基本上,该过程的作用是首先获取任何会话的最新页面视图,将其插入表中,然后迭代地回溯直到所有会话都完成。
毋庸置疑,这非常缓慢。任何人都有更好的解决方案,最好只涉及一个查询?
答案 0 :(得分:0)
这是MySQL的一个难题。你真的想要窗口函数。
但是,有一种方法。首先,您需要定义每个会话。为此,请查看浏览量之间大于半小时的间隔。以下查询向后查看,因此称为PrevSessionEnd
。
接下来,由于时间在增加,请为给定页面视图之前或之前发生的用户的所有页面视图选择此值的最大值。结果应该是每个页面视图都获得一个在会话中保持不变的值。第一个将是NULL,第二个将是第一个会话的最大时间戳,依此类推。
然后,按此数量分组。
select USER_ID, MIN(timestamp) as started_at, MAX(timestamp) as ended_at
from (select pv.*,
(select MAX(prevSessionEnd)
from (select pv.*,
(select timestamp
from pageviews pv2
where pv2.useid = pv.user_id and pv2.timestamp < pv.timestamp and
(pv.timestamp - pv2.timestamp) > 0.5/24
order by timestamp
limit 1
) as PrevSessionEnd
from pageviews pv
) pv2
where pv.user_id = pv2.user_id and pv2.timestamp <= pv.timestamp
) as SessionGrouper
from pageviews pv
) pv
group by user_id, SessionGrouper
此特定查询尚未经过测试,因此可能存在语法错误。
我将离开决赛insert
给你。
如果您在pageviews(user_id, timestamp)
上有索引,这反过来会更快。只能使用此索引解析子查询。