这将假设我们的测试页面 id = 9 ... 被查看,计入徽章(用户连续 7 天查看该页面)。该事实已从操作评论(7 天和徽章)中删除。至于第 9 页,我们只是在这个答案中做了这个。
因此,如果用户连续 7 天查看该页面,我们希望该用户出现在输出中。注意,页码是 9。
架构和数据加载
create schema db40076704;
use db40076704;
create table pageViews
( id int auto_increment primary key,
userId int not null,
viewDT datetime not null,
pageId int not null
-- include decent index choices here
-- include Foreign Key constraints here
);
truncate pageViews;
insert pageViews (userId,viewDT,pageId) values
(101,'2016-09-05 21:00:00',9),
(101,'2016-09-06 11:00:00',9),
(101,'2016-09-06 15:55:00',9),
(101,'2016-09-06 15:57:00',9),
(101,'2016-09-07 21:00:00',9),
(101,'2016-09-08 21:00:00',999999),
(101,'2016-09-09 21:00:00',9),
(101,'2016-09-10 21:00:00',9),
(101,'2016-09-11 21:00:00',9),
(150,'2016-09-01 21:00:00',9),
(150,'2016-09-06 11:00:00',9),
(150,'2016-09-06 15:55:00',9),
(150,'2016-09-06 15:57:00',9),
(150,'2016-09-07 21:00:00',9),
(150,'2016-09-08 10:44:00',9),
(150,'2016-09-09 21:00:00',9),
(150,'2016-09-10 21:00:00',9),
(150,'2016-09-11 23:00:00',9),
(150,'2016-09-12 23:00:00',9),
(200,'2016-09-08 10:44:00',9),
(200,'2016-09-10 21:00:00',9),
(200,'2016-09-12 21:00:00',9),
(200,'2016-09-14 23:00:00',9),
(200,'2016-09-16 23:00:00',9),
(200,'2016-09-18 23:00:00',9),
(200,'2016-09-20 23:00:00',9);
显示调试详细信息的内部查询
select userId,
date(viewDT),
(@rn := if(@curUser = userId
AND (@prevDate=DATE(viewDT) OR @prevDate=DATE_SUB(DATE(viewDT),INTERVAL 1 DAY)), @rn,
if(@curUser := GREATEST(userId,-1), @rn+1, @rn+1)
)
) rn,
@prevDate:=DATE(viewDT) as dummy1
from pageViews
join (select @curUser:=-1,@prevDate:='',@rn:=0) params
where pageId=9
order by userId,viewDt;
+--------+--------------+------+------------+
| userId | date(viewDT) | rn | dummy1 |
+--------+--------------+------+------------+
| 101 | 2016-09-05 | 1 | 2016-09-05 |
| 101 | 2016-09-06 | 1 | 2016-09-06 |
| 101 | 2016-09-06 | 1 | 2016-09-06 |
| 101 | 2016-09-06 | 1 | 2016-09-06 |
| 101 | 2016-09-07 | 1 | 2016-09-07 |
| 101 | 2016-09-09 | 2 | 2016-09-09 |
| 101 | 2016-09-10 | 2 | 2016-09-10 |
| 101 | 2016-09-11 | 2 | 2016-09-11 |
| 150 | 2016-09-01 | 3 | 2016-09-01 |
| 150 | 2016-09-06 | 4 | 2016-09-06 |
| 150 | 2016-09-06 | 4 | 2016-09-06 |
| 150 | 2016-09-06 | 4 | 2016-09-06 |
| 150 | 2016-09-07 | 4 | 2016-09-07 |
| 150 | 2016-09-08 | 4 | 2016-09-08 |
| 150 | 2016-09-09 | 4 | 2016-09-09 |
| 150 | 2016-09-10 | 4 | 2016-09-10 |
| 150 | 2016-09-11 | 4 | 2016-09-11 |
| 150 | 2016-09-12 | 4 | 2016-09-12 |
| 200 | 2016-09-08 | 5 | 2016-09-08 |
| 200 | 2016-09-10 | 6 | 2016-09-10 |
| 200 | 2016-09-12 | 7 | 2016-09-12 |
| 200 | 2016-09-14 | 8 | 2016-09-14 |
| 200 | 2016-09-16 | 9 | 2016-09-16 |
| 200 | 2016-09-18 | 10 | 2016-09-18 |
| 200 | 2016-09-20 | 11 | 2016-09-20 |
+--------+--------------+------+------------+
25 rows in set (0.00 sec)
使用上述方法的最终答案
SELECT userId,rn,count(*) days_In_A_Row
from
( SELECT userId,
DATE(viewDT),
(@rn := if(@curUser = userId
AND (@prevDate=DATE(viewDT) OR @prevDate=DATE_SUB(DATE(viewDT),INTERVAL 1 DAY)), @rn,
if(@curUser := GREATEST(userId,-1), @rn+1, @rn+1)
)
) rn,
@prevDate:=DATE(viewDT) as dummy1
FROM pageViews
JOIN (SELECT @curUser:=-1,@prevDate:='',@rn:=0) params
WHERE pageId=9
ORDER BY userId,viewDt
) xDerived
GROUP BY userId,rn
HAVING days_In_A_Row>6;
+--------+------+---------------+
| userId | rn | days_In_A_Row |
+--------+------+---------------+
| 150 | 4 | 9 |
+--------+------+---------------+
因此,用户 150 至少连续 7 天(实际上连续 9 天)查看了第 9 页。该用户在您的系统中获得徽章。
关于 mysql 变量(@变量)的一些信息。为了安全地使用变量,必须注意不要假设 select 的任何输出列会在另一个输出列之前触发。这是在名为User-Defined Variables的手册页中明确说明的事实:
在下面的语句中,您可能认为 MySQL 会先评估 @a,然后再进行赋值:
SELECT @a, @a:=@a+1, ...;
但是,涉及用户变量的表达式的求值顺序是未定义的。
也就是说,我们知道使用 GREATEST()、LEAST() 和 COALESCE() 等函数的优先级会更高。
此外,GREATEST(N,-1) 代码中的废话将始终返回 N。但是我们需要在计算之前强制计算该列的优先级
@prevDate:=DATE(viewDT) as dummy1
线。另请参阅 Baron Schwartz 的必读 高级 MySQL 用户变量技术。