数据库sql 实战----牛客实战题库


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

涉及到多个相同情况的考虑

select * from employees where hire_date=(select max(hire_date) from employees); 
select * from employees order by hire_date desc limit 0,1;      //LIMIT m,n : 表示从第m+1条开始,取n条数据;

CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,

PRIMARY KEY (`emp_no`));


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

涉及到重复情况的考虑  distinct 修饰hire_date 字段  并排序选取第2位(因为最初从0开始计数)

select * from employees 

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

CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,

PRIMARY KEY (`emp_no`));


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

涉及到两个表之间的连接

select s.*,  d.dept_no  from salaries sdept_manager d

where    s.to_date='9999-01-01' and     d.to_date='9999-01-01'    and   s.emp_no = d.emp_no;

使用 内连接 inner join    on 也可以~一个道理~

select s.*,d.dept_no from salaries s inner join dept_manager d on s.emp_no =d.emp_no and s.to_date='9999-01-01' and  d.to_date='9999-01-01';

//一定注意对应的顺序关系!!

CREATE TABLE `dept_manager` (
`dept_no` char(4) NOT NULL,
`emp_no` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`));



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

常规做法:

select e.last_name,e.first_name,d.dept_no  from employees e ,dept_emp d where e.emp_no =d.emp_no;

只有一列公共列且名称 类型均相同 则用自然连接:

select e.last_name,e.first_name,d.dept_no  from employees e natural join dept_emp d ;

CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,

PRIMARY KEY (`emp_no`)); 


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

左连接:

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

CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,

PRIMARY KEY (`emp_no`)); 


注意on与where有什么区别,两个表连接时用on, 在使用left  jion时,on和where条件的区别如下:
1、  on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。

2、where条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有left join的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。


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

常规做法:

select s.emp_no ,s.salary from salaries s,employees e  where s.emp_no=e.emp_no and s.from_date=e.hire_date order by e.emp_no desc; 

内连接:

select s.emp_no ,s.salary from salaries s inner join employees e  on s.emp_no=e.emp_no and s.from_date=e.hire_date order by e.emp_no desc; 


CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`)); 


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

主要考虑 count()和group by 情况:

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

CREATE TABLE `salaries` (

`emp_no` int(11) NOT NULL,

`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`)); 



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

使用distinct修饰字段 去除重复:

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

使用group by 来分组去重复:

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


CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`)); 


对于distinct,groupby的性能:

  1. 数据量非常巨大时候,比如1000万中有300W重复数据,这时候的distinct的效率略好于group by;
  2. 对于相对重复量较小的数据量比如1000万中1万的重复量,用groupby的性能会远优于distnct。


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

常规做法:

select d.dept_no ,d.emp_no ,s.salary from dept_manager d,salaries s where d.emp_no=s.emp_no and d.to_date='9999-01-01' and s.to_date='9999-01-01';

内连接:

select d.dept_no,d.emp_no,s.salary from dept_manager d inner join salaries s on d.emp_no=s.emp_no and d.to_date='9999-01-01' and s.to_date='9999-01-01';


CREATE TABLE `dept_manager` (
`dept_no` char(4) NOT NULL,
`emp_no` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));



10 获取所有非manager的员工emp_no

常规not in 方法:

select emp_no from employees where emp_no  not in(select emp_no from dept_manager);

用 左连接的方法:

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;


CREATE TABLE `dept_manager` (

`dept_no` char(4) NOT NULL,
`emp_no` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,

PRIMARY KEY (`emp_no`));


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

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

常规:

select d.emp_no , m.emp_no as manager_no from dept_emp d, dept_manager m where d.dept_no=m.dept_no and d.emp_no<>m.emp_no and d.to_date='9999-01-01' and m.to_date='9999-01-01';

内连接:

select d.emp_no , m.emp_no manager_no from dept_emp d inner join dept_manager m on d.dept_no=m.dept_no and d.to_date='9999-01-01' and m.to_date='9999-01-01' and d.emp_no !=m.emp_no;


CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `dept_manager` (
`dept_no` char(4) NOT NULL,
`emp_no` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`dept_no`)); 



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

注意 max聚合函数----分组group by ----别名:

select d.dept_no,d.emp_no,max(s.salary) as salary from dept_emp d inner join salaries s on d.emp_no=s.emp_no and d.to_date='9999-01-01' and s.to_date='9999-01-01' group by d.dept_no;

使用 分组后再限制条件  having :

select d.dept_no,d.emp_no, s.salary as salary from dept_emp d inner join salaries s on d.emp_no=s.emp_no and d.to_date='9999-01-01' and s.to_date='9999-01-01' group by d.dept_no having s.salary=max(salary);


CREATE TABLE `dept_emp` (

`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`)); 


补充 真正的答案:

写法一:

1
2
3
4
5
6
7
8
9
10
11
12
select dept_emp.dept_no as dept_no_a, dept_emp.emp_no, max(salaries.salary) as salary
from dept_emp,salaries
where salaries.emp_no=dept_emp.emp_no
group by dept_emp.emp_no,dept_emp.dept_no
having max(salaries.salary) =
(
     select max(salaries.salary)
     from dept_emp inner join salaries
     on salaries.emp_no=dept_emp.emp_no
     where dept_emp.dept_no = dept_no_a
)
order by dept_no_a

写法二:

1
2
3
4
5
6
7
8
9
10
11
12
select distinct dept_no, s.emp_no, salary
from dept_emp as d inner join salaries as s
on s.emp_no = d.emp_no and s.salary =
(select s2.salary
  from salaries as s2 inner join dept_emp as d2
  on s2.emp_no = d2.emp_no
  where d2.dept_no = d.dept_no
  order by s2.salary desc
  limit 1
)
order by dept_no
;

写法三:
受限于any_value的实现,如果是返回第一个值就没有问题

1
2
3
4
5
6
7
select ret.dept_no, any_value(ret.emp_no), max(ret.salary)
from
(select dept_emp.dept_no,dept_emp.emp_no, max(salaries.salary) as salary
from dept_emp,salaries
where salaries.emp_no=dept_emp.emp_no
group by dept_emp.emp_no,dept_emp.dept_no order by salary desc
) as ret group by ret.dept_no


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

count的使用和分组 之后条件的筛选------where之后不能跟count !
select title, count(title) as t from titles group by title having count(title)>=2;


CREATE TABLE IF NOT EXISTS "titles" (
`emp_no` int(11) NOT NULL,
`title` varchar(50) NOT NULL,
`from_date` date NOT NULL,

`to_date` date DEFAULT NULL); 




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

去除重复字段用 distinct 

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


CREATE TABLE IF NOT EXISTS "titles" (
`emp_no` int(11) NOT NULL,
`title` varchar(50) NOT NULL,
`from_date` date NOT NULL,
`to_date` date DEFAULT NULL); 



15  查找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 ;


CREATE TABLE `employees` (

`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,

