牛客网-数据库SQL实战(未完,持续更新)

2、[编程题]查找入职员工时间排名倒数第三的员工所有信息

数据库表:
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`));

题解:

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

语法注释:

distinct :用来过滤重复数据

distinct支持单列、多列的去重方式。 
单列去重的方式简明易懂,即相同值只保留1个。 
多列的去重则是根据指定的去重的列信息来进行,即只有所有指定的列信息都相同,才会被认为是重复的信息。

详细用法可参考:https://blog.youkuaiyun.com/ljl890705/article/details/70602442

 

order by:如果我们需要对读取的数据进行排序,我们就可以使用 MySQL 的 ORDER BY 子句来设定你想按哪个字段哪种方式来进行排序,再返回搜索结果。

以下是 SQL SELECT 语句使用 ORDER BY 子句将查询数据排序后再返回数据:

SELECT field1, field2,...fieldN table_name1, table_name2...
ORDER BY field1, [field2...] [ASC [DESC]]
  • 你可以使用任何字段来作为排序的条件,从而返回排序后的查询结果。
  • 你可以设定多个字段来排序。
  • 你可以使用 ASCDESC 关键字来设置查询结果是按升序降序排列。 默认情况下,它是按升序排列。
  • 你可以添加 WHERE...LIKE 子句来设置条件。

 

limit:

SELECT * FROM 表名 limit m,n;

 

1.m代表从m+1条记录行开始检索,n代表取出n条数据。(m可设为0)

如:SELECT * FROM 表名 limit 6,5;

表示:从第7条记录行开始算,取出5条数据

2.值得注意的是,n可以被设置为-1,当n为-1时,表示从m+1行开始检索,直到取出最后一条数据。

如:SELECT * FROM 表名 limit 6,-1;

表示:取出第6条记录行以后的所有数据。

3.若只给出m,则表示从第1条记录行开始算一共取出m条

如:SELECT * FROM 表名 limit 6;

表示:取出前6条记录行。

3、查找各个部门当前(to_date='9999-01-01')领导当前薪水详情以及其对应部门编号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`));
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`));

输出描述:

 

解法一:

SELECT s.*, d.dept_no FROM salaries s , dept_manager d

WHERE s.to_date='9999-01-01'

AND d.to_date='9999-01-01'

AND s.emp_no = d.emp_no;

解法二:

SELECT s.*, d.dept_no FROM salaries s  INNER JOIN dept_manager d

ON s.to_date='9999-01-01'

AND d.to_date='9999-01-01'

AND s.emp_no = d.emp_no;

 

语法注释:

首先说一下注意事项:

这里的坑主要在于两个表的逻辑关系,题目要求是薪水情况以及部门编号,再结合输出情况dept_no 被放到了最后一列,可见是主表是“salaries”。这里顺序错了就会提示:您的代码无法通过所有用例。

如下面缩写即提示错误:

select d.emp_no,s.* 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';

 

其次:

直接写s.to_date=d.to_date='9999-01-01',也是不通过的,需要严格按照上面语法将日期分别写出来,具体原因是什么待解决??????

 

最后介绍解法二中的连接多表的语法;

使用 MySQL 的 JOIN 在两个或多个表中查询数据。

JOIN 按照功能大致分为如下三类:

  • INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。
  • LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
  • RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。

具体可参考:http://www.runoob.com/mysql/mysql-join.html

 

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

数据库表:

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`));

输出描述:

 

题解:

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;

语法注释:

参考上一题即可,inner join表示两表中相同的字段;

 

