[牛客数据库SQL实战] 11~20题及个人解答

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

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

-- dept_emp部门员工表找到员工emp_no对应的部门dept_no, dept_manager部门管理表再找到管理者emp_no
SELECT de.emp_no, dm.emp_no AS manager_no 
FROM dept_emp AS de, dept_manager AS dm 
WHERE de.emp_no!=dm.emp_no AND dm.dept_no=de.dept_no 
AND de.to_date="9999-01-01" AND dm.to_date="9999-01-01";

运行时间:30ms

占用内存:3464k

-- 使用内连接INNER JOIN关联部门员工表和管理员表
SELECT de.emp_no, dm.emp_no AS manager_no 
FROM dept_emp AS de 
INNER JOIN dept_manager AS dm 
ON dm.dept_no=de.dept_no 
WHERE de.emp_no!=dm.emp_no 
AND de.to_date="9999-01-01" AND dm.to_date="9999-01-01";

运行时间:16ms

占用内存:3344k

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

当前(to_date="9999-01-01")各部门薪水最高员工
注意:该题目和审核系统是有坑的...

SELECT re1.dept_no, re2.emp_no, re1.salary
FROM (
	-- 先找出各部门最高薪水值 通过部门编号分组
	SELECT dept_em.dept_no, MAX(em_sa.salary) AS salary
	FROM (
		SELECT emp_no, salary
		FROM salaries
		WHERE to_date = '9999-01-01'
	) AS em_sa
	LEFT JOIN (
		SELECT emp_no, dept_no
		FROM dept_emp
		WHERE to_date = '9999-01-01'
	) AS dept_em 
	ON em_sa.emp_no = dept_em.emp_no
	GROUP BY dept_no) re1
JOIN (
	-- 再找出各部门员工的薪水值 通过左连接
    SELECT dept_em.dept_no, em_sa.emp_no, em_sa.salary AS salary
    FROM (
		SELECT emp_no, salary
		FROM salaries
		WHERE to_date = '9999-01-01'
	) AS em_sa
	LEFT JOIN (
		SELECT emp_no, dept_no
		FROM dept_emp
		WHERE to_date = '9999-01-01'
	) AS dept_em 
	ON em_sa.emp_no = dept_em.emp_no) re2 
-- 通过连接获得最高薪水值对应的员工编号
ON re1.dept_no = re2.dept_no AND re1.salary = re2.salary
ORDER BY re1.dept_no;

运行时间:20ms

占用内存:5076k

  • 分析SQL代码:
    第一种解法: 6.67's
-- 通过to_date缩减需要选取的数据 
-- 当前salaries表的emp_no和salary 
SELECT emp_no, salary FROM salaries WHERE to_date='9999-01-01';
-- 当前dept_emp表的dept_no和emp_no
SELECT dept_no, emp_no FROM dept_emp WHERE to_date='9999-01-01';
-- 通过部门编号分组, 当前各部门的最高薪水salary和部门编号dept_no
SELECT dm.dept_no, MAX(se.salary) salary
FROM (
	(
		SELECT emp_no, salary FROM salaries WHERE to_date='9999-01-01'
	) AS se
	LEFT JOIN
	(
		SELECT dept_no, emp_no FROM dept_emp WHERE to_date='9999-01-01'
	) AS dm
	ON se.emp_no=dm.emp_no)
GROUP BY dm.dept_no;
-- 无需分组, 将部门/员工/薪水合并到一个表内
SELECT dm.dept_no, dm.emp_no, se.salary
FROM (
	(
		SELECT emp_no, salary FROM salaries WHERE to_date='9999-01-01'
	) AS se
	LEFT JOIN
	(
		SELECT dept_no, emp_no FROM dept_emp WHERE to_date='9999-01-01'
	) AS dm
	ON se.emp_no=dm.emp_no);
-- 通过`部门/最高薪水`的条件 去筛选/定位 `员工编号`
SELECT re1.dept_no, re2.emp_no, re1.salary
FROM (
	SELECT dm.dept_no, MAX(se.salary) salary
	FROM (
		(
			SELECT emp_no, salary FROM salaries WHERE to_date='9999-01-01'
		) AS se
		LEFT JOIN
		(
			SELECT dept_no, emp_no FROM dept_emp WHERE to_date='9999-01-01'
		) AS dm
		ON se.emp_no=dm.emp_no)
	GROUP BY dm.dept_no
) AS re1 JOIN (
	SELECT dm.dept_no, dm.emp_no, se.salary
	FROM (
		(
			SELECT emp_no, salary FROM salaries WHERE to_date='9999-01-01'
		) AS se
		LEFT JOIN
		(
			SELECT dept_no, emp_no FROM dept_emp WHERE to_date='9999-01-01'
		) AS dm
		ON se.emp_no=dm.emp_no)
) AS re2
ON re2.dept_no=re1.dept_no AND re2.salary=re1.salary
ORDER BY re1.dept_no;

第二种解法: 5.93's 结果是一样的,但无法通过牛客的审核

SELECT de.dept_no,sa.emp_no,re.sal AS salary
FROM (
    -- 通过分组,拿到各部门编号以及对应的最高薪水
    SELECT d.dept_no, MAX(s.salary) AS sal
    FROM dept_emp AS d JOIN salaries AS 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
) AS re, dept_emp AS de, salaries AS sa
-- 三表查询 re的最高薪水 部门员工表的部门编号 薪水表的员工编号
WHERE re.dept_no=de.dept_no AND de.emp_no=sa.emp_no AND re.sal=sa.salary;

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

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

