2

假设我有一个如下所示的 MySQL 表,我在其中跟踪用户 (User.id) 何时 (Date) 阅读我网站 (Article.id) 上的文章:

------------------------------------------
Article_Impressions
------------------------------------------
date                | user_id | article_id
--------------------+---------+-----------
2013-04-02 15:33:23 | 815     | 2342
2013-04-02 15:38:21 | 815     | 108
2013-04-02 15:39:33 | 161     | 4815
...

我正在尝试确定我有多少会话,以及给定日期每个用户的平均会话持续时间。当一篇文章在另一篇文章之后的 30 分钟内没有被阅读时,会话结束。

问题

我怎样才能有效地确定我在某一天有多少会话?我正在使用 PHP 和 MySQL。

我的第一个想法是查询给定日期的所有数据,按用户排序。然后我遍历每个用户,检查一次印象是否在最后一次印象的 30 分钟内,并统计每个用户当天的会话总数。

由于我们的网站每天有大约 200 万次展示,因此我正在尝试优化此报告生成器。

4

2 回答 2

1

如果用户“会话”的概念对您的分析很重要,那么我将开始在您的表中记录数据,以便查询与会话相关的数据不是一个痛苦的过程。一个简单的方法是记录您的 PHP 会话 ID。如果您的 PHP 会话 ID 设置为具有相同的 30 分钟到期时间,并且您将 PHP 会话 ID 记录到此表中,那么您基本上将拥有您正在寻找的内容。

当然,这对您现有的记录没有帮助。我可能会继续创建会话字段,然后用随机生成的“会话”ID 重新填充它。我不会为此寻找一个完整的 SQL 解决方案,因为它在处理边缘情况(跨天的会话等)方面可能无法满足您的要求。我会编写一个脚本来执行此回填,其中将包含您需要的所有逻辑。

我的一般方法是像这样选择所有记录:

SELECT user_id, date /* plus any other fields like unique id that you would need for insert */
FROM Article_Impressions
WHERE session_id IS NULL
ORDER BY user_id ASC, date ASC

注意:确保您在 user_id 和 date 字段上都有索引。

然后,我将遍历结果集,构建每个 user_id 的临时数组,并遍历该数组以获取所有日期值,并分配一个随机生成的会话 id,每次日期更改大于 30 分钟时该会话 id 都会更改。一旦用户值增加,我将为该前一个用户进行插入以更新 session_id 值,然后将临时数组重置为空并继续与下一个用户进行该过程。

请注意,采取像这样保持相对较小的临时/工作数组的方法可能很重要,因为您正在谈论的记录数,您可能无法将整个结果集读入数组在记忆中。

填充数据后,查询变得微不足道:

每天的独特会话:

SELECT DATE(date) as `day`, COUNT(DISTINCT session_id) AS `unique_sessions`
FROM Article_Impressions
GROUP BY `day`
ORDER BY `day` DESC /* or ASC depending on how you want to view it */

每天平均会话数:

SELECT AVG(sessions_per_day.`unique_sessions`) AS `average_sessions_per_day`
FROM
  (
    SELECT DATE(date) as `day`, COUNT(DISTINCT session_id) AS `unique_sessions`
    FROM Article_Impressions
    GROUP BY `day`
  ) AS sessions_per_day
GROUP BY sessions_per_day.`day`

注意:您需要新 session_id 字段的索引。

于 2013-04-02T22:40:21.600 回答
1

试试这个查询

查询 1

select 
  @sessionId:=if(@prevUser=user_id AND diff <= 1800 , @sessionId, @sessionId+1) as sessionId,
  @prevUser:=user_id AS user_id, 
  article_id,
  date,
  diff
from 
  (select @sessionId:=0, @prevUser:=0) b
join
  (select 
    TIME_TO_SEC(if(@prevU=user_id, TIMEDIFF(date, @prevD), '00:00')) as diff,
    @prevU:=user_id as user_id,
    @prevD:=date as date,
    article_id
  from 
    tbl 
  join
    (select @prev:=0, @prevU=0)a
  order by 
    user_id, 
    date) a

[结果]

| SESSIONID | USER_ID | ARTICLE_ID |                DATE | DIFF |
-----------------------------------------------------------------
|         1 |     161 |       4815 | 2013-04-02 15:39:33 |    0 |
|         2 |     815 |       2342 | 2013-04-02 15:33:23 |    0 |
|         2 |     815 |        108 | 2013-04-02 15:38:21 |  298 |
|         3 |     815 |        108 | 2013-04-02 16:38:21 | 3600 |

如果根据您在问题中提到的要求,下一篇文章在 30 分钟后阅读,则此查询将为每个新用户以及同一用户返回一个唯一会话。diff 列返回同一用户的 2 篇文章之间的秒数差异,这有助于我们计算 sessionId。现在使用此结果,您可以轻松计算每个用户的平均时间以及每个会话的总时间。

希望这可以帮助你...

SQL小提琴

于 2013-04-03T04:01:26.930 回答