牛客sql刷题记录(更新中)

1.查找最晚入职员工的所有信息

方法一:

select * from employees
order by hire_date desc
limit 0,1

LIMIT m,n : 表示从第m+1条开始,取n条数据;
LIMIT n : 表示从第0条开始,取n条数据,是limit(0,n)的缩写。
本题limit 0,1 表示从第(0+1)条数据开始,取一条数据,即取出最晚入职员工。

方法二,更周到,因为最晚入职会不止一条。可能时间日期是一样的。

select * from employees
where hire_date=(
select max(hire_date) from employees)

2.查找入职员工时间排名倒数第三的员工所有信息

嗯如果是接着上一个题目的修正版本:

select * from employees
order by hire_date desc
limit 2,1

很简单就过了。但是其实是有歧义的。
不过如果要去重啥的好像要复杂一些
以及用row_number之类怎么定义这个倒数第三,先不管他了。

3.查找各个部门当前(to_date='9999-01-01')领导当前薪水详情以及其对应部门编号dept_no

select b.*,a.dept_no
from salaries b
inner join dept_manager a
on a.emp_no=b.emp_no
where a.to_date='9999-01-01'
and b.to_date='9999-01-01'

2个易错点:两个to_date都要限制,防止数据错误
主表salary放在前面,salary 和dept表的顺序换掉会跑不过,但我倒是不觉得有啥问题?

另外where改成and 连在on后面也能过

4.查找所有已经分配部门的员工的last_name和first_name以及dept_no

select 
a.last_name,a.first_name,b.dept_no
from employees a 
inner join dept_emp b
on a.emp_no=b.emp_no

简单题

5.查找所有员工的last_name和first_name以及对应部门编号dept_no,也包括展示没有分配具体部门的员工

select 
a.last_name,a.first_name,b.dept_no
from employees a 
left join dept_emp b
on a.emp_no=b.emp_no

在4的基础上改成left就好

6.查找所有员工入职时候的薪水情况,给出emp_no以及salary, 并按照emp_no进行逆序

select  a.emp_no,b.salary
from employees a
inner join salaries b
on a.emp_no=b.emp_no
and a.hire_date=b.from_date
order by a.emp_no desc

入职时候的薪水情况 所以有个 a.hire_date=b.from_date
话说写 and 和where应该都行吧,on and效率高一点,这个可以再查一下。但据说实习的时候能写on就不写where了。

7.查找薪水涨幅超过15次的员工号emp_no以及其对应的涨幅次数t

select emp_no,count(distinct salary)-1 as t
from salaries
group by emp_no
having count(distinct salary)>15

比较合理的话应该是这样,不过通不过,这题目里不是算差值,有一条就算涨幅了。。emm 改成这样就通过了。
然后涨幅也不要管是不是比上一次怎样了,就算count就好了。这题是这样。

select emp_no,count(distinct salary) as t
from salaries
group by emp_no
having count(distinct salary)>=15

8.找出所有员工当前(to_date='9999-01-01')具体的薪水salary情况,对于相同的薪水只显示一次,并按照逆序显示

select distinct salary 
from salaries
where to_date='9999-01-01'
order by salary desc

楼上都是用distinct,但是大表一般用distinct效率不高,大数据量的时候都禁止用distinct,建议用group by解决重复问题。
对于distinct与group by的使用: 1、当对系统的性能高并数据量大时使用group by 2、当对系统的性能不高时使用数据量少时两者皆可 3、尽量使用group by

select salary 
from salaries
where to_date='9999-01-01'
group by salary
order by salary desc

9.获取所有部门当前manager的当前薪水情况,给出dept_no, emp_no以及salary,当前表示to_date='9999-01-01'

select a.dept_no,a.emp_no,b.salary
from dept_manager a
left join salaries b
on a.emp_no=b.emp_no
where a.'to_date'='9999-01-01'
and b.'to_date'='9999-01-01'