PRIMARY KEY (`emp_no`)); 




16  统计出当前各个title类型对应的员工当前薪水对应的平均工资。结果给出title以及平均工资avg。

主要注意 分组---聚合函数--多表连接---当前时间默认为‘9999-01-01':

select t.title, avg(s.salary) as avg from titles t, salaries s where t.emp_no=s.emp_no and t.to_date='9999-01-01' and s.to_date='9999-01-01' group by t.title;


CREATE TABLE `salaries` (

`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));
CREATE TABLE IF NOT EXISTS "titles" (
`emp_no` int(11) NOT NULL,
`title` varchar(50) NOT NULL,
`from_date` date NOT NULL,

`to_date` date DEFAULT NULL); 



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

          order by ---

                         select emp_no ,salary  from  salaries  where  to_date='9999-01-01'   order by   salary  desc  limit 1,1;

            如果有多条重复数据:
                 select emp_no ,salary from salaries where to_date='9999-01-01' and salary=(select salary from salaries order by salary desc limit 1,1);


CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`));




18 查找当前薪水(to_date='9999-01-01')排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,不准使用order by

                故想到  选取 salary 是在除去最大工资的集合中的最大值------> 第二大。

select e.emp_no, max(s.salary), e.last_name, e.first_name from employees e, salaries s where e.emp_no=s.emp_no and s.to_date='9999-01-01' and s.salary not in(select max(salary) from salaries where to_date='9999-01-01') ; 

                   或者是 直接在小于最大的里面再找出最小的那个即可:

select e.emp_no, max(s.salary), e.last_name, e.first_name from employees e, salaries s where e.emp_no=s.emp_no and s.to_date='9999-01-01' and s.salary <(select max(salary) from salaries where to_date='9999-01-01') ;


CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));



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

3个表----使用两次左连接 
select em.last_name ,em.first_name, de.dept_name from (employees em left join dept_emp dp on em.emp_no=dp.emp_no) left join departments de on de.dept_no=dp.dept_no;

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


CREATE TABLE `departments` (
`dept_no` char(4) NOT NULL,
`dept_name` varchar(40) NOT NULL,
PRIMARY KEY (`dept_no`));
CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,

PRIMARY KEY (`emp_no`));



20  查找员工编号emp_now为10001其自入职以来的薪水salary涨幅值growth

不严谨的做法: 用最大-最小值 

select max(salary)-min(salary)  as growth from salaries where emp_no=10001 ;

严谨的做法: 找到第一次记录和最后一次记录

select (select salary from salaries where emp_no=10001 order by to_date desc limit 0,1)-(select salary from salaries where emp_no=10001 order by to_date limit 0,1)as growth  ;


CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`));


21   查找所有员工自入职以来的薪水涨幅情况,给出员工编号emp_noy以及其对应的薪水涨幅growth,并按照growth进行升序

思路:对应employees 员工表 和两个salaries 表----分别为入职时候的表---和 当前的薪水表

select e.emp_no , b.salary-c.salary as growth from employees e 

                inner join salaries b on e.emp_no=b.emp_no and b.to_date='9999-01-01' 

                inner join salaries c on c.emp_no=e.emp_no and e.hire_date=c.from_date  

order by growth ;


CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`));



22 统计各个部门对应员工涨幅的次数总和,给出部门编码dept_no、部门名称dept_name以及次数sum

主要注意 :分组group by -----连接两个表----统计次数count()

select de.dept_no ,dp.dept_name, count(s.salary) as sum from dept_emp de join departments dp on dp.dept_no=de.dept_no join salaries s on s.emp_no=de.emp_no group by de.dept_no;


CREATE TABLE `departments` (
`dept_no` char(4) NOT NULL,
`dept_name` varchar(40) NOT NULL,
PRIMARY KEY (`dept_no`));
CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`)); 



23   

你可能感兴趣的:(MySQL,数据库)