牛客面試必考真題SQL篇72題全答案——進階篇
本篇主要包含牛客面試必考真題SQL篇72題的進階部分,主要包括較難級別(18題)和困難級別(4題),題目較為複雜,我的答案可能也不是最優解哈,歡迎大家多多指正!
刷題連結:
SQL專題文章轉送門:
目錄
較難級別(18道)
SQL18:查詢當前薪水排名第二多的員工編號emp_no。
select employees.emp_no, salary, last_name, first_name from (select row_number() over(order by salary desc) num, emp_no, salary from salaries where to_date='9999-01-01') as temp right join employees on temp.emp_no = employees.emp_no where num = 2
SQL21:查詢所有員工自入職以來的薪水漲幅情況。
法一:(自己解法) select employees.emp_no, max(salary)-min(salary) as growth from (select row_number() over(partition by emp_no order by to_date) num1, row_number() over(partition by emp_no order by to_date desc) num2, emp_no, salary, to_date from salaries) as temp right join employees on temp.emp_no = employees.emp_no where num1 = 1 or num2 = 1 group by employees.emp_no having max(to_date)='9999-01-01' order by growth 法二:(參考答案,分兩次查詢) select a.emp_no, b.salary - a.salary as growth from (select e.emp_no, s.salary from employees e join salaries s on e.emp_no=s.emp_no where hire_date=from_date) as a join (select e.emp_no, s.salary from employees e join salaries s on e.emp_no=s.emp_no where to_date='9999-01-01') as b on a.emp_no = b.emp_no order by growth
SQL23:對所有員工的當前薪水按照salary進行按照1-N的排名。
select emp_no, salary, dense_rank() over(order by salary desc) t_rank
from salaries
where to_date='9999-01-01'
SQL24:獲取所有非manager員工當前的薪水情況。
# 沒有分配部門的員工不計算在內 select dept_no, t.emp_no, salary from (select emp_no, salary from salaries where to_date='9999-01-01' and emp_no not in (select emp_no from dept_manager where to_date='9999-01-01')) as t join dept_emp d on t.emp_no = d.emp_no
SQL25:獲取員工其當前的薪水比其manager當前薪水還高的相關資訊。
# 參考思路:分別用兩張表統計員工和manager的薪水
select a.emp_no, b.emp_no, a.salary, b.salary
from
(select d.emp_no, d.dept_no, s.salary
from dept_emp d join salaries s
on d.emp_no = s.emp_no
where s.to_date='9999-01-01') a
join
(select m.emp_no, m.dept_no, s.salary
from dept_manager m join salaries s
on m.emp_no = s.emp_no
where s.to_date='9999-01-01') b
on a.dept_no = b.dept_no
where a.salary>b.salary
SQL27:給出每個員工每年薪水漲幅超過5000的員工編號emp_no。
# 參考思路:自連線
select s1.emp_no, s2.from_date, s2.salary-s1.salary as salary_growth
from salaries s1 join salaries s2
on s1.to_date=s2.from_date and s1.emp_no=s2.emp_no
where s2.salary-s1.salary>5000
order by salary_growth desc
SQL28:查詢描述資訊中包括robot的電影對應的分類名稱以及電影數目。
# 參考答案:三表全連再過濾,注意必須新增group by
select c.name, count(f.film_id)
from film f join film_category fc on f.film_id=fc.film_id
join category c on fc.category_id=c.category_id
where f.description like '%robot%' and fc.category_id in (select category_id
from film_category
group by category_id
having count(film_id)>=5)
group by c.name
SQL56:獲取所有員工的emp_no。
select e.emp_no, d.dept_no, btype, received
from employees e join dept_emp d
on e.emp_no = d.emp_no
left join emp_bonus b
on e.emp_no = b.emp_no
SQL59:獲取有獎金的員工相關資訊。
select e.emp_no, first_name, last_name, btype, salary, (case btype
when 1 then salary*0.1
when 2 then salary*0.2
else salary*0.3
end) bonus
from employees e join salaries s
on e.emp_no = s.emp_no
join emp_bonus b
on e.emp_no = b.emp_no
where s.to_date='9999-01-01'
注:case的用法
SQL60:統計salary的累計和running_total。
select emp_no, salary, sum(salary) over(order by emp_no) running_total
from salaries
where to_date = '9999-01-01'
注:視窗函式 + 自連線取不等條件
SQL61:對於employees表中,給出奇數行的first_name。
select e.first_name
from (select row_number() over(order by first_name asc) num, first_name from employees) as t
join employees e
on t.first_name=e.first_name
where num%2=1
SQL65:異常的郵件概率。
select e.date, round(sum(case e.type when 'no_completed' then 1 else 0 end)/count(type),3) as p
from email e
join user u1 on e.send_id=u1.id
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 e.date
SQL67:牛客每個人最近的登入日期(二)
select u_n, c_n, d
from (select u.name as u_n, c.name as c_n , l.date as d,
row_number() over(partition by l.user_id order by l.date desc) num
from login l
join user u on l.user_id=u.id
join client c on l.client_id=c.id) as t
where num=1
order by u_n
SQL68:牛客每個人最近的登入日期(三)
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)
注:MySQL裡查詢某一天的後一天的用法是:date_add(yyyy-mm-dd, interval 1 day)
SQL69:牛客每個人最近的登入日期(四)
select date, sum(case num when 1 then 1 else 0 end) as new
from (select user_id, date, row_number() over(partition by user_id order by date) num
from login) as t
group by date
order by date
SQL71:牛客每個人最近的登入日期(六)
select u.name as u_n, p.date as date, sum(number) over(partition by user_id order by date) ps_num
from passing_number p join user u
on p.user_id = u.id
order by date, u_n
SQL74:考試分數(三)
select id, name, score
from (select g.id id, l.name name, score, dense_rank() over(partition by g.language_id order by score desc) num
from grade g join language l
on g.language_id=l.id) as t
where num<=2
order by name, score desc, id
SQL75:考試分數(四)
select job,
(case when count(id)%2=0 then cast(count(id)/2 as signed) else cast((count(id)+1)/2 as signed) end) as start,
(case when count(id)%2=0 then cast((count(id)+2)/2 as signed) else cast((count(id)+1)/2 as signed) end) as end
from grade
group by job
order by job
注:浮點數轉為整數:cast(col as signed)
困難級別(4道)
SQL12:獲取所有部門中當前員工薪水最高的相關資訊。
select dept_no, emp_no, salary
from(select e.dept_no, e.emp_no, s.salary, rank() over(partition by e.dept_no order by s.salary desc) num
from dept_emp e join salaries s
on e.emp_no=s.emp_no
where e.to_date='9999-01-01' and s.to_date='9999-01-01') as t
where num=1
order by dept_no
SQL26: 彙總各個部門當前員工的title型別的分配數目。
select t.dept_no, d.dept_name, t.title, t.count
from(select e.dept_no, t.title, count(title) as count
from dept_emp e join titles t
on e.emp_no=t.emp_no
where e.to_date='9999-01-01' and t.to_date='9999-01-01'
group by e.dept_no, t.title) t
join departments d
on t.dept_no = d.dept_no
order by t.dept_no, t.title
SQL70:牛客每個人最近的登入日期(五)
select a.date, round(ifnull(ifnull(count_2, 0)*1.0/count_1, 0), 3) p
from
(select date, sum(case num when 1 then 1 else 0 end) as count_1
from(select user_id, date, row_number() over(partition by user_id order by date) num
from login) as t
group by date) a
left join
(select date_add(min(date), interval -1 day) as date, count(user_id) as count_2
from login
where (user_id, date) in (select user_id, date_add(min(date), interval 1 day)
from login
group by user_id)
group by date) b
on a.date = b.date
注:ifnull()用於判斷第一個表示式是否為null,如果為null則返回第二個引數的值,如果不為null則返回第一個引數的值。具體形式:ifnull(expression, alt_value)
SQL76:考試分數(五)
# 注:注意partition by和group by的重複使用
select B.* from
(select job,
(case when count(id)%2=0 then cast(count(id)/2 as signed) else cast((count(id)+1)/2 as signed) end) as start,
(case when count(id)%2=0 then cast((count(id)+2)/2 as signed) else cast((count(id)+1)/2 as signed) end) as end
from grade
group by job) A
join
(select id, job, score, row_number() over(partition by job order by score desc) t_rank
from grade) B
on A.job=B.job and B.t_rank between A.start and A.end
order by B.id