牛客网MySQL答案整理

牛客网MySQL答案整理

入门题

(1) SQL1 寻找最晚入职员工的所有信息

select * from employees where hire_date=(select distinct hire_date from employees
order by hire_date desc limit 1 offset 2);

简单题

(1) SQL2 查找employees里入职员工时间排名倒数第三的员工所有信息

# 注意执行顺序是先select然后再order by
select * from employees where hire_date=(select distinct hire_date from employees
order by hire_date desc limit 1 offset 2);

(2) SQL4 查找所有已经分配部门的员工的last_name和first_name以及dept_no

select e.last_name,e.first_name,d.dept_no from 
employees e inner join dept_emp d on 
e.emp_no=d.emp_no;

(3) SQL7 查找薪水记录超过15次的员工号emp_no以及其对应的记录次数t

select emp_no,count(salary) t from salaries 
group by emp_no 
having count(salary)>15;

(4) SQL8 找出所有员工具体的薪水salary情况

select distinct salary from salaries order by salary desc;

(5) SQL10 找出所有非部门领导的员工emp_no

-- 方法1
select e.emp_no from 
employees e left join dept_manager d on 
e.emp_no=d.emp_no 
where d.emp_no is null;
-- 方法2
select e.emp_no from employees e 
where e.emp_no not in 
(select distinct emp_no from dept_manager);

(6) SQL15 查找employees表所有emp_no为奇数,且last_name不为Mary的员工信息

select * from employees where emp_no&1 and last_name != 'Mary'
order by hire_date desc;

(7) SQL 17 获取薪水第二多的员工的emp_no以及其对应的薪水salary

-- 方法1
select emp_no,max(salary) from salaries where 
salary<(select max(salary) from salaries);
-- 方法2
select emp_no,salary from salaries group by salary 
order by salary desc limit 1 offset 1;

(8)SQL32 将employees表的所有员工的last_name和first_name拼接起来作为Name

select concat(last_name,' ',first_name) Name from employees;
(9) 批量插入数据

