#一.背景描述:
##来源:尚硅谷MySQL教学98课案例2
##问题:查询平均工资最低的部门信息
##附件:相关数据库 提取码:8r48
#二.问题分析:
该问题中平均工资涉及employees表,部门信息涉及departments表,
可将该问题按步骤拆分:
SELECT AVG(salary) ,department_id FROM employees GROUP BY department_id;
SELECT MIN(ag)
FROM (
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
) ag_dep;
③查询哪个部门的平均工资=②
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)=(
SELECT MIN(ag)
FROM (
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
) ag_dep);
注意:这里SELECT后面只能进行标量子查询,不能查询两列
④接着查询部门信息
SELECT d.*
FROM departments d
WHERE d.department_id IN(
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)=(
SELECT MIN(ag)
FROM (
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
) ag_dep));
##方式二:
①各部门平均给工资
SELECT AVG(salary) ,department_id FROM employees GROUP BY department_id;
②将①中结果升序排序,只取第一行中的部门编号信息
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1;
③查询部门信息
SELECT *
FROM departments d
WHERE d.department_id=(
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1);
评价:这种解法在应对小数据量时,特别是可以通过第一步人工预判平均工资最小部门只有一个时是可行的,也就是当前场景有效;但争议在于,假如数据有变动,这块逻辑就有漏洞;
##方式三:
其实结合方式二的逻辑实现MIN函数功能我们也可以有第三种代码方式,
这里与方式二的区别就在于WHERE后面支持列子查询(多行子查询)
SELECT d.*
FROM departments d
WHERE d.department_id IN(
SELECT department_id FROM employees GROUP BY department_id
HAVING AVG(salary)=(SELECT MIN(s.ag)
FROM(SELECT AVG(salary) ag FROM employees GROUP BY department_id) s));
#三.引申:
其它常见错误:
##1.对于MIN函数的错误使用
SELECT *
FROM departments
WHERE department_id =(
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) =(
SELECT MIN(AVG(salary))
FROM employees
GROUP BY department_id));
上述解法看似符合逻辑,但实际上无法实现,原因在于MySQL并不支持MIN(AVG(salary))这种语法,因此需要通过排序或子查询代替实现MIN;
##2.SELECT仅仅支持标量查询
SELECT MIN(ag),department_id
FROM (
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
) ag_dep;
上述写法看似可以得到MIN(ag)对应的department_id,实则不然,原有在于SELECT后面只能进行标量查询,并且SELECT str1,str2中str1和str2并无对应联系,实际上上述语法查询出的department_id是虚拟表自然排序的第一行的结果,而MIN(ag)确实为最低工资;
##3.采用连接查询
SELECT d.* ,(
SELECT AVG(salary) ag
FROM employees
WHERE d.department_id=ag_dep.department_id
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1
)ag_dep
FROM departments d;
类似这种的写法就是没有考虑到语句查询顺序,导致有部分字段程序无法识别
此处若要采用连接查询也可以实现,同时可查询最低工资,但是较为复杂:
SELECT d.*,MIN(ad.avgs) "最低部门平均工资"
FROM departments d
JOIN (
SELECT AVG(salary) avgs,department_id
FROM employees
GROUP BY department_id
)ad
ON d.`department_id`=ad.department_id
AND d.`department_id` IN(
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)=(
SELECT AVG(salary) ag
FROM employees
GROUP BY department_id
ORDER BY AVG(salary) DESC
LIMIT 1
)
);
#四.全文总结:
本题综合性较强,综合考察了对于MySQL语言中DQL语句的应用,重点需要理解DQL语句中子查询、分组查询、排序+分页查询、条件查询,以及各语句执行顺序、为虚拟表起别名等知识,举一反三,通过这种练习可以更好掌握SQL语法。