5、查找所有员工的last_name和first_name以及对应部门编号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`));

 

题解:

SELECT e.last_name , e.first_name ,d.dept_no

FROM  employees e  LEFT JOIN dept_emp d

ON d.emp_no=e.emp_no;

语法注释:

根据题目,需要找出employes表中所有信息,同时找出dept_emp表中的部分信息的dept_no,即便该表中有的字段无对应信息;

因此需要使用LEFT JOIN提取信息:

LEFT JOIN 会读取左边数据表的全部数据,即便右边表无对应数据

6、查找所有员工入职时候的薪水情况,给出emp_no以及salary, 并按照emp_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`));
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`));

题解:

SELECT s.emp_no ,s.salary

FROM salaries s INNER JOIN employees e

ON e.emp_no = s.emp_no AND e.hire_date = s.from_date

ORDER BY e.emp_no desc;

语法注释:

此题应注意以下四个知识点:

1、由于测试数据中,salaries.emp_no不唯一(因为号码为emp_no的员工会有多次涨薪的可能,所以在salaries中对应的记录不止一条),employees.emp_no唯一,即salaries的数据会多于employees,因此需先找到employees.emp_no在salaries表中对应的记录salaries.emp_no,则有限制条件e.emp_no = s.emp_no

2、根据题意注意到salaries.from_date 和employees.hire_date的值应该要相等,因此有限制条件 e.hire_date = s.from_date

3、根据题意要按照emp_no值逆序排列,因此最后要加上 ORDER BY e.emp_no DESC

4、为了代码良好的可读性,运用了Alias别名语句,将employees简化为e,salaries简化为s,即 employees AS e与salaries AS s,其中AS可以省略

 

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

数据库表:

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`));

题解:

SELECT emp_no , COUNT(emp_no) AS t FROM salaries GROUP BY emp_no HAVING t>15;

语法注释:

GROUP BY:根据一个或多个列对结果集进行分组。在分组的列上我们可以使用 function如:COUNT, SUM, AVG,等函数。

由于COUNT()函数不可用于WHERE语句中,故使用HAVING语句来限定t>15的条件

 

WHERE 与 HAVING的区别:

WHERE语句在GROUP BY语句之前;SQL会在分组之前计算WHERE语句。   

HAVING语句在GROUP BY语句之后;SQL会在分组之后计算HAVING语句。

 

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

数据库表:

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`));

题解:

SELECT salary FROM salaries WHERE to_date= '9999-01-01'

GROUP BY salary

ORDER BY salary desc;

语法注释:

使用GROUP BY解决了salary的重复问题;

去重语法distinct和group by的区别:https://www.jianshu.com/p/34800d06f63d

这里一般建议选择GROUP BY

各类使用方法可参考:https://www.cnblogs.com/vanl/p/5912903.html

9、获取所有部门当前manager的当前薪水情况,给出dept_no, emp_no以及salary,当前表示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`));

题解:

SELECT d.dept_no , d.emp_no , s.salary

FROM dept_manager d  INNER JOIN salaries s 

WHERE d.to_date='9999-01-01' AND s.to_date = '9999-01-01' AND d.emp_no = s.emp_no;

语法注释:

 

10、获取所有非manager的员工emp_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`));
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`));

题解:

方法一:使用NOT IN选出在employees但不在dept_manager中的emp_no记录

1

2

SELECT emp_no FROM employees

WHERE emp_no NOT IN (SELECT emp_no FROM dept_manager);

方法二:先使用LEFT JOIN连接两张表,再从此表中选出dept_no值为NULL对应的emp_no记录

1

2

3

SELECT emp_no FROM (SELECT * FROM employees LEFT JOIN dept_manager

ON employees.emp_no = dept_manager.emp_no)

WHERE dept_no IS NULL;

方法三:方法二的简版,使用单层SELECT语句即可

1

2

3

SELECT employees.emp_no FROM employees LEFT JOIN dept_manager

ON employees.emp_no = dept_manager.emp_no

WHERE dept_no IS NULL;

语法注释:

在牛客网的讨论中,出现了下面一种错误解法,有大神做了详细的错误解析:

select A.emp_no from employees A,dept_manager B where A.emp_no not in B.emp_no;

解析:
作者:wasrehpic
链接:https://www.nowcoder.com/questionTerminal/32c53d06443346f4a2f2ca733c19660c
来源:牛客网

本身存在语法错误,in关键字后要跟一组数据,而不是一个,要将B.emp_no用括号括起来才能正常运行;其次(B.emp_no)中没用到SELECT,所以(B.emp_no)只包含dept_manager表中第一条记录的emp_no,即10002,而不是包含了所有记录的emp_no。因此以上语句将会输出除10002外的所有emp_no。

11、获取所有员工当前的manager,如果当前的manager是自己的话结果不显示,当前表示to_date='9999-01-01'。结果第一列给出当前员工的emp_no,第二列给出其manager对应的manager_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`));

题解:

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 
WHERE dm.to_date = '9999-01-01' AND de.to_date = '9999-01-01' AND de.emp_no != dm.emp_no;

语法注释:

!=和 <>:两者在MySQL中都是不等于的意思;

本题应注意以下三点:

1、用 INNER JOIN 连接两张表,因为要输出自己的经理,得知自己与经理的部门要相同,故有限制条件 de.dept_no = dm.dept_no;部门一定有manager,员工不一定有manager,所以不用de.dept_no = dm.dept_no  ;

2、再用 WHERE 限制当前员工与当前经理的条件,即 dm.to_date 等于 '9999-01-01' 、de.to_date 等于 '9999-01-01' 、 de.emp_no 不等于 dm.emp_no;

3、为了增强代码可读性,将 dept_emp 用别名 de 代替,dept_manager 用 dm 代替,最后根据题意将 de.emp_no 用别名 manager_no 代替后输出;

12、获取所有部门中当前员工薪水最高的相关信息,给出dept_no, emp_no以及其对应的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`));

题解:

方法一:

SELECT d.dept_no ,d.emp_no, s.salary 
FROM salaries s  INNER JOIN dept_emp d
ON d.emp_no = s.emp_no  
WHERE d.to_date = '9999-01-01' AND s.to_date = '9999-01-01' 
GROUP BY  d.dept_no HAVING  max(s.salary);

方法二:

SELECT d.dept_no, s.emp_no, MAX(s.salary) AS salary
FROM salaries  s INNER JOIN dept_emp  d
ON d.emp_no = s.emp_no 
WHERE d.to_date = '9999-01-01' AND s.to_date = '9999-01-01'
GROUP BY d.dept_no;

语法注释:

此题的难点是在,如何将dept_no分组找出它对应的最高salary;

我们使用GROUP BY d.dept_no 将dept_no进行分组;同时需注意方法一中max(s.salary)要在GROUP BY 之后,如果放在前面的话排序的就不是我们的目标dept_no了而是整体的表;

 

方法二是采用的一个大神思路:

链接:https://www.nowcoder.com/questionTerminal/4a052e3e1df5435880d4353eb18a91c6
来源:牛客网
此题思路如下:

1、先用INNER JOIN连接两张表,限制条件是两张表的emp_no相同,即d.emp_no = s.emp_no;

2、选取每个员工当前的工资水平,用d.to_date = '9999-01-01' AND s.to_date = '9999-01-01'作条件限制,因为此表中每条最新记录的 to_date 都用 9999-01-01 表示;

3、用GROUP BY d.dept_no将每个部门分为一组,用MAX()函数选取每组中工资最高者;

4、将salaries用s代替,dept_emp用d代替,最后将MAX(s.salary)用salary代替后输出。

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

数据库表:
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);

题解:

SELECT title, COUNT(title) AS t FROM titles GROUP BY title HAVING t >= 2;

语法注释:


此题应注意以下三点:

1、用COUNT()函数和GROUP BY语句可以统计同一title值的记录条数

2、根据题意,输出每个title的个数为t,故用AS语句将COUNT(title)的值转换为t

3、由于WHERE后不可跟COUNT()函数,故用HAVING语句来限定t>=2的条件;

 

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

数据库表:
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);

题解:

SELECT title, COUNT(DISTINCT emp_no) AS t FROM titles GROUP BY title HAVING t >= 2;

语法注释:

此题应注意以下三点:

1、先用GROUP BY title将表格以title分组,再用COUNT(DISTINCT emp_no)可以统计同一title值且不包含重复emp_no值的记录条数

2、根据题意,输出每个title的个数为t,故用AS语句将COUNT(DISTINCT emp_no)的值转换为t

3、由于WHERE后不可跟COUNT()函数,故用HAVING语句来限定t>=2的条件

 

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

数据库表:
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`));

题解:

方法一:直接计算

SELECT * FROM employees WHERE emp_no %2=1  AND last_name != 'Mary' ORDER BY hire_date desc;

方法二:按位与

SELECT * FROM employees WHERE emp_no &1  AND last_name != 'Mary' ORDER BY hire_date desc;

语法注释:

参考文章:https://blog.youkuaiyun.com/zhazhagu/article/details/80452473

方法一:直接计算法,如果查找偶数则是,%2=0

方法二:按位与查找法

查询奇数的方法 &1:1的二进制是0001,与运算符:1 & 1 = 1, 1 & 0 = 0, 0 & 1 = 0, 0 & 0 = 0;所以可以保证最后一位是奇数。

查询偶数的方法 字段=(字段>>1)<<1:这是左移和右移运算符,先整除再乘,意思就是先整除2再乘以2,看前后结果是否一样,一样的就是偶数。

方法三:函数法--MOD(x,y) ,返回 x 除以 y 以后的余数。但是本方法是可行的,不过是在牛客网测试未通过。

SELECT * FROM employees WHERE MOD(emp_no, 2)=1  AND last_name != 'Mary' ORDER BY hire_date desc;

 

方法四:正则表达式 

SELECT * FROM employees WHERE emp_no REGEXP '[13579]$' AND last_name != 'Mary' ORDER BY hire_date desc;

[13579]:奇数字符集合。匹配所包含的任意一个字符。

aa$ :以aa结尾;

参考:http://www.runoob.com/mysql/mysql-regexp.html

本方法适用于查询身份证末尾有X,但是仍然需要查询出奇偶数的情况。

 

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

数据库表:
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);

题解:

SELECT t.title ,AVG(s.salary) 

FROM salaries s iINNER JOIN titles t

ON s.emp_no = t.emp_no

AND s.to_date = '9999-01-01' AND t.to_date ='9999-01-01'

GROUP BY t.title;

语法注释:

此题的思路如下:

1、先用INNER JOIN连接两张表,限制条件是两张表的emp_no相同,即s.emp_no = t.emp_no;

2、选取员工当前日期薪水对应的平均工资,所以根据之前的题目,默认当前日期是:s.to_date = '9999-01-01' AND t.to_date = '9999-01-01'作条件限制,因为此表中每条最新记录的 to_date 都用 9999-01-01 表示;

3、用GROUP BY t.title将title进行分组;

4、将salaries用s代替,titles用t代替;

5、计算平均薪资用AVG函数,AVG(s.salary);

 

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

数据库表:
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`));

题解:

SELECT emp_no , salary FROM salaries 
WHERE salary =

(SELECT DISTINCT salary FROm salaries WHERE to_date= '9999-01-01' ORDER BY salary desc LIMIT 1,1) ;

语法注释:

此题的思路如下:

1、当前日期是to_date = '9999-01-01'

2、员工的薪水第二多,需要先对薪水排序,降序排列OEDER BY salary desc,再找出排名第二的薪水LIMIT 1,1;

      LIMIT m,n : 表示从第m+1条开始,取n条数据;

      LIMIT n : 表示从第0条开始,取n条数据,是limit(0,n)的缩写。

3、考虑到薪水排名第一的员工可能有多个,使用DISTINCT去除重复数据;

 

关于LIMIT的详细用法可参考:https://www.jb51.net/article/125394.htm

 

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

数据库表:
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`));

题解:

SELECT e.emp_no, MAX(s.salary) AS salary, e.last_name, e.first_name 
FROM employees AS e INNER JOIN salaries AS s 
ON e.emp_no = s.emp_no
WHERE s.to_date = '9999-01-01'
AND s.salary NOT IN (SELECT MAX(salary) FROM salaries WHERE to_date = '9999-01-01');

语法注释:

本题做法很多,主要思想为多层SELECT嵌套与MAX()函数结合

1、先利用MAX()函数找出salaries中当前薪水最高者,即SELECT MAX(salary) FROM salaries WHERE to_date = '9999-01-01'

2、再利用INNER JOIN连接employees与salaries表,限定条件为【同一员工】e.emp_no = s.emp_no、【当前】s.to_date = '9999-01-01'与【非薪水最高】s.salary NOT IN (SELECT MAX(salary) FROM salaries WHERE to_date = '9999-01-01')

3、在以上限制条件下找薪水最高者,即为所有员工薪水的次高者

 

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

数据库表:
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`));

 

题解:

