select id, number, dense_rank()over(order by number desc) as rank
from passing_number
order by number desc, id asc;
注意:题目要求的排名是 1 2 2 3 稠密排名,所以用 dense_rank() 函数。
select number from grade
group by number
having count(number) >= 3;
select p.id, p.name, t.content
from person p
left join task t
on p.id = t.person_id
order by p.id asc;
题目要求person表中所有的id都要有,所以这个时候用 left join。
select id, name, score
from
( select g.id, l.name, g.score, dense_rank() over( partition by language_id order by score desc) as ranking
from grade g
join language l
on g.language_id = l.id
)
where ranking <= 2
order by name,score desc, id;
思路:既然考到了名次,那么首先想到的是用窗口函数中的排名函数。如果取前二名,直接用where判断即可。
注意:
要计算每天邮件异常的概率。条件是正常用户发给正常用户中成功的,所以要关联表,提出掉黑名单里的用户。
select t.date,
round(sum( case when t.type='no_completed' then 1 else 0 end)*1.0 / count(*),3) as p
from email t
where t.send_id in (
select id
from user
where is_blacklist = 0
)
and t.receive_id in (
select id
from user
where is_blacklist = 0
)
group by t.date
order by t.date asc;
解法一:是用where条件来筛出符合条件的用户的。当然也可以用 inner join 来筛,方法如下:
select email.date, round(
sum(case email.type when'completed' then 0 else 1 end)*1.0/count(email.type),3
) as p
from email
join user as u1 on (email.send_id=u1.id and u1.is_blacklist=0)
join user as u2 on (email.receive_id=u2.id and u2.is_blacklist=0)
group by email.date order by email.date;
用排名函数就可以解决。
select t.date
from
(
select user_id, date, row_number() over( partition by user_id order by date desc) as rk
from login
) t
where t.rk = 1
order by t.user_id asc;
相比于上一道题,这道题要求查询出对应的用户名和设备名,其实也简单,用两个 join 连接就可以搞定。
select t.u_n, t.c_n, date as d
from
(select l.user_id, u.name as u_n, c.name as c_n, l.date, row_number() over(partition by l.user_id order by l.date desc) as rk
from login l
join user u
on l.user_id = u.id
join client c
on l.client_id = c.id) t
where rk = 1
order by t.u_n asc;
select round(count(login.user_id) * 1.0/count(a.user_id), 3) as p
from (
select user_id, min(date) as date
from login
group by user_id ) a
left join login
on login.user_id = a.user_id
and login.date = date(a.date, '+1 day')
注:这是一道计算次日留存率的题目,也是面试和笔试考察的重点内容。有一定的难度,需要重点关注一下。
这道题的解法思路还挺巧的,对用户进行分组并按照日期排名,取排名全为 1 的,即是首次登陆的用户,然后再按照日期分组即可。
select a.date,
sum(case when rk = 1 then 1 else 0 end) new
from (
select
user_id,
date,
row_number() over(partition by user_id order by date asc) as rk
from login
) a
group by date;
这道题的解法其实和第8道很像,但是唯一还需要考虑的是没有新增用户的日期。因此需要用到 union 函数。
select a.date,
round(count(login.user_id) * 1.0 / count(a.user_id), 3) as p
from
(
select user_id, min(date) as d
from login
group by user_id ) as a
left join login
on login.user_id = a.user_id
and login.date = date(a.date, '+1 day')
group by a.date # 要看每天的留存,所以这一步不能少
union
select date, 0.000 as p
from login
where date not in ( # 没有 is 直接 not in
select min(date) as date
from login
group by user_id)
order by date;
这道题需要注意是每天累计的通过题目的数量,用窗口函数 + 连接查询可以实现。
select
u.name,
c.name,
pn.date,
sum(pn.number) over( partition by pn.user_id order by pn.date) as ps_num
# 按照用户进行分类,看每一个用户在date下的累计刷题数目。
from passing_number pn
left join user u
on pn.user_id = u.id
left join login
on login.user_id = pn.user_id
and login.date = pn.date
left join client c
on login.client_id = c.id
order by pn.date, u.name;
select *,
concat(round(100*次日留存用户/日新增用户数,2),'%') 次日留存率,
concat(round(100*三日留存用户/日新增用户数,2),'%') 三日留存率,
concat(round(100*七日留存用户/日新增用户数,2),'%') 七日留存率,
concat(round(100*三十日留存用户/日新增用户数,2),'%') 三十日留存率
from
(
select
c.log_day 日期,
count(distinct c.u_id) 日新增用户数,
count(distinct d.u_id) 次日留存用户,
count(distinct e.u_id) 三日留存用户,
count(distinct f.u_id) 七日留存用户,
count(distinct g.u_id) 三十日留存用户
from
(
-- 确保是新增用户
select a.*
from user_login a
left join user_login b on a.u_id = b.u_id and b.log_day < a.log_day
where b.log_day is null
) c
left join user_login d on c.u_id = d.u_id and DATEDIFF(d.log_day,c.log_day) = 1
left join user_login e on c.u_id = e.u_id and DATEDIFF(e.log_day,c.log_day) = 3
left join user_login f on c.u_id = f.u_id and DATEDIFF(f.log_day,c.log_day) = 7
left join user_login g on c.u_id = g.u_id and DATEDIFF(g.log_day,c.log_day) = 30
group by c.log_day
) p;