运行时间:18ms

占用内存:3424k

  • 注意:where和having的不同之处在于,where是查找之前的限定,而having是查找之后

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

注意对于重复的emp_no进行忽略

-- 统计拥有相同 title 的不同员工的个数 distinct可以放入函数体内
SELECT title, COUNT(DISTINCT emp_no) AS t FROM titles GROUP BY title HAVING t >= 2;

运行时间:17ms

占用内存:3300k

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

-- 判断属性奇偶数 1)取余 2)除与2再乘与2 3)位运算
SELECT * FROM employees WHERE emp_no%2=1 AND last_name!='Mary' ORDER BY hire_date DESC;

运行时间:20ms

占用内存:3400k

mysql判断奇数偶数,并思考效率

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

-- 注意avg是sql语法的关键字,需要``对其进行标注
SELECT t.title, AVG(s.salary) AS `avg` FROM titles AS t INNER JOIN salaries AS s ON s.emp_no=t.emp_no AND s.to_date='9999-01-01' AND t.to_date='9999-01-01' GROUP BY title;

运行时间:28ms

占用内存:3404k

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

-- 将salaries表按薪水salary逆序排列,再选取第二位
SELECT emp_no, salary FROM salaries WHERE to_date='9999-01-01' ORDER BY salary DESC LIMIT 1,1;

运行时间:22ms

占用内存:3320k

-- 以上方法是有缺陷的,题意是指薪水第二多,而同一薪水可能有多名员工
-- 先求出当前第二多薪水数额(distinct和group by都可以)
SELECT DISTINCT salary FROM salaries WHERE to_date='9999-01-01' ORDER BY salary DESC LIMIT 1,1;
SELECT salary FROM salaries WHERE to_date='9999-01-01' GROUP BY salary ORDER BY salary DESC LIMIT 1,1;
-- 再通过当前时间和薪水数额定位员工编号
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
) AND to_date='9999-01-01';

运行时间:22ms

占用内存:3552k

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

-- 先找出当前最多薪水数额
SELECT MAX(salary) FROM salaries WHERE to_date='9999-01-01';
-- 使用排除法,在去掉最大值的序列中,此时最大值就原序列的第二大值
SELECT MAX(salary) FROM salaries 
WHERE to_date='9999-01-01' AND salary NOT IN (
	SELECT MAX(salary)  FROM salaries WHERE to_date='9999-01-01'
);
-- 使用以上求取得到的第二大值
SELECT s.emp_no, s.salary, e.last_name, e.first_name 
FROM salaries AS s INNER JOIN employees AS e ON s.emp_no=e.emp_no 
WHERE s.to_date='9999-01-01'
AND s.salary = (
	SELECT MAX(salary) FROM salaries 
	WHERE to_date='9999-01-01' AND salary NOT IN (
		SELECT MAX(salary)  FROM salaries WHERE to_date='9999-01-01'
	)
);

运行时间:24ms

占用内存:3304k

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

-- 先将员工表employees和部门员工表dept_emp连接 合成部门员工详细表ds
SELECT e.emp_no, de.dept_no FROM employees AS e LEFT JOIN dept_emp AS de ON e.emp_no=de.emp_no;
-- 再将部门信息表department和部门员工详细表ds连接
SELECT ds.last_name, ds.first_name, d.dept_name FROM (
	SELECT e.last_name, e.first_name, de.dept_no FROM employees AS e LEFT JOIN dept_emp AS de ON e.emp_no=de.emp_no
) AS ds LEFT JOIN departments AS d ON d.dept_no=ds.dept_no;

运行时间:18ms

占用内存:3312k

-- 第一次 left join 是把未分配部门的员工算进去了,但是只得到了部门编号dept_no,没有部门名dept_name,
-- 所以第二次也要 left join 把含有部门名 departments表 连接起来,
-- 否则在第二次连接时就选不上未分配部门的员工了
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;

运行时间:20ms

占用内存:3404k

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

个人理解为:获得入职时的工资距离最新的工资的涨幅情况

-- 最大值-最小值 这种解法是有问题的 其是以薪水一直在涨/上升的前提下
SELECT (MAX(salary)-MIN(salary)) AS growth FROM salaries WHERE emp_no='10001';

运行时间:19ms

占用内存:3424k

-- 薪水salary涨幅值growth 当前薪水-入职薪水
SELECT (ma.salary-mi.salary) AS growth
FROM (
        -- 入职薪水
	SELECT salary FROM salaries WHERE emp_no='10001' ORDER BY to_date LIMIT 0,1
) AS mi, (
        -- 当前薪水
	SELECT salary FROM salaries WHERE emp_no='10001' ORDER BY to_date DESC LIMIT 0,1
) AS ma;

运行时间:21ms

占用内存:3688k

完整的个人练习代码

我的练习SQL代码已经上传至Github:https://github.com/slowbirdoflsh/newcode-sql-practice
仅供参考~~~

posted @ 2019-07-18 23:28  slowbird  阅读(362)  评论(0编辑  收藏  举报