You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
createtableaccounts (id int, name varchar(10));
insert into accounts (id, name) values
(1, 'Winston'),
(7, 'Jonathan');
createtablelogins (id int, login_date date);
insert into logins (id, login_date) values
(7, '2020-05-30'),
(1, '2020-05-30'),
(7, '2020-05-31'),
(7, '2020-06-01'),
(7, '2020-06-02'),
(7, '2020-06-02'),
(7, '2020-06-03'),
(1, '2020-06-07'),
(7, '2020-06-10');
SQL:方法一
select
distinct id, name
from (
select
id,
login_date,
datediff(lead(login_date, 4) over(partition by id order by login_date), login_date) diff
from logins group by id, login_date
) temp left join accounts using(id) where diff =4
解析
去重,因为一名用户可能会在一天当中登录多次
select id, login_date from logins group by id, login_date
使用 lead 窗口函数,向下 4 行
lead(login_date, 4) over(partition by id order by login_date)
-- 结果
id | login_date | lead_date
1 | 2020-05-30 | null1 | 2020-06-07 | null7 | 2020-05-30 | 2020-06-037 | 2020-05-31 | 2020-06-107 | 2020-06-01 | null7 | 2020-06-02 | null7 ] 2020-06-03 | null7 | 2020-06-10 | null
使用 datediff 计算 login_date 和 lead_date 之间的时间差
将上面的结果作为临时表 temp
将临时表 temp 和 accounts 左连,筛选出第三步中的时间差为 4 的用户
SQL:方法二
select
distinct id, name
from accounts join (
select
id,
@rn:=if(@pre_id=id and @pre_login_date = subdate(login_date, interval 1 day), @rn +1, 1) rn,
@pre_id:=id,
@pre_login_date:=login_date
from (
select id, login_date from logins group by id, login_date
) t1, (select @pre_id:=null, @pre_login_date:=null, @rn:=0) init
) t2 using(id) wheret2.rn>=5order byaccounts.id;
select
distinct id, name
from (
select
id,
subdate(login_date, rn) first_login_date
from (
select
id,
login_date,
row_number() over(partition by id order by login_date) rn
from (
select
id,
login_date
from logins group by id, login_date order by id, login_date
) t1
) t2 group by id, first_login_date havingcount(*) >=5
) t3 left join accounts using(id);
select
id,
login_date,
row_number() over(partition by id order by login_date) rn
from (
select id, login_date from logins group by id, login_date
) t1
使用 subdate 计算每个日期的第一次登录的日期
计算一段连续的数:用一个数(日期)减去它的排名,会得到一个相同的数
select
id,
subdate(login_date, rn) first_login_date
from (
select
id,
login_date,
row_number() over(partition by id order by login_date) rn
from (
select id, login_date from logins group by id, login_date
) t1
) t2
题目
题目连接:活跃用户
写一个 SQL 查询, 找到活跃用户的
id
和name
。活跃用户是指那些至少连续
5
天登录账户的用户。SQL:方法一
解析
去重,因为一名用户可能会在一天当中登录多次
使用
lead
窗口函数,向下4
行使用
datediff
计算login_date
和lead_date
之间的时间差将上面的结果作为临时表
temp
将临时表
temp
和accounts
左连,筛选出第三步中的时间差为4
的用户SQL:方法二
解析
使用变量对将每个用户的登录日期(去重)按照先后顺序进行排名,
id
和 上一条数据的id
是否相等subdate
比较当前数据的login_date
和上一条数据的login_date
是否相等各变量初始化
@pre_id
保存上一条数据的id
, 初始化为null
@pre_login_date
保存上一条数据的login_date
, 初始化为null
@rn
保存媒体数据的排名,初始化为0
使用
if
进行判断@pre_id
(上一条数据的id
) 等于当前这条数据的id
,并且@pre_login_date
(上一条数据的login_date
)等于当前这条数据的login_date
,输出@rn + 1
,否则输出1
@pre_id = null
、@pre_login_date = null
@pre_id = id
是null = 7
为false
@pre_login_date = subdate(login_date, interval 1 day)
是null = '2020-05-29'
为false
1
@pre_id
赋值为当前数据的id
,结果是@pre_id:=7
;@pre_login_date
赋值为当前数据的login_date
,结果是@pre_login_date:= '2020-05-30'
@pre_id = 7
、@pre_login_date = '2020-05-30'
@pre_id = id
是7 = 7
为true
@pre_login_date = subdate(login_date, interval 1 day)
是'2020-05-30' = '2020-05-30'
为true
@rn + 1
为2
@pre_id
赋值为当前数据的id
,结果是@pre_id:=7
;@pre_login_date
赋值为当前数据的login_date
,结果是@pre_login_date:= '2020-05-31'
将步骤一和步骤二的结果作为临时表
t2
查询临时表
t2
筛选出rn
大于5
的数据。SQL:方法三
解析
使用窗口函数对将每个用户的登录日期(去重)按照先后顺序进行排名,再用
subdate
计算出第一天登录日期使用
row_number()
,将每个用户按照登录日期进行排序,因为一个用户可以一天登录多次,所以所以首先要对logins.login_date
进行去重使用
subdate
计算每个日期的第一次登录的日期按照
id
和first_login_date
进行分组,筛选出大于5
天的用户连接
accounts
,再对accounts.id
和accounts.name
进行去重5
天的区间SQL:方法四
解析
自连接,
l1.login_date
作为登录起始日,l2.login_date
用来和l1.login_date
进行比较,也就是说每个日期都检查之后是否有4
个连续日期datediff(l2.login_date, l1.login_date)
进行比较,是否在0
到4
天之间datediff('2022-01-02', '2022-01-02') // 0
datediff('2022-01-03', '2022-01-02') // 1
datediff('2022-01-04', '2022-01-02') // 2
datediff('2022-01-05', '2022-01-02') // 3
datediff('2022-01-06', '2022-01-02') // 4
那你可能会想到可不可以用
between 1 and 5
,那我们来看一下1 ~ 5
的结果是啥datediff('2022-01-03', '2022-01-02') // 1
datediff('2022-01-04', '2022-01-02') // 2
datediff('2022-01-05', '2022-01-02') // 3
datediff('2022-01-06', '2022-01-02') // 4
datediff('2022-01-07', '2022-01-02') // 5
l2.login_date
是l1.login_date
开始日后(包括开始日)连续登录日期,如果l2.login_date
不包括开始日,后面处理就麻烦了。将
accounts
和l1
、l2
连接,并且按照accounts.id
、accounts.name
、l1.login_date
进行分组。l1.login_date
,因为第一步中计算l1.login_date
后是否有4
个连续日期使用
having
筛选出l2.login_date
等于5
数据(需要使用distinct
去重)l2.login_date
不去重的话,不符合题目要求The text was updated successfully, but these errors were encountered: