忍痛开了一个月会员,就是想刷一下leetcode的sql会员题,在这里也把刷题过程都记录下来,供大家一起学习,以下所有题目的答案以及思路都是本人亲自写的,有不对的地方欢迎大家评论讨论~
目录
511. 游戏玩法分析I
512. 游戏玩法分析II
534. 游戏玩法分析III
550. 游戏玩法分析IV
569. 员工薪水中位数
570. 至少有5名直接下属的经理
571. 给定数字的频率查询中位数
574. 当选者I
577. 员工奖金
578. 查询回答效率最高的问题
579. 查询员工的累计薪水
580. 统计各专业学生人数
585. 2016年的投资
597. 好友申请I: 总体通过率
602. 好友申请II: 谁有最多的好友
603. 连续空余座位
610. 判断三角形I
511. 游戏玩法分析I
- 源代码:
select player_id, min(event_date) as first_login from activity group by player_id;
- 思路:题目要求每一个玩家对应的…,很明显按照player_id分组,想到这里应该就不难求出每位玩家的最早登陆日期
512. 游戏玩法分析II
- 源代码:
select player_id, device_id from activity where (player_id, event_date) in ( select player_id, min(event_date) from activity group by player_id );
- 思路:首先对需求拆解,第一步求出首次登录的用户,第二步,求出设备名称,有两个步骤所以可以考虑使用子查询的方式
534. 游戏玩法分析III
- 源代码:
select player_id, event_date, sum(games_played) over(partition by player_id order by event_date) as games_played_so_far from activity;
- 思路:很明显,题目是一个求累计的过程,所以想到了开窗函数
550. 游戏玩法分析IV
- 源代码:
select round(count(*) / (select count(distinct player_id) from activity), 2) from ( select player_id, date_sub(event_date, row_number() over(partition by player_id order event_date)) as diff from activity )tmp group by player_id, diff having count(*) >= 2;
- 思路:用户连续登陆问题,这是一个面试常考的SQL题,我们可以将每个用户按照日期进行排名,如果是连续登陆,那么日期减去排名得到的日期是相同的,我们只需要判断每个用户相同的有多少个,如果是两个,就是连续登陆两天的用户
569. 员工薪水中位数
- 源代码:
select id, company, salary from ( select id, company, row_number() over(partition by company order by salary) rk, count(*) over(partition by company) as total from employee ) tmp where tmp.rk in (floor((total + 1) / 2), floor((total + 2) / 2));
- 思路:可以按照分组进行排名,然后如果当前分组数据行数为偶数条,就返回中间的两个排名的数据,否则,返回最中间的排名的数据
570. 至少有5名直接下属的经理
- 源代码:
select name from employee where id in ( select managerId from ( select managerId, count(*) cnt from employee group by managerId )t where cnt >= 5 );
- 思路:首先拆分需求,第一步,求出至少有5个直接下属的经理编号,第二步,求出对应的名字
571. 给定数字的频率查询中位数
- 源代码:
select avg(num) as median from ( select num, sum(frequency) over() total, sum(frequency) over(order by num) sum_fre, sum(frequency) over(order by num desc) as sum_fre_desc from numbers ) t where sum_fre >= total / 2 and sum_fre_desc >= total / 2;
- 思路:这里就是用到了根据频数求中位数的技巧,我们将频数按照数字升序累加和逆序累加,然后取出升序和逆序频数累计都大于等于 总共累计的一半
574. 当选者
- 源代码:
with tmp as ( select candidateid, count(id) cnt from vote group by candidateId ) select name from tmp join candidate on tmp.candidateId = candidate.id where cnt = ( select max(cnt) from tmp )
- 思路:这种很显然需要求出每个候选人被投票的数量,建立一张临时表,以便后面多次使用到
577. 员工奖金
- 源代码:
select name, bonus from employee left join bonus on employee.empId = bonus.empId where bonus.bonus < 1000 or bonus.bonus is null;
- 思路:我们应该了解 有些员工是没有奖金的,所以这一部分员工也符合题目的要求,需要输出
578. 查询回答率最高的问题
- 源代码:
with tmp as ( select question_id, count(case action when "answer" then 1 end) / count(case action when "show" then 1 end) as rate from surveylog group by question_id ) select question_id as survey_log from tmp where rate = ( select max(rate) from tmp ) order by question_id limit 1;
- 思路:先求出每个问题的回答率,然后再过滤出最大的回答率的问题
579. 查询员工的累计薪水
- 源代码:
select id, month, salary from ( select id, month, sum(salary) over(partition by id order by month range 2 preceding) salary, row_number() over(partition by id order by month desc) r from employee )t where t.r > 1 order by id, month desc;
- 思路:本题的核心点其实不是去掉最后一个月,而是如何累计最近的3个月,
range
的用法我也是第一次使用,以前可能都是使用rows
,这两者的区别是 range根据order by的字段进行累计,而rows根据行来进行累计
580. 统计各专业学生人数
- 源代码:
select dept_name, count(student_id) student_number from student stu right join department dep on stu.dept_id = dep.dept_id group by dept_name order by student_number desc, dept_name;
- 思路:本题的边界条件过于细致,真正的面试中给出大致的思路就可以了
585. 2016年的投资
- 源代码:
select round(sum(tiv_2016), 2) TIV_2016 from insurance join ( select LAT, LON from insurance group by LAT, LON having count(*) <= 1 )t1 on insurance.LAT = t1.LAT and insurance.LON = t1.LON join ( select tiv_2015 from insurance group by tiv_2015 having count(tiv_2015) >= 2 )t2 on insurance.tiv_2015 = t2.tiv_2015
- 思路:分别对两个条件进行取数,然后和源表进行join
597. 好友申请 I:总体通过率
- 源代码:
select round(ifnull( (select count(distinct requester_id, accepter_id) from requestaccepted) / (select count(distinct sender_id, send_to_id) from FriendRequest), 0), 2 ) as accept_rate
- 思路:理解需求,就是用通过的人数除以总人数,也就是要求通过的人数和总人数,总人数很清楚,就是friendrequest表,由于题目要求通过的人数只用统计requestaccepted表就可以了。
602. 好友申请 II :谁有最多的好友
- 源代码:
with tmp as ( select id, sum(num) total from ( select requester_id id, count(requester_id) num from requestaccepted group by requester_id union all select accepter_id id, count(accepter_id) num from requestaccepted group by accepter_id )t group by id ) select id, total num from tmp where tmp.total = ( select max(total) from tmp );
- 思路:我们不仅需要按照发送好友的人进行聚合,而且还需要按照接收好友的人进行聚合,最后得到的都是一样的字段
id, num
,因此这个时候我就想到了union all
可以将两个中间结果进行累加
603. 连续空余座位
- 源代码:
with tmp as ( select seat_id, (seat_id - row_number() over(partition by free order by seat_id)) as diff from cinema where free = 1 ) select seat_id from tmp where diff in ( select diff from tmp group by diff having count(*) >= 2 )
- 思路:连续问题,开窗就可以了
610. 判断三角形
- 源代码:
select x, y, z, if((x + y > z) and (x + z > y) and (y + z > x), 'Yes', 'No') triangle from triangle
- 思路:两边之和大于第三边