「牛客网SQL实战二刷」是个系列学习笔记博文,每天解析6道SQL题目~ 今天是第19-24题~
每篇笔记的格式大致为,三大板块:
- 大纲
- 题目(题目描述、思路、代码、相关参考资料/答疑)
- 回顾
一、大纲
题号 | 知识点 |
---|---|
19 | LEFT JOIN |
20 | SELECT 嵌套 |
21 | SELECT 嵌套 |
22 | GROUP BY, COUNT() |
23 | 表的复用,DISTINCT,ORDER BY,GROUP BY |
24 | NOT IN |
二、题目
19. 查找所有员工的last_name和first_name以及对应的dept_name
- 题目描述
查找所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工
CREATE TABLEdepartments
(
dept_no
char(4) NOT NULL,
dept_name
varchar(40) NOT NULL,
PRIMARY KEY (dept_no
));
CREATE TABLEdept_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 TABLEemployees
(
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
));
- 输出描述
last_name | first_name | dept_name |
---|---|---|
Facello | Georgi | Marketing |
省略 | 省略 | 省略 |
Sluis | Mary | NULL |
- 思路
- 第一次LEFT JOIN employees表和dept_emp表,得到last_name 和first_name;
- 第二次LEFT JOIN departments表,得到dept_name;
- 因为查找所有员工,「也包括暂时没有分配部门的员工」,所以用LEFT JOIN。
- 代码
SELECT e.last_name, e.first_name, d.dept_name
FROM employees AS e LEFT JOIN dept_emp AS de ON e.emp_no = de.emp_no
LEFT JOIN departments AS d ON de.dept_no = d.dept_no
20.查找员工编号emp_no为10001其自入职以来的薪水salary涨幅值growth
- 题目描述
查找员工编号emp_no为10001其自入职以来的薪水salary涨幅值growth
CREATE TABLEsalaries
(
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
));
- 输出描述
growth |
---|
28841 |
-
思路
分别找到emp_no为10001员工当前薪水和入职薪水,做差即为growth -
代码
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 from_date ASC LIMIT 1)
)AS growth
21. 查找所有员工自入职以来的薪水涨幅情况
- 题目描述
查找所有员工自入职以来的薪水涨幅情况,给出员工编号emp_no以及其对应的薪水涨幅growth,并按照growth进行升序
CREATE TABLEemployees
(
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 TABLEsalaries
(
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
));
- 输出描述
emp_no | growth |
---|---|
10011 | 0 |
省略 | 省略 |
54496 | 10004 |
-
思路
-
产生两张表,一张记录当前薪水,一张记录入职薪水;
-
用emp_no连接两张表,对应salary做差表示growth。
-
代码
SELECT sCurrent.emp_no, (sCurrent.salary - sStart.salary) AS growth
FROM (SELECT e.emp_no, s.salary FROM employees AS e, salaries AS s WHERE e.emp_no = s.emp_no AND s.to_date = '9999-01-01') AS sCurrent,
(SELECT e.emp_no, s.salary FROM employees AS e, salaries AS s WHERE e.emp_no = s.emp_no AND e.hire_date = s.from_date) AS sStart
WHERE sCurrent.emp_no = sStart.emp_no
ORDER BY growth
22. 统计各个部门对应员工涨幅的次数总和
- 题目描述
统计各个部门对应员工涨幅的次数总和,给出部门编码dept_no、部门名称dept_name以及次数sum
CREATE TABLEdepartments
(
dept_no
char(4) NOT NULL,
dept_name
varchar(40) NOT NULL,
PRIMARY KEY (dept_no
));
CREATE TABLEdept_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 TABLEsalaries
(
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
));
- 输出描述
dept_no | dept_name | sum |
---|---|---|
d001 | Marketing | 24 |
d002 | Finance | 14 |
d003 | Human Resources | 13 |
d004 | Production | 24 |
d005 | Development | 25 |
d006 | Quality Management | 25 |
- 思路
- 先连接dept_emp表和salaries表,得到dept_no和emp_no;
- 再连接departments表,得到dept_name ;
- 用GROUP BY按照dept_no分组,用COUNT()计数。
- 代码
SELECT de.dept_no, d.dept_name, COUNT(s.emp_no) AS sum
FROM (dept_emp AS de INNER JOIN salaries AS s ON de.emp_no = s.emp_no)
INNER JOIN departments AS d ON de.dept_no = d.dept_no
GROUP BY de.dept_no
23. 对所有员工的薪水按照salary进行按照1-N的排名
- 题目描述
对所有员工的当前(to_date=‘9999-01-01’)薪水按照salary进行按照1-N的排名,相同salary并列且按照emp_no升序排列
CREATE TABLEsalaries
(
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
));
- 输出描述
emp_no | salary | rank |
---|---|---|
10005 | 94692 | 1 |
10009 | 94409 | 2 |
10010 | 94409 | 2 |
10001 | 88958 | 3 |
10007 | 88070 | 4 |
10004 | 74057 | 5 |
10002 | 72527 | 6 |
10003 | 43311 | 7 |
10006 | 43311 | 7 |
10011 | 25828 | 8 |
- 思路
- 复用salaries表进行排名比较;
- rank即为第二张表中有多少大于等于当前salary的个数,由于并列排名,应当使用DISTINCT去重;
- 按照emp_no分组;
- 按照salary逆序和emp_no升序排列。
- 代码
SELECT s1.emp_no, s1.salary, COUNT(DISTINCT s2.salary) AS rank
FROM salaries AS s1, salaries AS s2
WHERE s1.to_date = '9999-01-01' AND s2.to_date = '9999-01-01' AND s1.salary <= s2.salary
GROUP BY s1.emp_no
ORDER BY s1.salary DESC, s1.emp_no
24.获取所有非manager员工当前的薪水情况
- 题目描述
获取所有非manager员工当前的薪水情况,给出dept_no、emp_no以及salary ,当前表示to_date=‘9999-01-01’
CREATE TABLEdept_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 TABLEdept_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 TABLEemployees
(
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 TABLEsalaries
(
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
));
- 输出描述
dept_no | emp_no | salary |
---|---|---|
d001 | 10001 | 88958 |
d004 | 10003 | 43311 |
d005 | 10007 | 88070 |
d006 | 10009 | 95409 |
- 思路
- 连接dept_emp表和salaries表得到dept_no,emp_no和salary
- 「所有非manager员工」,因此要排除dept_manager表中的员工emp_no
- 代码
SELECT de.dept_no, de.emp_no, s.salary
FROM dept_emp AS de INNER JOIN salaries AS s ON de.emp_no = s.emp_no AND s.to_date = '9999-01-01'
WHERE de.emp_no NOT IN (SELECT emp_no FROM dept_manager)
三、回顾
知识点 | 题号 |
---|---|
LEFT JOIN | 19 |
ORDER BY | 23 |
表的复用 | 23 |
DISTINCT | 23 |
NOT IN | 24 |
函数 | 22「COUNT()」 |
SELECT 嵌套 | 20, 21 |
GROUP BY | 22, 23 |