1. 删除emp_no重复的记录,只保留最小的id对应的记录。
按照emp_no分组,找出最小的id。然后delete 掉不在最小id中的数据。
注意不能一边删一边查找,错误方法:
DELETE FROM titles_test
WHERE id NOT IN(SELECT MIN(id)FROM titles_testGROUP BY emp_no);
MySQL中不允许在子查询的同时删除表数据(不能一边查一边把查的表删了)
正确做法:
DELETE FROM titles_test
WHERE id NOT IN(SELECT * FROM(SELECT MIN(id)FROM titles_testGROUP BY emp_no)a); -- 把得出的表重命名那就不是原表了
2.平均工资
方法1:筛选掉最高工资和最低工资,然后用avg函数。(这个比较严谨)
select avg(salary) as avg_salary from salaries
where to_date = '9999-01-01'
and salary not in (select max(salary) from salaries where to_date = '9999-01-01')
and salary not in (select min(salary) from salaries where to_date = '9999-01-01')
方法2:纯用数学公式(不严谨,因为有最大最小值重复的情况)
select (sum(salary)-max(salary)-min(salary))/(count(1)-2) avg_salary
from salaries
where to_date='9999-01-01';
3.分页查询employees表,每5行一页,返回第2页的数据
若每页显示n条记录,要显示第i页数据,则可以用 limit n*(i-1),n
因此,得到如下代码:
select * from employees
order by emp_no
limit 5*(2-1),5;
4.获取有奖金的员工相关信息。
用case when的方式实现
select e.emp_no,first_name,last_name,btype,salary,
(case when eb.btype=1 then s.salary*0.1when eb.btype=2 then s.salary*0.2else s.salary*0.3 end) bonus
from
employees e
left join emp_bonus eb
on e.emp_no=eb.emp_no
left join salaries s
on eb.emp_no=s.emp_no
where to_date="9999-01-01"
order by e.emp_no;
5.统计salary的累计和running_total (开窗)
按照排序号的emp_no开窗,然后求和即可。注意sum和over后面的括号不需要空格。另外开窗函数over后面可以单独使用partition by 或者order by等等。
select emp_no,salary,
sum(salary) over(order by emp_no) running_total
from salaries
where to_date = '9999-01-01'
6.给出employees表中排名为奇数行的first_name (开窗)
按照first_name开窗得到rownum,然后和原来的表关联后取技术行。
select e.first_name
from employees e
left join
(select first_name,
row_number() over(order by first_name) rownum
from employees) t
on e.first_name=t.first_name
where t.rownum % 2!=0
7. 牛客每个人最近的登录日期(二)
查询每个用户最近一天登录的日子,用户的名字,以及用户用的设备的名字。(在聚合时无法同时查找用户的名字和设备的名字,所以不能用group b’y和max组合查找)
方式一:几张表连接好,然后直接用in语句来筛选出嘴的日期。
select u.name u_n,c.name c_n,l.date
from login l
join user u
on u.id=l.user_id
join client c
on c.id=l.client_id
where (l.user_id,l.date)in (select user_id,max(date) maxdate from login group by user_id)
order by u.name方式二:使用开窗找到每个用户的最近登录日期,然后用where筛选出只有最近登录日期的数据。另外这里是直接通过where条件来连接几张表,效率比join on低,但是看起来比较简单。这种方式不普遍,看看就行。
select user.name, client.name, d date
from user,
client,
(select *, max(date) over(partition by user_id) d from login) t
where user.id = t.user_id
and client.id = t.client_id
and t.date = t.d
order by user.name
8.牛客每个人最近的登录日期(三)
计算新登录用户次日成功的留存率,这题不方便开窗
思路:用户和首次登录日期在下一次登录日期中也存在即可。
select
round(count(distinct user_id)*1.0/(select count(distinct user_id) from login) ,3)
from login
where (user_id,date)
in (select user_id,DATE_ADD(min(date),INTERVAL 1 DAY) from login group by user_id);
9.牛客每个人最近的登录日期(四)
查询每个日期登录新用户个数。
思路:开窗后使用case when。
select date,sum(case when rn=1 then 1 else 0 end ) s
from (select user_id,date,dense_rank() over(partition by user_id order by date) rn
from login) a
group by date
10.牛客每个人最近的登录日期(六)
截止到某天,累计总共通过了多少题
思路:左连接,开窗(开窗时注意按照日期排序)
select u.name,pn.date,
# pn.number,
sum(pn.number) over(partition by u.name order by date)
from passing_number pn
left join user u
on pn.user_id=u.id
order by pn.date
11.考试分数(一)
查询各个岗位分数的平均数,并且按照分数降序排序,结果保留小数点后面3位(3位之后四舍五入):
思路:单纯分组聚合即可。
select job,round(avg(score) ,3) avgscore
from grade
group by job
order by avgscore desc
12.考试分数(二)
查询用户分数大于其所在工作(job)分数的平均分的所有属性
方法一:开窗函数
select a.id,a.job,a.score from (
select id,job,score,avg(score) over(partition by job) ac
from grade
) a
where a.score > a.ac方法二:按组求出平均分,判断分数大于平均分的数据。用where子查询,子查询中使用where连接两张表
SELECT g.id,g.job,g.score
FROM grade g
WHERE g.score > (SELECT AVG(score)FROM grade g1WHERE g.job = g1.jobGROUP BY job)
ORDER BY id ASC;方法三:按组求出平均分,并且加上job形成一张表。然后关联原表,用where判断大于平均分的数据
select g.id,g.job,g.score from
grade g
left join
(
select job,round(avg(score) ,3) avgscore
from grade
group by job
) a
on g.job=a.job
where g.score>a.avgscore
13.考试分数(三)
找出每个岗位分数排名前2名的用户
思路:开窗后找出排名前2的language_id 。
select a.id,l.name,a.score from
(select id,language_id,score,dense_rank() over(partition by language_id order by score desc) rn
from grade ) a
join language l
on a.language_id=l.id
where a.rn<=2
order by l.name,a.score desc,a.id
14. 牛客的课程订单分析(二)
查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的user_id(只需要user_id单列)。
思路:在要求的输出内容为单一可以聚合的字段的情况下只需要用简单的group,having就能筛选出内容。涉及到多个字段不能聚合的情况很多时候就是要用到开窗的,看15题牛客的课程订单分析(三)。
#正常方式
select user_id
from order_info
where date > "2025-10-15"
and status = "completed"
and product_name in ("C++","Java","Python")
group by user_id
having count(status)>=2
order by user_id;#要想直接用开窗也可以,但在这个单一聚合字段的情况就显得多余了,还要嵌套一层。
select distinct(user_id)
from
(
select *,count(*) over(partition by user_id) as c
from order_info
where date > '2025-10-15'
and status ="completed"
and product_name in ("C++","Java","Python")
) a
where a.c>=2
order by user_id;
15.牛客的课程订单分析(三)
查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的订单信息(订单的所有信息,多列),所以用开窗比较合适。
select id,user_id,product_name,status,client_id,date from
(
select *,count(status) over(partition by user_id ) c
from order_info
where date>'2025-10-15'
and status = 'completed'
and product_name in("C++","Java","Python")
) a
where a.c>=2
order by id