MySQL 08 章——聚合函数
聚合函数是对一组数据进行汇总的函数,输入的是一组数据的集合,输出的是单个值
一、聚合函数介绍
(1)AVG和SUM函数
- 举例:
- 只适用于数值类型的字段(或变量)
- AVG函数和SUM函数在计算空值时,会自动把NULL过滤掉
(2)MIN和MAX函数
- 举例:
- 适用于数值类型、字符串类型、日期时间类型的字段(变量)
(3)COUNT函数
- 举例:
- 作用:计算指定字段在查询结构中出现的个数,如果值为NULL就不会被计算进去
- 如果计算表中有多少条记录,如何实现?
- COUNT(*)
- COUNT(1)
- COUNT(具体字段):不一定对!因为字段中的NULL值不会被计算进去
- AVG = SUM / COUNT,因为这三个函数都过滤掉了NULL值,所以
- 需求:查询公司中的平均奖金率
- 如果需要统计表中的记录数,使用COUNT(*)、COUNT(1)、COUNT(具体字段)哪个效率更高呢?
- 如果使用的是MyISAM存储引擎,则三者的效率相同,都是O(1)
- 如果使用的是InnoDB存储引擎,则三者效率:COUNT(*) = COUNT(1) > COUNT(具体字段)
二、GROUP BY
(1)基本使用
- 需求:查询各个部门的平均工资、最高工资
(2)使用多个列分组
- 需求:查询各个部门中,各个工种的平均工资
- 注意:SELECT中出现的非聚合函数的字段,必须声明在GROUP BY中;反之,GROUP BY中声明的字段可以不出现在SELECT中
- GROUP BY声明在FROM后面,WHERE后面,ORDER BY前面,LIMIT前面
(3)GROUP BY中使用WITH ROLLUP
- 使用WITH ROLLUP关键字之后,在所有查询出的记录之后,增加了一条记录。该记录就是汇总行
- 当使用WITH ROLLUP时,不能同时使用ORDER BY子句进行结果排序,即WITH ROLLUP和ORDER BY是相互排斥的
三、HAVING
(1)基本使用
- HAVING的作用:是用来过滤数据的
- 练习:查询各个部门中最高工资比10000高的部门信息
- 如果过滤条件中使用了聚合函数,则必须用HAVING来替换WHERE,否则报错
- HAVING必须声明在GROUP BY的后面
- 开发中,使用HAVING的前提是使用了GROUP BY
(2)WHERE和HAVING的对比
- 练习:查询部门id为10,20,30,40这4个部门中最高工资比10000高的部门信息(两种方式)
- 推荐使用方式一,执行效率高于方式二
- 当过滤条件中有聚合函数时,则此过滤条件必须声明在HAVING中。当过滤条件中没有聚合函数时,则此过滤条件声明在HAVING或WHERE中都可以。但是建议声明在WHERE中,效率更高
- WHERE和HAVING对比:
- 从适用范围上来讲,HAVING的适用范围更广
- 如果过滤条件中没有聚合函数,这种情况下,WHERE的效率要高于HAVING
四、SELECT的执行过程
(1)查询的结构
- sql92语法:
SELECT ...,...,...(存在聚合函数) FROM ...,...,... WHERE 多表的连接条件 AND 不包含聚合函数的过滤条件 GROUP BY ...,... HAVING 包含聚合函数的过滤条件 ORDER BY ...,...(ASC/DESC) LIMIT ...,...
- sql99语法:
SELECT ...,...,...(存在聚合函数) FROM ...(LEFT / RIGHT)JOIN...ON 多表的连接条件 JOIN...ON 多表的连接条件 WHERE 不包含聚合函数的过滤条件 GROUP BY ...,... HAVING 包含聚合函数的过滤条件 ORDER BY ...,...(ASC/DESC) LIMIT ...,...
(2)SELECT执行顺序
- FROM ...JOIN...,先对两张表进行笛卡尔积。ON用来去掉不应该关联的数据
- 关注左外连接和右外连接
- WHERE过滤数据
- 对过滤后的数据进行分组(GROUP BY),然后执行HAVING
- SELECT选出字段
- ORDER BY排序,然后分页查询
(3)SQL的执行原理
-
SELECT 是先执行 FROM 这一步的。在这个阶段,如果是多张表联查,还会经历下面的几个步骤:
(1)首先先通过 CROSS JOIN 求笛卡尔积,相当于得到虚拟表 vt(virtual table)1-1
(2)通过 ON 进行筛选,在虚拟表 vt1-1 的基础上进行筛选,得到虚拟表 vt1-2
(3)添加外部行。如果我们使用的是左连接、右链接或者全连接,就会涉及到外部行,也就是在虚拟表 vt1-2 的基础上增加外部行,得到虚拟表 vt1-3
(4)当然如果我们操作的是两张以上的表,还会重复上面的步骤,直到所有表都被处理完为止。这个过程得到是我们的原始数据 -
当我们拿到了查询数据表的原始数据,也就是最终的虚拟表 vt1,就可以在此基础上再进行 WHERE 阶段。在这个阶段中,会根据 vt1 表的结果进行筛选过滤,得到虚拟表 vt2
-
然后进入第三步和第四步,也就是 GROUP 和 HAVING 阶段。在这个阶段中,实际上是在虚拟表 vt2 的基础上进行分组和分组过滤,得到中间的虚拟表 vt3 和 vt4
-
当我们完成了条件筛选部分之后,就可以筛选表中提取的字段,也就是进入到 SELECT 和 DISTINCT 阶段。首先在 SELECT 阶段会提取想要的字段,然后在 DISTINCT 阶段过滤掉重复的行,分别得到中间的虚拟表 vt5-1 和 vt5-2
-
当我们提取了想要的字段数据之后,就可以按照指定的字段进行排序,也就是 ORDER BY 阶段,得到虚拟表 vt6
-
最后在 vt6 的基础上,取出指定行的记录,也就是 LIMIT 阶段,得到最终的结果,对应的是虚拟表 vt7
-
当然我们在写 SELECT 语句的时候,不一定存在所有的关键字,相应的阶段就会省略
五、课后练习
- 查询公司员工工资的最大值、最小值、平均值、总和
SELECT MAX(salary),MIN(salary),AVG(salary),SUM(salary) FROM employees;
- 查询各job_id的员工工资的最大值、最小值、平均值、总和
SELECT job_id,MAX(salary),MIN(salary),AVG(salary),SUM(salary) FROM employees GROUP BY job_id;
- 选择具有各个job_id的员工人数
SELECT job_id,COUNT(*) "workers" FROM employees GROUP BY job_id;
- 查询员工最高工资和最低工资的差距(DIFFERENCE)
SELECT MAX(salary) - MIN(salary) "DIFFERENCE" FROM employees;
- 查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
SELECT manager_id,MIN(salary) FROM employees WHERE manager_id IS NOT NULL GROUP BY manager_id HAVING MIN(salary) >= 6000;
- 查询所有部门的名字,location_id,员工数量和平均工资,并按平均工资降序
SELECT department_name,location_id,COUNT(*),AVG(salary) avg_sal FROM departments d LEFT OUTER JOIN employees e ON d.department_id = e.department_id GROUP BY department_name,location_id#select中出现的非聚合函数的字段,必须出现在group by中 ORDER BY avg_sal DESC;
- 查询每个部门的部门名,以及它对应工种的工种名和该工种的最低工资
SELECT department_name,job_id,MIN(salary) FROM departments d LEFT OUTER JOIN employees e ON d.department_id = e.department_id GROUP BY department_name,job_id;
该笔记根据尚硅谷的MySQL课程整理