08:聚合函数
什么是聚合函数?
聚合函数是输入一组值,返回一个值的函数。
可以理解为以下的图片:
常见的聚合函数
-
AVG
平均值/SUM
总和这两个函数只可以传递数值类型。
SELECT AVG(salary),SUM(salary)/107 FROM employees
-
MAX
最大值/MIN
最小值这两个函数可以传递数值、字符串、日期等多种类型。
SELECT MIN(last_name),MAX(last_name),MAX(salary) FROM employees
-
COUNT
计算指定字段在表中出现的次数。
例一:计算表中的所有字段出现的次数:SELECT COUNT(*),COUNT(1) FROM employees # 不推荐使用 COUNT(字段名)——因为这样计算会自动忽略NULL值。
例二:计算平均值:
SUM / COUNT = AVG
# 计算commission_pct的平均值。commission_pct存在NULL值。 SELECT SUM(commission_pct)/COUNT(IFNULL(commission_pct,1)),AVG(IFNULL(commission_pct,0)) FROM employees
如何需要统计表中的记录数,使用COUNT()、COUNT(1)、COUNT(具体字段) 哪个效率更高呢?
如果使用的是MyISAM 存储引擎,则三者效率相同,都是O(1)
如果使用的是InnoDB 存储引擎,则三者效率:COUNT() = COUNT(1)> COUNT(字段)
-
GROUP BY
对数据进行分组
例一:查询每个部门的平均工资。要使用GROUP BY将各个部门进行分组。SELECT department_id,AVG(salary) FROM employees GROUP BY department_id
例二:查询每个部门并且同一工种的平均工资。要使用GROUP BY将各个部门中的各个工种进行多重分组。
SELECT department_id,job_id,AVG(salary) FROM employees GROUP BY department_id,job_id
注意:
一:SELECT中出现的非组(聚合)函数的字段必须声明在GROUP BY 中。如例二。
反之,GROUP BY中声明的字段可以不出现在SELECT中。二:GROUP BY 声明在FROM后面、WHERE后面,ORDER BY 前面、LIMIT前面。
三:GROUP BY中使用WITH ROLLUP,显示最后一条记录会不分组进行汇总。
当使用ROLLUP时,不能同时使用ORDER BY子句进行结果排序,即ROLLUP和ORDER BY是互相排斥的。
-
HAVING
过滤分组
- HAVING要在GROUP BY 之后使用
- 已经使用了聚合函数时,要用HAVING
- HAVING不单独使用,和GROUP BY 一起使用
例一:查询各个部门中最高工资比10000高的部门信息
SELECT department_id,MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary)>10000
WHERE和HAVING的区别:
- WHERE可以使用表中的字段作为筛选条件,但不能使用聚合函数作为筛选条件。HAVING必须与GROP BY配合使用,可以把聚合函数作为筛选条件。
- 直接使用表中的字段作为筛选条件时,使用WHERE要优于HAVING。因为WHERE执行顺序优于HAVING,先筛选数据在进行GROUP UP。这样可以用一个较小的数据集进行分组。提高效率。
SELECT的执行过程
SELECT ...,... #5
FROM ... JOIN ... #1
ON 多表的连接条件
JOIN ...
ON ...
WHERE 不包含组函数的过滤条件 #2
GROUP BY ... #3
HAVING 包含组函数的过滤条件 #4
ORDER BY ... ASC/DESC #6
LIMIT ...,... #7
执行过程:
FROM ...,...-> ON ->
(LEFT/RIGNT JOIN) ->
WHERE ->
GROUP BY -> HAVING ->
SELECT -> DISTINCT(去重) ->
ORDER BY -> LIMIT
- SQL的执行原理
FROM
SELECT 是先执行 FROM
这一步的。在这个阶段,如果是多张表联查,还会经历下面的几个步骤:
- 首先先通过
CROSS JOIN
求笛卡尔积,相当于得到虚拟表 vt(virtual table)1-1; - 通过
ON
进行筛选,在虚拟表 vt1-1 的基础上进行筛选,得到虚拟表 vt1-2; - 添加外部行。如果我们使用的是
左连接、右链接或者全连接
,就会涉及到外部行,也就是在虚拟表 vt1-2 的基础上增加外部行,得到虚拟表 vt1-3。
当然如果我们操作的是两张以上的表,还会重复上面的步骤,直到所有表都被处理完为止。这个过程得到是我们的原始数据。
WHERE
当我们拿到了查询数据表的原始数据,也就是最终的虚拟表 vt1 ,就可以在此基础上再进行 WHERE
阶段 。在这个阶段中,会根据 vt1 表的结果进行筛选过滤,得到虚拟表 vt2 。
GROUP–>HAVING
然后进入第三步和第四步,也就是 GROUP
和 HAVING
阶段 。在这个阶段中,实际上是在虚拟表 vt2 的基础上进行分组和分组过滤,得到中间的虚拟表 vt3 和 vt4 。
SELECT
当我们完成了条件筛选部分之后,就可以筛选表中提取的字段,也就是进入到 SELECT 和 DISTINCT 阶段 。
首先在 SELECT
阶段会提取想要的字段,然后在 DISTINCT 阶段过滤掉重复的行,分别得到中间的虚拟表vt5-1 和 vt5-2 。
ORDER BY
当我们提取了想要的字段数据之后,就可以按照指定的字段进行排序,也就是 ORDER BY
阶段 ,得到虚拟表 vt6 。
LIMIT
最后在 vt6 的基础上,取出指定行的记录,也就是 LIMIT
阶段 ,得到最终的结果,对应的是虚拟表vt7 。