0

我有一个具有挑战性的 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 |

基本上,该过程所做的是首先获取任何会话的最新页面浏览量,将其插入表中,然后迭代回溯,直到所有会话完成。

不用说,这非常慢。任何人都有更好的解决方案,最好是只涉及一个查询的解决方案?

4

1 回答 1

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)。只能使用此索引来解析子查询。

于 2013-02-21T14:52:41.730 回答