left改成inner也可以通过,不过现实工作中,有的领导没有工资应该填写null去排查。

10.获取所有非manager的员工emp_no

select a.emp_no
from employees a
left join  dept_manager b
on a.emp_no=b.emp_no
where  b.emp_no is null

11.获取所有员工当前的manager,如果当前的manager是自己的话结果不显示,当前表示to_date='9999-01-01'。

结果第一列给出当前员工的emp_no,第二列给出其manager对应的manager_no。

select a.emp_no as emp_no,b.emp_no as manager_no
from dept_emp a
left join dept_manager b
on a.dept_no=b.dept_no
where a.to_date='9999-01-01'
and b.to_date='9999-01-01'
and a.emp_no <> b.emp_no

容易错的 一个是命名
一个是最后一句如果当前的manager是自己的话结果不显示
这里的不显示是指当部门主管是自己的时候,整行都不显示;不是不显示部门经理的编号。
这道题用INNER JOIN 和 LEFT JOIN 是都可以通过的,因为默认职员表中每个职员都是分配了部门的,因此保留不保留职员表(左表)没有关联的行(实际上都关联了)结果都一样

另外一个不错的关于on和where 的解释

通不过:

SELECT de.emp_no, dm.emp_no AS manager_no
FROM dept_emp AS de
LEFT JOIN dept_manager AS dm
ON de.dept_no = dm.dept_no
AND de.to_date = '9999-01-01' AND dm.to_date = '9999-01-01' 
AND de.emp_no NOT IN (SELECT dm.emp_no FROM dept_manager)  - 这里的and改成where可以通过,因为用on的话经理也会显示,只是显示null,这里的不显示是指当部门主管是自己的时候,整行都不显示,所以要过滤掉

通过了:

SELECT de.emp_no, dm.emp_no AS manager_no
FROM dept_emp AS de
INNER JOIN dept_manager AS dm
ON de.dept_no = dm.dept_no
AND de.to_date = '9999-01-01' AND dm.to_date = '9999-01-01'
AND de.emp_no NOT IN (SELECT dm.emp_no FROM dept_manager)

这里涉及ON和WHERE的用法。

使用连接操作,关联两张或多张表时,数据库会生成并返回一张临时表。

LEFT JOIN 模式下,ON 和 WHERE 过滤条件的区别( RIGHT JOIN、FULL JOIN 同理):
ON 条件是在生成临时表时使用的条件,不管 ON 中的条件是否为真,都会返回左边表中的记录;
WHERE 条件是在临时表已经生成后,对临时表进行的过滤条件,如果 WHERE 条件不为真,记录就会被过滤掉。

INNER JOIN 只返回同时存在于两张或多张表的行数据,所以过滤条件放在 ON 中或 WHERE 中,返回的结果一样。

12.获取所有部门中当前员工薪水最高的相关信息,给出dept_no, emp_no以及其对应的salary

能通过但是有问题:
问题在于emp_no
同时select了b.dept_no,a.emp_no,但是group by 第一个b.dept_no
那会出现一个部门有多条emp_no,但最后筛选出来的是默认第一条, 而非max对应的那一条。

这个问题其实也包括了我曾经困扰的:
select 多字段 ,但是只group by 其中某些字段
有时候会出错的, 因为都会出现默认第一条。
有些时候我们可以用max或者sum来筛选。

select b.dept_no,a.emp_no,max(a.salary) as salary
from salaries a
inner join dept_emp b
on a.emp_no=b.emp_no

where a.to_date='9999-01-01'
and b.to_date='9999-01-01'
group by 1

修改成,三表匹配:

select c.dept_no,d.emp_no,c.max_salary
from
    (select b.dept_no,max(a.salary) as max_salary
    from salaries a
    inner join dept_emp b
    on a.emp_no=b.emp_no
    where a.to_date='9999-01-01'
    and b.to_date='9999-01-01'
    group by 1)c