```sql
insert into actor(actor_id,first_name,last_name,last_update) 
values(1,'PENELOPE','GUINESS','2006-02-15 12:34:33'),
(2,'NICK','WAHLBERG','2006-02-15 12:34:33');

(10) SQL42 删除emp_no重复的记录,只保留最小的id对应的记录

  • 注意不能直接对同一张表进行删除和取数,需要将取数的结果视为另外一张表t
delete from titles_test where id not in 
(select * from (select min(id) from titles_test group by ) as t)

(11) SQL43 将所有to_date为9999-01-01的全部更新为NULL,且 from_date更新为2001-01-01

update titles_test set to_date = NULL, from_date = "2001-01-01"
where id in
(
    select * from (
        select id
        from titles_test
        where to_date = "9999-01-01"
        ) as t
);

-- 注意,此处最好不涉及多表查询,注意中间是','(不要用and)
update titles_test set to_date = NULL,from_date = "2001-01-01"
where to_date = "9999-01-01";

(12) SQL44 将id=5以及emp_no=10001的行数据替换成id=5以及emp_no=10005,其他数据保持不变,使用replace实现,直接使用update会报错
  MySQL中replace into 有三种形式

  1. replace into tbl_name(col_name, …) values(…)
  2. replace into tbl_name(col_name, …) select …
  3. replace into tbl_name set col_name=value, …
    其中前两种对应于insert,后面一种对应update(不过update是update table set col_name=’…’)
-- (1) 
REPLACE INTO titles_test VALUES (5, 10005, 'Senior Engineer', '1986-06-26', '9999-01-01');
-- (2)
EPLACE INTO titles_test VALUES (5, 10005, 'Senior Engineer', '1986-06-26', '9999-01-01');

REPLACE INTO titles_test
SELECT 5,10005,title,from_date,to_date FROM titles_test
WHERE id=5 AND emp_no=10001;
--(3)
update titles_test set emp_no=replace(emp_no,'10001','10005');

(13) SQL45 将titles_test表名修改为titles_2017

alter table titles_test rename to titles_2017;

(14) SQL62 出现3次以上积分相同的情况

select number from grade group by number having count(id)>=3

(15) SQL64 找到每个人的任务

select p.*,t.content from 
person p left join task t on 
p.id=t.person_id;

(16) SQL66 牛客每个人最近的登陆日期(一)

select user_id,max(date) from login group by user_id 
order by user_id;

(17) SQL72 考试分数(一)

select job,round(avg(score),3) avg from grade group by job 
order by avg desc;

(18) SQL84 实习广场投递简历

-- count()是对行数进行求和,而sum()则是普通的求和函数
select job,sum(num) as cnt from resume_info
where date<='2025-12-31' and date>='2025-01-01'
group by job
order by cnt desc;

select job,sum(num) as cnt from resume_info
where date like '2025%'
group by job
order by cnt desc;

select job,sum(num) cnt
from resume_info
where date between '2025-01-01' and '2025-12-31'
group by job
order by cnt desc;

中等题

(1) SQL3 查找各个部门当前领导的薪水详情以及其对应部门编号dept_no

select s.*,dm.dept_no from salaries s 
inner join dept_manager dm on 
s.emp_no=dm.emp_no
order by s.emp_no asc;

(2) SQL5 查找所有已经分配部门的员工的last_name和first_name以及dept_no,也包括暂时没有分配具体部门的员工

select e.last_name,e.first_name,de.dept_no from employees e 
left join dept_emp de on 
e.emp_no=de.emp_no;

(3) SQL11 获取所有的员工和员工对应的经理,如果员工本身是经理的话则不显示

select de.emp_no,dm.emp_no from dept_emp de 
inner join dept_manager dm on 
de.dept_no=dm.dept_no 
where de.emp_no not in 
(select distinct emp_no from dept_manager);

(4) SQL 16 统计出各个title类型对应的员工薪水对应的平均工资avg

select t.title,avg(s.salary) from titles t 
inner join salaries s on 
t.emp_no=s.emp_no 
group by title 
order by avg(s.salary);

(5) SQL19 查找所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工

select e.last_name,e.first_name,d.dept_name from 
employees e left join dept_emp de on 
e.emp_no=de.emp_no 
left join departments d on 
de.dept_no=d.dept_no;

(6) SQL 22 统计各个部门的工资记录数

select d.dept_no,d.dept_name,count(salary) from 
departments d inner join dept_emp de on 
d.dept_no=de.dept_no 
inner join salaries s on 
de.emp_no=s.emp_no
group by d.dept_no
order by d.dept_no asc;

(7) SQL 29 使用join查询方式找出没有分类的电影id以及名称

select f.film_id,f.title from film f 
left join film_category fc on 
f.film_id=fc.film_id 
where fc.film_id is null;

select f.film_id,f.title from film f 
left join film_category fc on 
f.film_id=fc.film_id 
where f.film_id not in
(select distinct film_id from film_category);

(8) SQL 30 使用子查询的方式找出属于Action分类的所有电影对应的title,description

-- 非子查询方式
select f.title,f.description from 
film f left join film_category fc on f.film_id=fc.film_id 
left join category c on fc.category_id=c.category_id
where c.name = 'Action';
-- 子查询方式
select f.title,f.description from film f 
where f.film_id in 
(select fc.film_id from film_category fc inner join category c on
fc.category_id=c.category_id where c.name='Action'
);

(9) SQL 33 创建一个actor表

create table if not exists actor(
actor_id smallint(5) not null primary key,
first_name varchar(45) not null,
last_name varchar(45) not null,
last_update date not null);

(10) 批量插入数据,不使用replace

# 注意忽略其中已经存在的数据
insert ignore into actor values('3','ED','CHASE','2006-02-15 12:34:33');

(11) SQL 36 创建一个actor_name表

-- 方法1:先建表再插入
create table if not exists actor_name(
first_name varchar(45) not null,
last_name varchar(45) not null);
insert into actor_name 
select first_name,last_name from actor;
-- 方法2:在建表的同时进行插入
create table actor_name
select first_name,last_name from actor;

(12) SQL 37 对first_name创建唯一索引uniq_idx_firstname,对last_name创建普通索引idx_lastname

create unique index uniq_idx_firstname on actor(first_name);
create index idx_lastname on actor(last_name);

(13) SQL 38 针对actor表创建视图actor_name_view,只包含first_name以及last_name两列,并对这两列重新命名,first_name为first_name_v,last_name修改为last_name_v

create view actor_name_view as 
select first_name first_name_v,last_name last_name_v from actor;

(14) SQL 39 针对salaries表emp_no字段创建索引idx_emp_no,查询emp_no为10005,使用强制索引

# 注意其中的()不能少
select * from salaries 
force index (idx_emp_no)
where emp_no=10005;

(15) SQL 40 在last_update后面新增加一列名字为create_date, 类型为datetime, NOT NULL,默认值为’2020-10-01 00:00:00’

alter table actor add create_date 
datetime not null default '2020-10-01 00:00:00';

(16) SQL 41 构建一个触发器

create trigger audit_log 
after insert on employees_test 
for each row
begin
    insert into audit values(new.id,new.name);
end

(17) SQL 46 在audit表上创建外键约束,其emp_no对应employees_test表的主键id

alter table audit add constraint c1 foreign key(emp_no)
references employees_test(ID);

alter table audit add foreign key(emp_no)
references employees_test(ID);

(18) SQl 48 将所有获取奖金的员工当前的(salaries.to_date=‘9999-01-01’)薪水增加10%

update salaries set salary=1.1*salary 
where emp_no in 
(select emp_no from emp_bonus 
where to_date='9999-01-01');

(19) SQL 50 将employees表中的所有员工的last_name和first_name通过(’)连接起来

select concat(last_name,"'",first_name) from employees;

(20) SQL 51 查找字符串’10,A,B’ 中逗号’,'出现的次数cnt

select length('10,A,B')-length(replace('10,A,B',',','')) as cnt;
select char_length('10,A,B')-char_length(replace('10,A,B',',','')) as cnt;

(21) SQL 52 获取Employees中的first_name,查询按照first_name最后两个字母,按照升序进行排列

# -2代表从倒数第2个开始,2代表截取两个字符
select first_name from employees
order by substr(first_name,-2,2) asc;

(22) SQL 53 按照dept_no进行汇总,属于同一个部门的emp_no按照逗号进行连接,结果给出dept_no以及连接出的结果employees

select dept_no,group_concat(emp_no) employees from 
dept_emp group by dept_no;

(23) SQL 53 按照dept_no进行汇总,属于同一个部门的emp_no按照逗号进行连接,结果给出dept_no以及连接出的结果employees

select dept_no,group_concat(emp_no) employees from dept_emp group by dept_no;

(24) SQL 54 查找排除最大、最小salary之后的当前(to_date = ‘9999-01-01’ )员工的平均工资avg_salary

select avg(salary) avg_salary from salaries
where salary not in 
(select min(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 to_date='9999-01-01';

select sum(salary)/count(*) from salaries 
where salary != (select min(salary) from salaries)
and salary != (select max(salary) from salaries)
and to_date='9999-01-01';

select (sum(salary)-min(salary)-max(salary))/(count(emp_no)-2) from salaries
where to_date='9999-01-01';

(25) SQL 55 分页查询employees表,每5行一页,返回第2页的数据

select * from employees limit 5 offset 5;
# order by emp_no;

-- 先写order by,然后进行limit 
select * from employees 
limit 5 offset 5;

(26) SQL 56 使用含有关键字exists查找未分配具体部门的员工的所有信息
  使用exists关键字进行查询的时候,首先,我们先查询的不是子查询的内容,而是查我们的主查询的表,也就是说,我们先执行的sql语句是:select * from students. 然后,根据表的每一条记录,执行以下语句,依次去判断where后面的条件是否成立(这与关联子查询的思路是一致的)。如果成立则返回true不成立则返回false。如果返回的是true的话,则该行结果保留,如果返回的是false的话,则删除该行,最后将得到的结果返回

select * from employees where not exists
# 首先得到父查询中的emp_no
# 然后带入子查询判断emp_no是否存在于dept_emp中——也就是是否已经分配好了部门
(select emp_no from dept_emp where employees.emp_no=dept_emp.emp_no);

-- 等价查询
select * from employees where emp_no not in 
(select emp_no from dept_emp);

(27)SQL 63 刷题通过的题目排名

select id,number,dense_rank() over(order by number desc)
as t_rank from passing_number
order by number desc,id asc;

(28) SQL 73 考试分数(二)

-- 用两个select实现
select * from grade
where score > 
      (select avg(score) from grade g1 where g1.job = grade.job)
order by id;

-- 将job及对应的平均数建立一个表,然后与原表进行连接
select g.id, g.job,score 
from grade g join (select job, avg(score) avg1 from grade group by job)s
on g.job = s.job
where score > avg1
order by id;

select * from grade 
where score>(select avg(score) from grade g1 where g1.job=grade.job)
order by id;

select * from grade g1 
inner join (select job,avg(score) avg_score from grade g2 group by job) t
where g1.job=t.job 
and g1.score>t.avg_score
order by id;

(29) SQL 78 牛客的课程订单分析(二) *
  请你写出一个sql语句查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的user_id,并且按照user_id升序排序

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(*)>1
order by user_id asc;

(30) SQL 79 牛客的课程订单分析(三) *
  写出一个sql语句查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的订单信息,并且按照order_info的id升序排序

# 注意在使用group by之后使用select * 只能得到部分数据
select * from order_info
where user_id in 
(select user_id from order_info o1
where product_name in ('C++','Python','Java')
and status='completed'
and date>'2025-10-15'
group by user_id 
having count(user_id)>=2)
and product_name in ('C++','Python','Java')
and status='completed'
and date>'2025-10-15';

(31) SQL 82 牛客的课程订单分析(六) *

# 该题需要注意order_info中部分client_id在client中是不存在的,为了突出id的不存在采用left join而非inner join
select o.id,o.is_group_buy,
(case when o.is_group_buy='Yes' then NULL
else c.name end) client_name
from order_info o left join client c on 
o.client_id=c.id 
where o.user_id in 
(select user_id from order_info
where date>'2025-10-15'
and product_name in ('C++','Java','Python')
and status='completed'
group by user_id
having count(*)>=2)
and date>'2025-10-15'
and product_name in ('C++','Java','Python')
and status='completed';

select o.id,o.is_group_buy,c.name client_name from 
order_info o left join client c on 
o.client_id=c.id where user_id in 
(select user_id from order_info
where status='completed'
and date>'2025-10-15'
and product_name in ('C++','Python','Java')
group by user_id 
having count(user_id)>=2)
and status='completed'
and date>'2025-10-15'
and product_name in ('C++','Python','Java');

(32) SQL 85 实习广场投递简历分析(二) *

select job,date_format(date,'%Y-%m') mon,
sum(num) cnt from resume_info 
where date_format(date,'%Y')='2025'
group by job,mon
order by mon desc,cnt desc;

(33) SQL 87 最差是第几名 *

select grade,sum(number) over(order by grade asc) t_rank from class_grade;

(34) 获得积分最多的人 *
  首先找到积分最多的人以及其对应的id,然后与user表进行连接

select u.name,t.sumnum from user u 
inner join 
(select user_id,sum(grade_num) sumnum from grade_info 
group by user_id
order by sumnum desc
limit 1 offset 0) t
on u.id=t.user_id;

select distinct u.name,sum(grade_num) 
over(partition by g.user_id) grade_sum from 
user u inner join grade_info g on 
u.id=g.user_id
order by grade_sum desc 
limit 1 offset 0;

较难

(1) SQL 18 查找薪水排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,不能使用order by完成

# 从salaries中进行选取,限制salary<max(salary)
select e.emp_no,s.salary,e.last_name,e.first_name from 
employees e inner join salaries s on 
e.emp_no=s.emp_no 
where s.salary=
(select max(salary) from salaries where
salary<(select max(salary) from salaries));
# 从删除最大salary的salaries中进行选择
select e.emp_no,s.salary,e.last_name,e.first_name from 
employees e inner join salaries s on 
e.emp_no=s.emp_no
where s.salary=
(select max(salary) from 
(select salary from salaries where salary< 
(select max(salary) from salaries)) t );

(2) SQL 23 对所有员工的薪水按照salary降序进行1-N的排名,要求相同salary并列且按照emp_no升序排列:

select emp_no,salary,dense_rank() over(order by salary desc) t_rank
from salaries
order by salary desc,emp_no asc;

(3) SQL 24 获取所有非manager员工薪水情况,给出dept_no、emp_no以及salary:

select de.dept_no,de.emp_no,s.salary from 
dept_emp de inner join salaries s on 
de.emp_no=s.emp_no
left join dept_manager dm on 
de.emp_no=dm.emp_no 
where dm.emp_no is null;

select de.dept_no,de.emp_no,s.salary from 
dept_emp de inner join salaries s on 
de.emp_no=s.emp_no
where de.emp_no not in 
(select emp_no from dept_manager);

select de.dept_no,de.emp_no,s.salary from 
dept_emp de inner join salaries s on 
de.emp_no=s.emp_no
where not exists 
(select dm.emp_no from dept_manager dm
where de.emp_no=dm.emp_no);

(4) SQL 59 获取有奖金的员工相关信息

select e.emp_no,e.first_name,e.last_name,eb.btype,s.salary,
(case eb.btype
when 1 then 0.1*s.salary
when 2 then 0.2*s.salary
else 0.3*s.salary
end) bonus 
from employees e inner join emp_bonus eb 
on e.emp_no=eb.emp_no 
inner join salaries s 
on e.emp_no=s.emp_no 
where to_date='9999-01-01';

(5) SQL 60 统计salary的累计和running_total *

select emp_no,salary,
sum(salary) over(order by emp_no) running_total 
from salaries
where to_date = '9999-01-01';

【注】:(1) 当使用窗口函数的时候所选的字段可以自动匹配sum的结果(但是不会进行去重,需要去重的时候可以添加distinct,且需要注意的是最后的结果数目与原始表格的记录数是一样的),以login表为例,我们计算对于user_id的加和(也就是sum),加和的顺序按照client_id进行排序:
在这里插入图片描述select id,client_id,sum(user_id) over(order by client_id) from login;
在这里插入图片描述
可以看出id,user_id和sum()的结果实际上是按照user_id进行同步排序的
  (2) 使用聚合函数的时候则不会有相同的结果,最后得到1|1|10

(6) SQL 61 对于employees表中,输出first_name排名(按first_name升序排序)为奇数的first_name **
  括号中的子查询是查找到first_name<=e1.first_name的人数,如果是奇数就输出此时的e1.first_name

select first_name from employees e1 
where (select count(*) from employees e2
      where e2.first_name<=e1.first_name)%2=1;

select first_name from 
(select emp_no,first_name,row_number() over(order by first_name asc) t_rank 
from employees) t 
where t.t_rank%2=1
order by t.emp_no asc; # row_number()中order by打乱了原来的顺序

# 注意以下写法是错误的:在使用聚合函数的时候要么通过关联子查询进行限制,要么就通过group by来限制分组
select first_name from 
(select first_name,count(*) as cnt from employees
where count(*)%2=1
order by first_name asc) t;

(7) SQL65 异常的邮件概率 **

# select e.date,round(sum(case e.type 
# when "no_completed" then 1 else 0 end)/count(*),3) p
# from email e
# inner join user u1 on e.send_id = u1.id
# inner join user u2 on e.receive_id = u2.id
# where u1.is_blacklist = 0 and u2.is_blacklist = 0
# group by e.date
# order by date;

# select date,round(avg(case when type='no_completed' then 1 else 0 end),3) p
# from email
# where send_id not in(select id from user where is_blacklist=1)
# and receive_id not in(select id from user where is_blacklist=1)
# group by date
# order by date;



# 基本思想:将send_id和receive_id分别限制为正常用户然后计算其中正常用户的比例
select date,round(avg(case type
when 'no_completed' then 1
else 0
end),3) p from email 
where send_id in (select id from user where is_blacklist=0)
and receive_id in (select id from user where is_blacklist=0)
group by date 
order by date asc ;

select date,round(sum(case type
when 'no_completed' then 1
else 0 end)/count(*),3) p
from email e inner join user u1 
on e.send_id=u1.id
inner join user u2 
on e.receive_id=u2.id 
where u1.is_blacklist=0
and u2.is_blacklist=0
group by date
order by date asc;

(8) SQL 67 牛客每个人最近的登陆日期(二) *
   注意,在进行select的时候如果同时select多个字段,需要保证select的条件是一致的:在采用group by的时候只会根据group的对象将数据进行分组来进行筛选,采用聚合函数的时候并不会同步去考虑其他的字段,如下图可以看出选择max(date)的时候并没有同步考虑u_n和c_n:
在这里插入图片描述
如果group by中出现group by则此时聚合的结果和对应的group by是一致的:
在这里插入图片描述
但是该方法仅限于group by一个分组的时候,如下图对c_n,u_n进行group by的结果是3条记录:
在这里插入图片描述

-- 一种错误的写法
select u.name u_n,c.name c_n,max(l.date) date from login l 
inner join user u on 
l.user_id=u.id
inner join client c on 
l.client_id=c.id 
group by u.name,l.date
order by u.name asc;

-- 正确的写法
select u.name u_n, c.name c_n, date
from login l
left join user u on l.user_id = u.id
left join client c on l.client_id = c.id
where (l.user_id, l.date) in (select user_id, max(date) from login group by user_id)
order by u_n;

(9) SQL 68 牛客每个人最近的登陆日期(三) *
牛客每天有很多人登录,请你统计一下牛客新登录用户的次日成功的留存率:

# # 留存率:第二天登录人数/第一天登陆人数
select round(count(distinct user_id)/(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);

(10) SQL 69 牛客每个人最近的登陆日期(四) ***(very important)
  写一个sql语句查询每个日期登录新用户个数,并查询结果按照日期升序排序

# 次日留存率
select a.date,count(b.user_id) from 
(select distinct date from login) a left join 
(select user_id,min(date) first_date from login group by user_id) b 
on a.date=b.first_date 
group by a.date 
order by a.date;

select a.date,sum(case when t_rank=1 then 1 else 0 end) new 
from (select date,row_number() over(partition by user_id 
      order by date)
      t_rank from login) a 
group by date;

(11) SQL 71 牛客每个人最近的登陆日期(六) ***
  牛客每天有很多人登录,请你统计一下牛客每个用户查询刷题信息,包括: 用户的名字,以及截止到某天,累计总共通过了多少题。 不存在没有登录却刷题的情况,但是存在登录了没刷题的情况,不会存在刷题表里面,有提交代码没有通过的情况,但是会记录在刷题表里,只不过通过数目是0

 此处计算累计通过数目

#因为有登录没有刷题的数据不需要输出,所以截止到某天指的是刷题表有记录的日期。没有用到登录表
select name as u_n,date,
sum(number) over(partition by user_id order by date) as ps_num
from passing_number p
inner join user u
on p.user_id=u.id
order by date,u_n;

select u.name u_n,p.date date,
sum(p.number) over(partition by p.user_id order by p.date) ps_num
from passing_number p 
inner join user u 
on p.user_id=u.id 
inner join login l 
# 为什么在进行多表连接的时候不能直接采用以下注释的表达式?
# on p.user_id=l.user_id / p.user_id=l.user_id
# 原因在于如果仅使用用户id,同一个用户可能在不同的日子里登录
# 在进行连接的时候必须确保同一个用户在同一天登录并通过提交
on p.date=l.date and p.user_id=l.user_id
order by p.date,u.name;

【注】:多表连接,感觉结果相同,实际结果不同
在这里插入图片描述
(12) SQL 74 考试分数(三) ****
找出每个岗位分数排名前2名的用户,得到的结果先按照language的name升序排序,再按照积分降序排序,最后按照grade的id升序排序
  关联子查询的执行顺序:https://zhuanlan.zhihu.com/p/41844742

# 采用关联子查询:首先select g1.score放入子查询语句中
# 然后判断g2.score>=g1.score的个数
select g1.id, l.name, g1.score
from grade g1 join language l on g1.language_id=l.id
where
(select count(distinct g2.score) from grade g2
 # 将grade中分数小于前两名的筛选掉
 where g2.score>=g1.score and g1.language_id=g2.language_id
) <=2 order by l.name asc,g1.score desc ,g1.id asc;

SELECT a.id,name,score
FROM (
    SELECT id,language_id,score,
    DENSE_RANK() OVER (PARTITION BY language_id ORDER BY score DESC) AS r
    FROM grade) AS a
INNER JOIN language
ON a.language_id=language.id
WHERE r<=2
ORDER BY name,score DESC,a.id

【注】:Top N问题:
(1) 寻找最小值
在这里插入图片描述
(2) 在分组不多的时候寻找各组TopN
在这里插入图片描述
(3) 在分组较多的时候寻找各组TopN
在这里插入图片描述

(13) SQL75 考试分数(四) *

SELECT job,
    floor(( count(*) + 1 )/ 2 ) AS start,
    floor(( count(*) + 2 )/ 2 ) AS end
FROM grade
GROUP BY job
ORDER BY job

(14) SQL80 牛客的课程订单分析(四)
  写出一个sql语句查询在2025-10-15以后,如果有一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程,那么输出这个用户的user_id,以及满足前面条件的第一次购买成功的C++课程或Java课程或Python课程的日期first_buy_date,以及购买成功的C++课程或Java课程或Python课程的次数cnt,并且输出结果按照user_id升序排序
  该题只能算中等难度…

select user_id,min(date) first_buy_date,count(user_id) cnt from 
order_info where date>'2025-10-15'
and status='completed'
and product_name in ('C++','Java','Python')
group by user_id
having cnt>=2
order by user_id;

(15) SQL 83 牛客的课程订单分析(七) ***
  写出一个sql语句查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的来源信息,第一列是显示的是客户端名字,如果是拼团订单则显示GroupBuy,第二列显示这个客户端(或者是拼团订单)有多少订单,最后结果按照第一列(source)升序排序

select (case when is_group_buy = 'No'
        then c.name
        else 'GroupBuy'
        end) as source,
        count(t.id) as cnt
from (select *,
      count(id) over(partition by user_id) as num
      from order_info
      where date > '2025-10-15'
      and status = 'completed'
      and product_name in ('C++','Java','Python')) as t
left join client c on t.client_id = c.id
where t.num >= 2
group by source
order by source;

(16) SQl 88 最差是第几名(二) ***
【注】:该题主要考察中位数的计算

select grade from 
(select grade,
(select sum(number) from class_grade) as total,
sum(number) over(order by grade) a,
sum(number) over(order by grade desc) b
from class_grade
order by grade) t
where a>=total/2 and b>=total/2
order by grade;

# 等价的式子
with t_rank as
(
    select grade,
       (select sum(number) from class_grade) as total,
       sum(number) over(order by grade) a,
       sum(number) over(order by grade desc) b
    from class_grade
    order by grade
)
select grade 
from t_rank
where a>=total/2 and b>=total/2
order by grade

(17) SQL 83 牛客网的订单分析 ***
  写出一个sql语句查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的来源信息,第一列是显示的是客户端名字,如果是拼团订单则显示GroupBuy,第二列显示这个客户端(或者是拼团订单)有多少订单,最后结果按照第一列(source)升序排序:

-- 方法1:
select (case when o.is_group_buy='Yes' then 'GroupBuy'
       else c.name end) source,count(*) cnt 
# 注意这里有部分用户是团购的,在client中找不到对应的id,因此需要采用left join 
from order_info o left join client c
on o.client_id=c.id 
# 查询满足条件的用户id
where o.user_id 
in (select user_id from order_info 
where product_name in ('C++','Java','Python')
and status='completed' 
and date>'2025-10-15'
group by user_id 
having count(*)>=2)
and o.product_name in ('C++','Java','Python') 
and o.status='completed' 
and o.date>'2025-10-15'
group by source # 注意最后是根据客户端进行分组统计
order by source;

-- 方法2:
select (case when is_group_buy = 'No'
        then c.name
        else 'GroupBuy'
        end) as source,
        count(t.id) as cnt
from (select *,
      count(id)over(partition by user_id) as num
      from order_info
      where date > '2025-10-15'
      and status = 'completed'
      and product_name in ('C++','Java','Python')) as t
left join client c on t.client_id = c.id
where t.num >= 2
group by source
order by source

(18) SQL 90 积分最多的人(二)

select t.user_id,u.name,t.grade_sum from 
user u inner join 
(select user_id,sum(grade_num) grade_sum from grade_info group by user_id) as t 
on t.user_id=u.id 
where t.grade_sum=(select max(grade_sum) from (select user_id,sum(grade_num) grade_sum from grade_info group by user_id) t1)
order by t.grade_sum desc,t.user_id asc;


select t.user_id,u.name,t.grade_sum from 
(select distinct user_id,sum(grade_num) over(partition by user_id) grade_sum
from grade_info) t 
inner join user u on t.user_id=u.id
where t.grade_sum=
(select max(grade_sum) from 
(select sum(grade_num) over(partition by user_id) grade_sum
from grade_info) t1);

困难题

(1) SQl 12 获取每个部门中当前员工薪水最高的相关信息,给出dept_no, emp_no以及其对应的salary,按照部门编号升序排列:

select de.dept_no,de.emp_no,s.salary 
from dept_emp de inner join salaries s on 
de.emp_no=s.emp_no 
where de.to_date='9999-01-01' and s.to_date='9999-01-01'
and s.salary in 
# 获取同一部门的最大薪资
(select max(salary) from salaries s1 
inner join dept_emp de1 
on s1.emp_no=de1.emp_no
where de1.to_date='9999-01-01' and s1.to_date='9999-01-01'
and de1.dept_no=de.dept_no)
order by de.dept_no;

(2) SQL 21 查找在职员工自入职以来的薪水涨幅情况,给出在职员工编号emp_no以及其对应的薪水涨幅growth,并按照growth进行升序:

select e.emp_no,(s2.salary-s1.salary) growth from 
employees e inner join salaries s1 on 
e.emp_no=s1.emp_no and e.hire_date=s1.from_date 
inner join salaries s2 on 
e.emp_no=s2.emp_no and s2.to_date='9999-01-01'
order by growth asc;

(3) SQL 25 获取员工其当前的薪水比其manager当前薪水还高的相关信息:

select t1.emp_no,t2.emp_no,t1.salary,t2.salary from 
# 查找员工的工资
(select de.emp_no,de.dept_no,s.salary from 
dept_emp de inner join salaries s on 
de.emp_no=s.emp_no) t1
inner join 
# 查找部门领导的工资
(select dm.emp_no,dm.dept_no,s.salary from 
dept_manager dm inner join salaries s on 
dm.emp_no=s.emp_no) t2 
on t1.dept_no=t2.dept_no 
where t1.salary>t2.salary;

(4) SQL 26 汇总各个部门当前员工的title类型的分配数目,即结果给出部门编号dept_no、dept_name、其部门下所有的员工的title以及该类型title对应的数目count,结果按照dept_no升序排序:

# 寻找每个部门员工分配的title类型
select t.dept_no,d.dept_name,t.title,t.cnt from
(select de.dept_no,t.title,count(*) cnt from dept_emp de
inner join titles t on 
de.emp_no=t.emp_no 
group by de.dept_no,t.title) t 
inner join departments d on 
t.dept_no=d.dept_no
order by dept_no asc,t.title;

(5) SQL 28 查找描述信息(film.description)中包含robot的电影对应的分类名称(category.name)以及电影数目(count(film.film_id)),而且还需要该分类包含电影总数量(count(film_category.category_id))>=5部:

select c.name,count(f.film_id) from film f
inner join film_category fc on f.film_id=fc.film_id
inner join category c on fc.category_id=c.category_id
where f.description like '%robot%' # 找到包含robot的电影信息
and c.category_id in
(select category_id from 
film_category group by category_id
having count(film_id)>=5)

(6) SQL 70 牛客每天有很多人登录,请你统计一下牛客每个日期新用户的次日留存率:

select a.date, round(count(b.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 b
on a.user_id=b.user_id
and b.date=date_add(a.date,interval 1 day)
group by a.date
union 
select date ,0.000 as p
from login 
where date not in (
select min(date) from login group by user_id)
order by date

(7) SQL 76 考试分数(五)
  写一个sql语句查询各个岗位分数的中位数位置上的所有grade信息,并且按id升序排序:

select id,job,score,s_rank
from 
(select *
        ,(row_number()over(partition by job order by score desc))as s_rank
	    ,(count(score)over(partition by job))as num
        from grade)t1
where abs(t1.s_rank-(t1.num+1)/2)<1
order by id;

(8) SQL 81 牛客的课程订单分析(五)

select user_id,
min(date) as first_buy_date,
min(second_buy_date)as second_buy_date,
count(*) as cnt
from
# 得到符合条件的用户id、第一次购买时间以及第二次购买时间
(select user_id,date,
lead(date,1) over(partition by user_id order by date)as second_buy_date
from order_info
where date > '2025-10-15' 
and product_name in ('C++','Java','Python') 
and status = 'completed') t
group by user_id
having count(*)>=2
order by user_id;

(9) SQL 86 写出SQL语句查询在2025年投递简历的每个岗位,每一个月内收到简历的数目,和对应的2026年的同一个月同岗位,收到简历的数目,最后的结果先按first_year_mon月份降序,再按job降序排序显示:

select t1.job first_year_mon,t1.mon first_year_mon,t1.cnt first_year_cnt,
t2.mon second_year_mon,t2.cnt second_year_cnt from 
(select job,date_format(date,'%Y-%m') mon,sum(num) cnt from resume_info r 
where date>'2025-01-01' and date<'2025-12-31'
group by job,mon) t1
inner join 
(select job,date_format(date,'%Y-%m') mon,sum(num) cnt from resume_info r 
where date>'2026-01-01' and date<'2026-12-31'
group by job,mon)t2
on t1.job=t2.job 
and right(t1.mon,2)=right(t2.mon,2)
order by t1.mon desc,t1.job desc;

(9) SQL 91 获得积分最多的人(三)

select u.id,u.name,t.grade_sum from 
user u inner join 
(select user_id,sum(
case when type='add' then grade_num
else -grade_num 
end) grade_sum from grade_info 
group by user_id) t 
on u.id=t.user_id
where t.grade_sum=
(select max(grade_sum) from 
(select user_id,sum(
case when type='add' then grade_num
else -grade_num 
end) grade_sum from grade_info 
group by user_id) t1)
order by id;

# 首先计算所有用户的积分
with t as 
(select u.id,u.name,g.user_id,
sum(case when g.type='add' then grade_num
   else -grade_num end) grade_sum
from user u inner join grade_info g 
on u.id=g.user_id
group by u.id)
# 然后选取出积分最高的用户信息
select u.id,u.name,t.grade_sum
from user u inner join t
on u.id=t.user_id 
and t.grade_sum=(select max(grade_sum) from t);

【注】:date_add(date,interval 1 day)与lead(date) over(partition by … order by…)区别:前者是简单的时间相加,而后者则是将序列向前进行移动(也就是取得下一天的值):
在这里插入图片描述在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值