使用连接在MySQL中执行队列分析的两种方法
我做一个处理器。输入参数:时间范围和步骤、条件初始事件到EXTRACT队列、附加条件保留事件到每N小时/天/月后检查。输出参数:队列分析网格,如下所示:使用连接在MySQL中执行队列分析的两种方法,mysql,sql,analytics,Mysql,Sql,Analytics,我做一个处理器。输入参数:时间范围和步骤、条件初始事件到EXTRACT队列、附加条件保留事件到每N小时/天/月后检查。输出参数:队列分析网格,如下所示: 0h | 16h | 32h | 48h | 64h | 80h | 96h | cohort #00 15 | 6 | 4 | 1 | 1 | 2 | 2 | cohort #01 1 | 35 | 8 |
0h | 16h | 32h | 48h | 64h | 80h | 96h |
cohort #00 15 | 6 | 4 | 1 | 1 | 2 | 2 |
cohort #01 1 | 35 | 8 | 0 | 2 | 0 | 1 |
cohort #02 0 | 3 | 31 | 11 | 5 | 3 | 0 |
cohort #03 0 | 0 | 4 | 27 | 7 | 6 | 2 |
cohort #04 0 | 1 | 1 | 4 | 29 | 4 | 3 |
#user-id, timestamp, event_name
events_view (uid varchar(64), tm int(11), e varchar(64))
user_id | 1st timestamp | cohort_no
uid1 1423836540 0
uid2 1423839540 0
uid3 1423841160 1
uid4 1423841460 2
...
uidN 1423843080 M
user_id | 1st timestamp | cohort_no | user_id | other fields...
uid1 1423836540 0 uid1
uid2 1423839540 0 null
uid3 1423841160 1 null
uid4 1423841460 2 uid4
...
uidN 1423843080 M null
基本上:
获取群组:从时间开始到每一步,每个时段都做1件事的唯一用户。
找出每个队列中有多少人在N秒后做了2件事,N*2秒,N*3秒,依此类推到现在。
简而言之,我有两个解决方案。其中一个工作太慢,包括一个沉重的选择,每个数据步骤都有连接:1天、2天、3天等等。我想通过将每个数据步骤的结果连接到队列来优化它——这是第二个解决方案。这看起来很有效,但我不确定这是否是最好的方法,也不确定它是否会给出相同的结果,即使队列交叉。请检查一下
这就是整个故事。
我有一张超过100000个事件的表格,大致如下:
0h | 16h | 32h | 48h | 64h | 80h | 96h |
cohort #00 15 | 6 | 4 | 1 | 1 | 2 | 2 |
cohort #01 1 | 35 | 8 | 0 | 2 | 0 | 1 |
cohort #02 0 | 3 | 31 | 11 | 5 | 3 | 0 |
cohort #03 0 | 0 | 4 | 27 | 7 | 6 | 2 |
cohort #04 0 | 1 | 1 | 4 | 29 | 4 | 3 |
#user-id, timestamp, event_name
events_view (uid varchar(64), tm int(11), e varchar(64))
user_id | 1st timestamp | cohort_no
uid1 1423836540 0
uid2 1423839540 0
uid3 1423841160 1
uid4 1423841460 2
...
uidN 1423843080 M
user_id | 1st timestamp | cohort_no | user_id | other fields...
uid1 1423836540 0 uid1
uid2 1423839540 0 null
uid3 1423841160 1 null
uid4 1423841460 2 uid4
...
uidN 1423843080 M null
输入行示例:
"user_sampleid1", 1423836540, "level_end:001:win"
为了首先进行群组分析,我提取了群组:例如,从2015-02-13开始到2015-02-16结束的10小时内发送特别活动“首次发布”的用户。这篇文章中的所有代码都经过了简化和缩短,以了解其思想
DROP TABLE IF EXISTS tmp_c;
create temporary table tmp_c (uid varchar(64), tm int(11), c int(11) );
set beg = UNIX_TIMESTAMP('2015-02-13 00:00:00');
set en = UNIX_TIMESTAMP('2015-02-16 00:00:00');
select min(tm) into t_start from events_view ;
select max(tm) into t_end from events_view ;
if beg < t_start then
set beg = t_start;
end if;
if en > t_end then
set en = t_end;
end if;
set period = 3600 * 10;
set cnt_c = ceil((en - beg) / period) ;
/*works quick enough*/
WHILE i < cnt_c DO
insert into tmp_c (
select uid, min(tm), i from events_view where
locate("1st_launch", e) > 0 and tm > (beg + period * i)
AND tm <= (beg + period * (i+1)) group by uid );
SET i = i+1;
END WHILE;
然后,我需要再次划分时段的时间范围,并计算每个时段每个队列中有多少用户发送了事件级别_end:001:win。
对于每个小时间段,我选择所有发送了level_end:001:win事件的唯一用户,并将其加入tmp_c队列表。所以我有这样的想法:
0h | 16h | 32h | 48h | 64h | 80h | 96h |
cohort #00 15 | 6 | 4 | 1 | 1 | 2 | 2 |
cohort #01 1 | 35 | 8 | 0 | 2 | 0 | 1 |
cohort #02 0 | 3 | 31 | 11 | 5 | 3 | 0 |
cohort #03 0 | 0 | 4 | 27 | 7 | 6 | 2 |
cohort #04 0 | 1 | 1 | 4 | 29 | 4 | 3 |
#user-id, timestamp, event_name
events_view (uid varchar(64), tm int(11), e varchar(64))
user_id | 1st timestamp | cohort_no
uid1 1423836540 0
uid2 1423839540 0
uid3 1423841160 1
uid4 1423841460 2
...
uidN 1423843080 M
user_id | 1st timestamp | cohort_no | user_id | other fields...
uid1 1423836540 0 uid1
uid2 1423839540 0 null
uid3 1423841160 1 null
uid4 1423841460 2 uid4
...
uidN 1423843080 M null
通过这种方式,我可以看到我的团队中有多少用户发送了level_end:001:win,exclude not found by where子句:where t2.uid not null。
最后,我进行分组,并统计每个队列中的用户数,这些用户在这个特定的时间段内发送了level_end:001:win。
代码如下:
DROP TABLE IF EXISTS tmp_res;
create temporary table tmp_res (uid varchar(64) CHARACTER SET cp1251 NOT NULL, c int(11), cnt int(11) );
set i = 0;
set cnt_c = ceil((t_end - beg) / period) ;
WHILE i < cnt_c DO
insert into tmp_res
select concat(beg + period * i, "_", beg + period * (i+1)), c, count(distinct(uid)) from
(select t1.uid, t1.c from tmp_c t1 left join
(select uid, min(tm) from events_view where
locate("level_end:001:win", e) > 0 and
tm > (beg + period * i) AND tm <= (beg + period * (i+1)) group by uid ) t2
on t1.uid = t2.uid where t2.uid is not null) t3
group by c;
SET i = i+1;
END WHILE;
/*getting result of the first method: tooo slooooow!*/
select * from tmp_res;
它可以工作,但处理起来需要太多时间,因为这里有很多查询,而不是一个,所以我需要重写它
我认为它可以用连接重写,但我仍然不确定如何重写。
我决定制作一个临时表,并在其中写入期间边界:
DROP TABLE IF EXISTS tmp_times;
create temporary table tmp_times (tm_start int(11), tm_end int(11));
set cnt_c = ceil((t_end - beg) / period) ;
set i = 0;
WHILE i < cnt_c DO
insert into tmp_times values( beg + period * i, beg + period * (i+1));
SET i = i+1;
END WHILE;
然后,我将periods to events映射到user_id+timestamp,表示特定事件到temp表,并将其左键连接到队列表并对结果进行分组:
SELECT Concat(tm_start, "_", tm_end) per,
t1.c coh,
Count(DISTINCT( t2.uid ))
FROM tmp_c t1
LEFT JOIN (SELECT *
FROM tmp_times t3
LEFT JOIN (SELECT uid,
tm
FROM events_view
WHERE Locate("level_end:101:win", e) > 0)
t4
ON ( t4.tm > t3.tm_start
AND t4.tm <= t3.tm_end )
WHERE t4.uid IS NOT NULL
ORDER BY t3.tm_start) t2
ON t1.uid = t2.uid
WHERE t2.uid IS NOT NULL
GROUP BY per,
coh
ORDER BY per,
coh;
在我的测试中,这将返回与方法1相同的结果。我无法手动检查结果,但我了解方法1的工作原理,就我所知,它给出了我想要的结果。方法2的速度更快,但我不确定它是否是最好的方法,即使队列交叉,它也会给出相同的结果
也许有一些众所周知的常用方法可以在SQL中执行队列分析?我使用的方法1比方法2更可靠吗?我并不经常使用连接,这就是为什么我还没有完全理解连接的魔力。
方法2看起来像纯魔法,我过去不相信我不懂的东西:
谢谢你的回答 你们能不能从概述你们的任务开始,而不是直接跳到“我不知道什么”的解决方案上来?@ivan_pozdeev队列分析是一个已知的术语,我认为这个问题是显而易见的。好的,我会编辑。你能提供几行输入数据的示例吗?老实说,我无法解析您对它的描述//用户id、时间戳、事件名称事件视图uid varchar64、tm int11、evarchar64@OllieJones这是一个表结构。添加了示例行。创建表事件\u视图uid varchar64、tm int11、e varchar64将使用此数据类型创建表。Real table有更多的列,但通过events\u view,您会明白这一点。您在问优化问题。不幸的是,如果不知道您的表是什么样子,就无法以一种有用的方式回答这些问题。视图中隐含的数据隐藏也会使RDM难以很好地处理满足您的需求所必需的连接、选择和聚合操作。顺便说一句,如果你寻找MySQL群组分析,你最喜欢的搜索引擎会产生一些好结果。