聚合(或聚集、分组)函数,它是对一组数据进行汇总的函数,输入的是一组数据的集合,输出的是单个值。
聚合函数作用于一组数据,并对一组数据返回一个值。
AVG / SUM :只适用于数值类型的字段(或变量)
SELECT AVG(salary),MAX(salary),MIN(salary),SUM(salary)
FROM employees;
MAX / MIN :适用于数值类型、字符串类型、日期时间类型的字段(或变量)
SELECT MAX(last_name),MIN(last_name),MAX(hire_date),MIN(hire_date)
FROM employees;
SELECT COUNT(*)
FROM employees
SELECT COUNT(commission_pct)
FROM employees;
#如果计算表中有多少条记录,如何实现?
#方式1:COUNT(*)
#方式2:COUNT(1)
#方式3:COUNT(具体字段) : 不一定对!-> 不会计数为Null数据
SELECT COUNT(employee_id),COUNT(salary),COUNT(2 * salary),COUNT(1),COUNT(2),COUNT(*),COUNT(commission_pct)
FROM employees ;
#公式:AVG = SUM / COUNT
SELECT AVG(salary),SUM(salary)/COUNT(salary),
AVG(commission_pct),SUM(commission_pct)/COUNT(commission_pct),
SUM(commission_pct) / 107
FROM employees;
#需求:查询公司中平均奖金率
#错误的!
SELECT AVG(commission_pct)
FROM employees;
#正确的:
SELECT SUM(commission_pct) / COUNT(IFNULL(commission_pct,0)),
AVG(IFNULL(commission_pct,0))
FROM employees;
如何需要统计表中的记录数,使用COUNT()、COUNT(1)、COUNT(具体字段) 哪个效率更高呢?*
如果使用的是MyISAM 存储引擎,则三者效率相同,都是O(1)
如果使用的是InnoDB 存储引擎,则三者效率:COUNT(*) = COUNT(1)> COUNT(字段)
可以使用GROUP BY
子句将表中的数据分成若干组
SELECT column, group_function(column)
FROM table
[WHERE condition]
[GROUP BY group_by_expression]
[ORDER BY column];
明确:WHERE一定放在FROM后面
SELECT department_id, AVG(salary)
FROM employees
GROUP BY department_id ;
包含在 GROUP BY 子句中的列不必包含在SELECT 列表中
SELECT AVG(salary)
FROM employees
GROUP BY department_id ;
SELECT department_id dept_id, job_id, SUM(salary),AVG(salary)
FROM employees
GROUP BY department_id, job_id ;
#错误的!
SELECT department_id,job_id,AVG(salary)
FROM employees
GROUP BY department_id;
#结论1:SELECT中出现的非组函数的字段必须声明在GROUP BY 中。
# 反之,GROUP BY中声明的字段可以不出现在SELECT中。
#结论2:GROUP BY 声明在FROM后面、WHERE后面,ORDER BY 前面、LIMIT前面
#结论3:MySQL中GROUP BY中使用WITH ROLLUP
#需求:查询各个部门的平均工资,按照平均工资升序排列
SELECT department_id,AVG(salary) avg_sal
FROM employees
GROUP BY department_id
ORDER BY avg_sal ASC;
使用WITH ROLLUP
关键字之后,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量。
SELECT department_id,AVG(salary)
FROM employees
WHERE department_id > 80
GROUP BY department_id WITH ROLLUP;
注意:
当使用ROLLUP时,不能同时使用ORDER BY子句进行结果排序,即ROLLUP和ORDER BY是互相排斥的。
过滤分组:HAVING子句
SELECT department_id, MAX(salary) max_sal
FROM employees
GROUP BY department_id
HAVING max_sal > 10000;
where 和 having 都能使用 但是 where不能作为分组的条件
非法使用聚合函数 : 不能在 WHERE 子句中使用聚合函数。
区别一:
区别二:
优点 | 缺点 | |
---|---|---|
WHERE | 先筛选数据再关联,执行效率高 | 不能使用分组中的计算函数进行筛选 |
HAVING | 可以使用分组中的计算函数 | 在最后的结果集中进行筛选,执行效率较低 |
结论:
当过滤条件中有聚合函数时,则此过滤条件必须声明在HAVING
中。
当过滤条件中没有聚合函数时,则此过滤条件声明在WHERE
中或HAVING
中都可以。但是,建议大家声明在WHERE
中。
SELECT department_id,MAX(salary)
FROM employees
WHERE department_id IN (10,20,30,40)
GROUP BY department_id
HAVING MAX(salary) > 10000;
/*
WHERE 与 HAVING 的对比
1. 从适用范围上来讲,HAVING的适用范围更广。
2. 如果过滤条件中没有聚合函数:这种情况下,WHERE的执行效率要高于HAVING
*/
#方式1:
SELECT ...,....,...
FROM ...,...,....
WHERE 多表的连接条件
AND 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,...
#方式2:
SELECT ...,....,...
FROM ... JOIN ...
ON 多表的连接条件
JOIN ...
ON ...
WHERE 不包含组函数的过滤条件
AND/OR 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,...
#其中:
#(1)from:从哪些表中筛选
#(2)on:关联多表查询时,去除笛卡尔积
#(3)where:从表中筛选的条件
#(4)group by:分组依据
#(5)having:在统计结果中再次筛选
#(6)order by:排序
#(7)limit:分页
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT...
2. SELECT 语句的执行顺序(在 MySQL 和 Oracle 中,SELECT 执行顺序基本相同):
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT
举例:
SELECT DISTINCT player_id, player_name, count(*) as num # 顺序 5
FROM player JOIN team ON player.team_id = team.team_id # 顺序 1
WHERE height > 1.80 # 顺序 2
GROUP BY player.team_id # 顺序 3
HAVING num > 2 # 顺序 4
ORDER BY num DESC # 顺序 6
LIMIT 2 # 顺序 7
在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个
虚拟表
,然后将这个虚拟表传入下一个步骤中作为输入。
需要注意的是,这些步骤隐含在 SQL 的执行过程中,对于我们来说是不可见的。
/*
#sql92语法:
SELECT ....,....,....(存在聚合函数)
FROM ...,....,....
WHERE 多表的连接条件 AND 不包含聚合函数的过滤条件
GROUP BY ...,....
HAVING 包含聚合函数的过滤条件
ORDER BY ....,...(ASC / DESC )
LIMIT ...,....
#sql99语法:
SELECT ....,....,....(存在聚合函数)
FROM ... (LEFT / RIGHT)JOIN ....ON 多表的连接条件
(LEFT / RIGHT)JOIN ... ON ....
WHERE 不包含聚合函数的过滤条件
GROUP BY ...,....
HAVING 包含聚合函数的过滤条件
ORDER BY ....,...(ASC / DESC )
LIMIT ...,....
*/
#SQL语句的执行过程:
#FROM ...,...-> ON -> (LEFT/RIGNT JOIN) -> WHERE -> GROUP BY -> HAVING -> SELECT -> DISTINCT ->
# ORDER BY -> LIMIT
#1.where子句可否使用组函数进行过滤?
-- 不能
#2.查询公司员工工资的最大值,最小值,平均值,总和
SELECT MAX(salary),MIN(salary),AVG(salary),SUM(salary)
FROM employees;
#3.查询各job_id的员工工资的最大值,最小值,平均值,总和
SELECT job_id,MAX(salary),MIN(salary),AVG(salary),SUM(salary)
FROM employees
GROUP BY job_id;
#4.选择具有各个job_id的员工人数( 每个job有多少员工人数
SELECT job_id,COUNT(job_id)
FROM employees
GROUP BY job_id WITH ROLLUP;
SELECT job_id, COUNT(*)
FROM employees
GROUP BY job_id;
# 5.查询员工最高工资和最低工资的差距(DIFFERENCE)
SELECT MAX(salary) - MIN(salary)
FROM employees;
# 6*.查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
SELECT manager_id , MIN(salary) AS min_sal
FROM employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id
HAVING min_sal > 6000
# 7*.查询所有部门的名字,location_id,员工数量和平均工资,并按平均工资降序
SELECT d.department_name,location_id,COUNT(employee_id),AVG(salary) AS avg_sal
FROM employees e
RIGHT JOIN departments d
ON e.department_id = d.department_id
GROUP BY d.department_name,location_id
ORDER BY avg_sal DESC;
# 8.查询部门名字,所有工种和最低工资
SELECT d.department_name,e.job_id,MIN(e.salary)
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
GROUP BY d.department_name,e.job_id