SELECT em.last_name, em.first_name, dp.dept_name
FROM (employees AS em LEFT JOIN dept_emp AS de ON em.emp_no = de.emp_no)
LEFT JOIN departments AS dp ON de.dept_no = dp.dept_no;

语法注释:

本题思路为运用两次LEFT JOIN连接嵌套

1、第一次LEFT JOIN连接employees表与dept_emp表,得到所有员工的last_name和first_name以及对应的dept_no,也包括暂时没有分配部门的员工

2、第二次LEFT JOIN连接上表与departments表,即连接dept_no与dept_name,得到所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工

20、查找员工编号emp_no为10001其自入职以来的薪水salary涨幅值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`));

题解:

SELECT ( 
(SELECT salary FROM salaries WHERE emp_no = 10001 ORDER BY to_date DESC LIMIT 1) -
(SELECT salary FROM salaries WHERE emp_no = 10001 ORDER BY to_date ASC LIMIT 1)
) AS growth;

语法注释:

本题严谨的思路如下:

1、先分别找到emp_no=10001的员工的第一次工资记录与最后一次工资记录

2、再将最后一次工资记录减去第一次工资记录得到入职以来salary的涨幅,最后用别名growth代替

 

21、查找所有员工自入职以来的薪水涨幅情况,给出员工编号emp_no以及其对应的薪水涨幅growth,并按照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`));

题解:

方法一:内层用LEFT JOIN,外层用INNER JOIN(内层也可以改用 INNER JOIN)

1

2

3

4

5

SELECT sCurrent.emp_no, (sCurrent.salary-sStart.salary) AS growth

FROM (SELECT s.emp_no, s.salary FROM employees e LEFT JOIN salaries s ON e.emp_no = s.emp_no WHERE s.to_date = '9999-01-01') AS sCurrent

INNER JOIN (SELECT s.emp_no, s.salary FROM employees e LEFT JOIN salaries s ON e.emp_no = s.emp_no WHERE s.from_date = e.hire_date) AS sStart

ON sCurrent.emp_no = sStart.emp_no

ORDER BY growth

方法二:内外层都用FROM并列查询

1

2

3

4

5

SELECT sCurrent.emp_no, (sCurrent.salary-sStart.salary) AS growth

FROM (SELECT s.emp_no, s.salary FROM employees e, salaries s WHERE e.emp_no = s.emp_no AND s.to_date = '9999-01-01') AS sCurrent,

(SELECT s.emp_no, s.salary FROM employees e, salaries s WHERE e.emp_no = s.emp_no AND s.from_date = e.hire_date) AS sStart

WHERE sCurrent.emp_no = sStart.emp_no

ORDER BY growth

方法三:

select a.emp_no, (b.salary - c.salary) as growth

from

    employees as a

    inner join salaries as b

    on a.emp_no = b.emp_no and b.to_date = '9999-01-01'

    inner join salaries as c

    on a.emp_no = c.emp_no and a.hire_date = c.from_date

order by growth asc

语法注释:

方法一、二解题思路:

本题思路是先分别用两次LEFT JOIN左连接employees与salaries,建立两张表,分别存放员工当前工资(sCurrent)与员工入职时的工资(sStart),再用INNER JOIN连接sCurrent与sStart,最后限定在同一员工下用当前工资减去入职工资。

 

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

数据库表:
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`));

题解:

SELECT de.dept_no, dp.dept_name, COUNT(s.salary) AS sum 
FROM (dept_emp AS de INNER JOIN salaries AS s ON de.emp_no = s.emp_no) 
INNER JOIN departments AS dp ON de.dept_no = dp.dept_no 
GROUP BY de.dept_no;

语法注释:

本题关键是要 每个部门分组,并分别统计工资记录总数,思路如下

1、用INNER JOIN连接dept_emp表和salaries表,并以dept_emp.no分组,统计每个部门所有员工工资的记录总数

2、再将上表用INNER JOIN连接departments表,限制条件为两表的dept_no相等,找到dept_no与dept_name的对应关系,最后依次输出dept_no、dept_name、sum

 

23、对所有员工的当前(to_date='9999-01-01')薪水按照salary进行按照1-N的排名,相同salary并列且按照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`));

题解:

语法注释:

(未完待更新)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值