单表查询----聚合函数
-
普通函数输入是一列,输出也是一列,输出列中的行与输出列的行,作用于列的每一个单元格,聚合函数以列输入,但是输出结果列只有一行。
-
聚合函数是汇总处理数据,不是像SELECT一样的检索数据。比如求和、求平均值。
聚合函数–参数是列名
(1)计数函数 COUNT(列名)
COUNT(*) 确定表中行的数目
COUNT(1) 确定表中第一列的行的数目。如果表中没有NULL,那么任何一列的行数目是一样的
COUNT(col) 确定col这一列有多少行,只作用非NULL行,但是不可以计数多列,即参数只能是一个列名
COUNT(DISTINCT col) 对行去重计数
注:
- COUNT是唯一可以用*的聚合函数。
- COUNT(*)
(col表示列,column) - COUNT忽略NULL,比如实际有8行,其中一行是NULL,那么COUNT的结果是7.
- 尽量不要再一个语句中写多个COUNT,比如尽量不要这样写:
SELECT
COUNT(m.prod_name)
,COUNT(m.saleprice)
FROM milk_tea AS m;
说是会出现问题??还没试出什么问题。。。
(2)求和函数 SUM
指定列的和:SUM(col)
具体用法:
- 区别于直接四则运算;因为四则运算是不能做有空值的运算,如果有空值,那么四则运算的结果就是空值,而SUM求和是直接跳过空值,如果有空值,就忽略空值,计算其他所有非空值的和。
- 只对数值型作用;
- 可以同时操作多个列;
- 可以作用于计算表达式,指的是参数可以是列名,参数也可以是列名之间的计算
(3)均值函数 AVG
指定列的平均值:AVG(col)
具体用法:
- 分母不对NULL计数,即空值被过滤掉,即列的SUM值/列的COUNT值(分子也不对NULL行计数)
- 可以同时操作多个列
(4)最大值函数 MAX
指定列的最大值:MAX(col)
具体用法:
-
适用于数值型、文本型、日期型,其中文本型也是可以比较最大值最小值的,只是用的比较少
-
要求该列可以排序,文本数据排序考虑的是编码的问题
-
忽略NULL,因为NULL没法比较大小
-
DISTINCT无意义, DISTINCT指的是去重,但是不论去重不去重,最大值依然是最大值,所以结果是一样的。
(5)最小值函数 MIN
指定列的最小值:MIN(col)
具体用法:
-
适用于数值型、文本型、日期型,其中文本型也是可以比较最大值最小值的,只是用的比较少
-
要求该列可以排序,文本数据排序考虑的是编码的问题
-
忽略NULL,因为NULL没法比较大小
-
DISTINCT无意义, DISTINCT指的是去重,但是最大值依然是最大值,所以结果是一样的。
Navicat实操
1. count
SELECT
COUNT(*) #这个表格的行数
, COUNT(1) #第一列的行数
, COUNT(3) #第三列的行数
FROM milk_tea AS m;
SELECT
COUNT(m.pro_date) #pro_date列的行数
, COUNT(m.prod_name) #prod_name列的行数
FROM milk_tea AS m;
#m.pro_date有一个空值
#最好不要一个语句中写两个COUNT函数
SELECT
COUNT(m.net_w)
, COUNT(DISTINCT m.net_w) #先对net_w列去重,再计数该列的行数
FROM milk_tea AS m;
#DISTINCT去重计数
#最好不要一个语句中写两个COUNT函数,可能会出错
SELECT DISTINCT m.sale_price FROM milk_tea AS m ;
# DISTINCT作用于所有列。去重
SELECT COUNT(DISTINCT m.sale_price) FROM milk_tea AS m;
# sale_price中 8行,有一行是NULL,DISTINCT去重之后NULL保留,但是计数函数会忽略NULL,所以最后计数仍然是跳过NULL来计数。
2.SUM
SELECT SUM(m.in_price) FROM milk_tea AS m;
SELECT SUM(m.in_price * 1.1) FROM milk_tea AS m;
#进价提升10%
SELECT SUM(m.sale_price) FROM milk_tea AS m;
SELECT SUM(m.sale_price-m.in_price) FROM milk_tea AS m;
#1,结果会忽略sale_price为NULL和in_price为NULL的值,原表中saleprice有一个空值。
#因为**四则运算是不对空值行做计算**,如果有空值,那么四则运算的结果就是空值
#SUM(m.sale_price-m.in_price)计算了7对值的差,然后求和
SELECT SUM(m.sale_price) - SUM(m.in_price) FROM milk_tea AS m;
#2,计算时不会忽略NULL为空的值
#SUM求和是直接跳过空值,如果有空值,就忽略空值,计算其他所有非空值的和。SUM(m.sale_price)计算了7个值的和,SUM(m.in_price)计算了8个值的和
#如果对NULL做一个处理、
SELECT SUM(IFNULL(m.sale_price,0) - m.in_price)FROM milk_tea AS m;
#3,结果与2相同,因为跳过NULL求和与将NULL变成0求和结果是一样的
#IFNULL(m.sale_price,0)是将NULL值变成0,参与四则运算后,再参与求和运算
解释:
3与2的结果相同,因为
SUM(IFNULL(m.sale_price,0) - m.in_price)
=SUM(m.sale_price) - SUM( m.in_price)
3.AVG
SELECT AVG(m.sale_price) FROM milk_tea AS m; #1
SELECT SUM(m.sale_price)/COUNT(m.sale_price) FROM milk_tea AS m;#2,与结果1一样
SELECT SUM(m.sale_price)/COUNT(1) FROM milk_tea AS m;#3,与结果12不一样,
#因为sale_price有空值,再SUM和COUNT中都是直接跳过NULL,而第一列是prod_id没有空值,所以两者的行数不同
#所以求均值时,分母的COUNT一定要写整列的列名,否则有可能出错
解释:
SUM和COUNT中都是直接跳过NULL
注:求均值时,如果用SUM/COUNT公式,那么**COUNT参数一定要写整列的列名,**否则一旦有NULL出现用其他的行COUNT就会出错。
4.先过滤后聚合
SELECT
SUM(m.sale_price)
FROM milk_tea AS m
WHERE m.net_w = '100g' ;
#是先过滤后聚合,这条规则同样适用于AVG、MIN、MAX
执行顺序是:
确定表–过滤表–选取展示列
FROM–WHERE–SELECT
5. MIN(),MAX()
SELECT MIN(m.sale_price) , MIN(DISTINCT m.sale_price) FROM milk_tea AS m; #说明DISTINCT再MIN、MAX中不起作用
SELECT MAX(m.sale_price) FROM milk_tea AS m;
6. 一行写多个聚合函数
SELECT
COUNT(m.sale_price)
, MIN(m.sale_price)
, MAX(m.sale_price)
, SUM(m.sale_price)
, AVG(m.sale_price)
FROM milk_tea AS m;
#如果有两个条件,分别聚合之后的得到两个结果,怎么放在一起呈现?
举例:重量是100g的一组,重量150g一组,对他们分组进行聚合,然后同时呈现结果,如何做?用到分组数据。
分组数据
1. 原理
数据分组,再在各组中进行聚合。分组的目的是聚合,所以分组函数与聚合函数同时出现。
2. 语句结构
SELECT … FROM … (这是必需子句)
WHERE +条件 (这是过滤子句)
GROUP BY +聚合键(这是分组子句)
注:SELECT子句中组成 包括聚合键、聚合函数、常数(作修饰)
3. 分组关键字
GROUP BY +分组子句(聚合键)
解释:聚合键就是要分组的列,即GROUP BY +指定列
比如按照重量进行分组,100g与150g两个标准分组,那么重量这组net_wet就是聚合键
4. 结果
(1)是多行数据
(2)一行数据代表一个分组
(3)SELECT展示的列必须与分组对应,聚合键决定分组标准,那么SELECT展示的列 要有聚合键所在列、聚合函数作用得到的结果列
(4)NULL值单独成为一个分组
(5)聚合键尽量不要出现别名,指的是用SELECT可以对列名检索呈现时起一个别名,但是聚合键不要起别名,因为后面要用
聚合函数:是为了达到聚合目的,比如求和、求均值
Navicat操作
(1)直接分组聚合
SELECT
m.net_w
, SUM(m.sale_price)
FROM milk_tea AS m
GROUP BY m.net_w;
解释:
(1)SELECT后面第一个是聚合键,即以net_w列进行聚合(这是分类标准),第二个是聚合函数SUM,是对sale_price进行聚合(这是每个类别的聚合目标)。
(相当于看net_w中有几类,对每一类中的值进行SUM聚合)
(2)GROUP BY 后面跟的是聚合键net_w,意思是按照聚合键net_w进行分组,比如聚合键net_w中去重后有3类,那么每一类就是一组。
(2)先过滤,后分组聚合
SELECT
m.net_w
, SUM(m.sale_price)
FROM milk_tea AS m
WHERE m.net_w IN ('100g','150g')
GROUP BY m.net_w;
解释:
增加了过滤语句WHERE,运行原理是先对结果进行过滤,过滤完后,再根据100g和150g这两类进行聚合分组求和。
SELECT
m.net_w
, COUNT(m.sale_price)
FROM milk_tea AS m
WHERE m.net_w IN ('100g','150g')
GROUP BY m.net_w;
解释:运行原理是先对结果进行100g和150g的过滤,过滤完后,再聚合分组,即分别对每组进行COUNT计数。
其他的聚合函数都一样,注意每个部分的位置。
(3)先聚合,后过滤?
比如我想看每种重量net_w下的总价格,对这个价格满足一定的条件进行过滤怎么做?分组过滤
-------分组过滤
1.分组过滤
将数据分组,各组中进行聚合。对聚合结果再次筛选(即过滤),得到所需分组。
注:再次筛选的语句一定在GROUP BY 之后。
2.结构
SELECT…
FROM…
WHERE + 过滤条件
GROUP BY + 聚合键
HAVING +聚合函数(这是分组筛选子句);
示例:
SELECT brand,
COUNT(*)
FROM milk_tea
WHERE cost > 5
GROUP BY brand
HAVING COUNT(*) > =2 ;
解释:
(1)WHERE 指定”行“的条件,HAVING 指定”组“的条件。
即先WHERE 对整张表进行过滤,再GROUP BY对过滤的结果进行分组,再HAVING对分组结果进行筛选,结果是只剩下满足条件的组。
(2)分组之前排除的行不参与分组,意思是WHERE是分组之前进行的过滤,利用WHERE过滤掉的行便不再参与GROUP BY 的分组和HAVING的分组过滤。
Q:聚合键到底写在哪里?
A:首先,明确分组完后的结果包含聚合键和聚合函数结果两列内容,其次,对聚合键的筛选用普通WHERE行筛选就可以完成。所以聚合键写在WHERE中更合适
SELECT
m.net_w ,
SUM(m.sale_price)
FROM milk_tea AS m
GROUP BY m.net_w;
注解:
相当于以m.net_w为类,筛选出展示m.net_w , SUM(m.sale_price) 这两列,其中m.net_w列中每类的和是对应的 SUM(m.sale_price) 中的值。
SELECT
m.net_w
, SUM(m.sale_price)
FROM milk_tea AS m
GROUP BY m.net_w
HAVING SUM(m.sale_price) > 20;
#这里,Having后跟的是聚合函数结果列,即对分组结果进行过滤
#HAVING相当于在分组聚合的结果下(此时两列为m.net_w和SUM(m.sale_price)),取出此时表中SUM(m.sale_price)>20的行
注:这里Having后跟的是聚合函数结果列,即对分组结果进行过滤
结果:
也可以对分组聚合后的net_w作为条件进行筛选,但是有一个问题,下面解释这个问题。
代码:
SELECT
m.net_w
, SUM(m.sale_price)
FROM milk_tea AS m
GROUP BY m.net_w
HAVING m.net_w IN ('100g','150g');
#不推荐这种方法
这个问题就是,对聚合键net_w的过滤完全可以在之前完成(分组之前)。
其实可以在分组聚合前就把where m.net_w IN (‘100g’,‘150g’)作为条件进行筛选,这样效率更高,因为这样是先过滤再进行分组聚合的求和计算,之前用Having那种方法是先进行分组聚合过滤,再进行条件过滤,所以Having的效率相对低一点,也比较能顺下来,符合逻辑。WHERE方法的代码就变成:
SELECT
m.net_w
, SUM(m.sale_price)
FROM milk_tea AS m
WHERE m.net_w IN ('100g','150g')
GROUP BY m.net_w ;
建议:先进行过滤,再进行分组聚合,这样对HAVING后面接的都是聚合函数,因为聚合函数只有在分组之后才能有这个结果。
举例
(1)在分组后对聚合键进行过滤
SELECT
p.class
, COUNT(1)
, SUM(p.sale_price)
, AVG(p.sale_price)
FROM prod_info as P
GROUP BY p.class;
结果:
(2)在分组后对聚合结果列进行过滤
SELECT
p.class
, COUNT(1)
, SUM(p.sale_price)
, AVG(p.sale_price)
FROM prod_info as P
GROUP BY p.class
HAVING COUNT(1) > 4;
感受一下,在分组前过滤表中的行,在分组后对组进行过滤,读起来更顺一点。
所以sql中的书写顺序和执行顺序他们的逻辑是怎样的?
再举例感受:
(1)先过滤后分组
SELECT
p.class
, COUNT(1)
, SUM(p.sale_price)
, AVG(p.sale_price)
FROM prod_info as P
WHERE P.class != '零食'
GROUP BY p.class;
(2)先过滤后分组
SELECT
p.class
, COUNT(1)
, SUM(p.sale_price)
, AVG(p.sale_price)
FROM prod_info as P
WHERE p.sale_price > 10
GROUP BY p.class;
总结:
1.sql语句的书写顺序
SELECT FROM
WHERE (过滤的是行)
GROUP BY (分组条件)
HAVING (筛选的是组)
ORDER BY (作为排序条件的组)
;
2.sql语句的执行顺序
FROM (确定从哪个表中选数据)
WHERE (确定选什么数据)
GROUP BY (对选出来的数据进行分组)
HAVING (对分组的数据进行组的筛选)
SELECT (然后对满足条件的具体的列做一个展示)
ORDER BY (展示的时候进行排序)
注: 尽量不要对GROUP BY使用别名,但是ORDER BY可以使用别名。
(因GROUP BY是先执行的,之后才执行SELECT将列名定为别名,所以要注意尽量在GROUP BY中不要使用别名,比如当使用的DBMS不智能时就会出错。)