这是一个系列文章,总共61题,分6期,有答案以及解题思路,并附上解题的一个思考过程。
具体题目可参考牛客网的SQL实战模块:https://www.nowcoder.com/ta/sql?page=0
第一题:查找最晚入职员工的所有信息
select * from employeeswhere hire_date = (select max(hire_date) from employees);
解题思路:使用子查询的方法,先找出所有入职时间里最大(即最晚)的时间,这样可以确保多条最晚入职时间都可以被查询出来。
有使用order by 按由大到小排序后选取第一条的方法,但这种方法仅限于当最晚入职数据只有一条时才正确,不能保证有多条同一最晚时间时的准确性。
select * from employeesorder by hire_date desclimit 1;
第二题:查找入职员工时间排名倒数第三的员工所有信息
select * from employeeswhere hire_date = (select distinct hire_date from employeesorder by hire_date desc limit 2,1);
解题思路:同样使用子查询方法,关键是要用distinct去重,去重后会按入职日期进行分组,多个相同入职日期会分为一组。
第一次做的方法:
select * from employeesorder by hire_date desc limit 2,1;
这个查询没有去重,只能查出入职时间排名三的时间的员工信息,而不是入职员工时间排名倒数第三的员工的入职时间。
第三题:查找各个部门当前(to_date='9999-01-01')领导当前薪水详情以及其对应部门编号dept_no
select s.*,d.dept_nofrom salaries s join dept_manager d on s.emp_no = d.emp_nowhere d.to_date = '9999-01-01' and s.to_date = '9999-01-01';
解题思路:以s.emp_no = d.emp_no为联结条件联结两表,同时限制两表to_date = '9999-01-01'(原因:去重锁定)
第四题:查找所有已经分配部门的员工的last_name和first_name
select e.last_name,e.first_name,d.dept_nofrom employees e left join dept_emp d on e.emp_no = d.emp_nowhere d.dept_no is not null;
解题思路:employees作为主表,使用左联结,关键是where d.dept_no is not null;题目要查找所有已经分配部门的员工,意味着必须要带有dept_no,没有dept_no的就是还未分配部门的,去除dept_no为空即可求得。
第五题:查找所有员工的last_name和first_name以及对应部门编号dept_no,也包括展示没有分配具体部门的员工
select e.last_name,e.first_name,d.dept_nofrom employees e left join dept_emp d on e.emp_no = d.emp_no;
解题思路:employees作为主表,使用左联结,比较简单。
第六题:查找所有员工入职时候的薪水情况,给出emp_no以及salary, 并按照emp_no进行逆序
select e.emp_no,s.salaryfrom salaries s left join employees e on s.emp_no = e.emp_nowhere e.hire_date = s.from_dateorder by s.emp_no desc;
解题思路:使用左联结(内联结也可以),此题最关键的是salaries表的from_date 和 employees表的hire_date 的值应该要相等,因此有限制条件e.hire_date = s.from_date,两个date都是刚入职的时间,一开始我就是搞不清这个逻辑关系,一个劲想用min(from_date)求出。
另外,salaries表中有多个相同emp_no的salary,即emp_no在salaries表中不唯一,仔细查看时间和工资,推断出是同一员工在不同时间的工资不同,即涨薪或降薪(具体涨薪或降薪没有一个个查看),理解这两个关键点解题就没问题了。
第七题:查找薪水涨幅超过15次的员工号emp_no以及其对应的涨幅次数t
select a.emp_no,count(*) tfrom salaries a inner join salaries b on a.emp_no=b.emp_no and a.to_date = b.from_datewhere a.salary < b.salarygroup by a.emp_nohaving t > 15;
解题思路:count(*) 为计算全部数据的行数地意思,比较关键的一个点就是联结条件a.to_date = b.from_date,这个条件限定了两个工资之比必须是相邻的,如果没有这个条件,那同一个emp_no下的任意两个salary都可以做对比,可以把这个条件去掉,对比两个查询结果,就明白了。
验证查询:创建表sa,表信息如下:
计算涨薪次数查询代码:
select a.emp_no,count(*) tfrom sa a inner join sa b on a.emp_no=b.emp_no and a.to_date = b.from_datewhere a.salary < b.salarygroup by a.emp_no;
验证无误,但还是有一个小问题,这个查询结果仅以后一次工资跟前一次工资对比,只要比前一次工资高,就算一次涨薪,而不与这“后一次”工资跟前一次之前的工资数据相比。
例如:
在原来数据的基础上增加两条数据,工资为1和3,这个数据查询出的涨薪次数就是4。
这条题目比较有争议,题意不清晰。以下是我找到正确答案之前的尝试:方法一:
select emp_no,count(salary) as tfrom salariesgroup by emp_nohaving t > 16;
但是这个会有一个问题,它只查询工资出现次数的计数,不管是加薪、降薪还是工资不变的所有情况,都会被计算进去。
方法二:
select emp_no,count(distinct salary) as tgroup by emp_nohaving t > 16;
这个也有问题,这只能计算出在没有降薪的情况下的涨薪次数,后面如果有降薪的话,就会出错。
(这两个方法都选择大于16的条件限定的原因:都是以工资出现次数为计算涨薪的,把第一次工资也计算进去了,实际上第一次工资不能纳入涨薪次数中去,所以用大于16来限定条件,达到符合题目要求的涨薪大于15次目的)
第八题:找出所有员工当前(to_date='9999-01-01')具体的薪水salary情况,对于相同的薪水只显示一次,并按照逆序显示
select salaryfrom salarieswhere to_date='9999-01-01'group by salaryorder by salary desc;
解题思路:关键理解对于相同的薪水只显示一次,使用分组方法对salary进行分组求得,也可以使用distinct去重的方法,但是一般数据较多时使用distinct效率相比group by 要慢,所以从性能角度考虑,应该尽量使用group by。
使用distinct方法如下:
select distinct salaryfrom salarieswhere to_date='9999-01-01'order by salary desc;
第九题:获取所有部门当前manager的当前薪水情况,给出dept_no, emp_no以及salary,当前表示to_date='9999-01-01'
SELECT d.dept_no,d.emp_no,s.salaryfrom salaries s left join dept_manager d on d.emp_no = s.emp_nowhere d.to_date = '9999-01-01' and s.to_date = '9999-01-01';
解题思路:关键点是dept_manager表的to_date和salaries表的to_date都要等于'9999-01-01',s.to_date = '9999-01-01'容易理解,限定当前时间当前工资,d.to_date = '9999-01-01'则限定在职经理,注意理解题目,有两个“当前”。
第十题:获取所有非manager的员工emp_no
select e.emp_nofrom employees e left join dept_manager d on e.emp_no = d.emp_nowhere d.dept_no is null;
解题思路:employees作为主表,使用左联结,限定条件为d.dept_no为空,选出在employees但不在dept_manager中的emp_no记录。
--end--