inner join salaries d
on c.max_salary=d.salary
inner join dept_emp e
on d.emp_no=e.emp_no
and c.dept_no=e.dept_no
where d.to_date='9999-01-01'
and e.to_date='9999-01-01'
order by c.dept_no asc

另外一个更常用,不容易出错的方法,就是窗口函数

select c.dept_no,c.emp_no,c.salary
from
    (select a.dept_no,b.emp_no,b.salary,
    rank() over(partition by a.dept_no order by b.salary desc) as ranking
    from dept_emp a
    inner join salaries b
    on a.emp_no=b.emp_no
    where a.to_date='9999-01-01' and b.to_date='9999-01-01'
    )c

where c.ranking=1

13.从titles表获取按照title进行分组,每组个数大于等于2,给出title以及对应的数目t。

so easy

select title,count(*) as t
from titles
group by 1
having count(*)>=2

14.从titles表获取按照title进行分组,每组个数大于等于2,给出title以及对应的数目t。注意对于重复的emp_no进行忽略。

我是这样写的,不过看了大家的讨论有一些争议。
争议在于有人说distinct是对后面所有的起作用的。

select title,count(*) as t
from 
(select distinct emp_no,title from titles)
group by 1
having count(*)>=2

答案是

select  title ,count(distinct emp_no) as t
from titles 
 
group by title 
having t >= 2

distinct 与group by 去重

mysql中常用去重复数据的方法是使用 distinct 或者group by ,以上2种均能实现,但2者也有不同的地方。

distinct 特点:

如:select distinct name, sex,from tb_students 这个sql的语法中,查询 tb_students 表中 name, sex,并去除名字和性别都重复的学生:

1、distinct 只能放在查询字段的最前面,不能放在查询字段的中间或者后面。

备注:select sex,distinct name from tb_students 这种写法是错误的,distinct 只能写在所有查询字段的前面

2、distinct 对后面所有的字段均起作用,即 去重是查询的所有字段完全重复的数据,而不是只对 distinct 后面连接的单个字段重复的数据。

备注:也就是 distinct 关键字对 name, sex 都起作用,去重姓名、性别完全一样的学生,如果姓名相同、性别不同是不会去重的。

3、要查询多个字段,但只针对一个字段去重,使用distinct去重的话是无法实现的。

group by 特点:

1、一般与聚类函数使用(如count()/sum()等),也可单独使用。

2、group by 也对后面所有的字段均起作用,即 去重是查询的所有字段完全重复的数据,而不是只对 group by后面连接的单个字段重复的数据。

3、查询的字段与group by 后面分组的字段没有限制。

特别说明:在oracle中使用group by时,查询的字段必须是group by 分组的字段和聚类函数。如select name,sex from tb_students group by name这个sql

语法在oracle中是错误的,因为sex 不在group by 分组后面;但在mysql中是支持的。

14.查找employees表所有emp_no为奇数,且last_name不为Mary的员工信息,并按照hire_date逆序排列

select *
from employees
where emp_no%2=1
and last_name<>'Mary'
order by hire_date desc

mod(emp_no,2)=1本应该也可以。但是可能牛客的库不支持。

15.统计出当前各个title类型对应的员工当前(to_date='9999-01-01')薪水对应的平均工资。结果给出title以及平均工资avg。

select b.title,avg(a.salary)
from salaries a inner join
titles b
on a.emp_no=b.emp_no
where a.to_date='9999-01-01'
and b.to_date='9999-01-01'
group by 1

16.获取当前(to_date='9999-01-01')薪水第二多的员工的emp_no以及其对应的薪水salary

select  a.emp_no,a.salary 
from

(select emp_no,salary,
    dense_rank()over (order by salary desc)as ranking
from salaries)a
where a.ranking=2

dense_rank 是11112的类型。

你可能感兴趣的:(牛客sql刷题记录(更新中))