假设我有一个如下所示的MySQL表,我跟踪用户(User.id)在我的网站(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万次展示,因此我正在尝试优化此报告生成器。
答案 0 :(得分: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分钟时都会更改。一旦用户值递增,我将为前一个用户插入更新session_id值,然后将temp数组重置为空,并继续与下一个用户进行该过程。
请注意,采用保持这样一个相对较小的临时/工作数组的方法可能很重要,就像你正在谈论的记录数一样,你可能无法读取整个结果集。进入内存中的数组。
填充数据后,查询变得微不足道:
每天的独特会话:
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字段的索引。
答案 1 :(得分: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 Fiddle 强>