第3章 聚合与排序-SQL基础教程

3-1 对表进行聚合查询

聚合函数

函数就像是输入某个值就能输出相应结果的盒子一样
用于汇总的函数称为聚合函数(所谓聚合,就是将多行汇总为一行):COUNT / SUM / AVG / MAX / MIN

计算表中数据的行数

select count(*)
from product;

计算NULL之外的数据的行数

-- 如果想得到purchase_price列(进货单价)中非空行数的话,可以通过将对象列设定为参数来实现
select count(purchase_price)
from product;

COUNT()会得到包含NULL的数据行数,而COUNT(<列名>)会得到NULL之外的数据行数
只有COUNT函数能将
作为参数

计算合计值

select sum(sale_price),sum(purchase_price)
from product;

四则运算中如果存在NULL,结果一定是NULL
所有的聚合函数,如果以列名为参数,那么在计算之前就已经把NULL排除在外了

计算平均值

select avg(sale_price),avg(purchase_price)
from product;

计算最大值和最小值

select max(sale_price),min(purchase_price)
from product;
/*
SUM/AVG函数只能对数值类型的列使用
而MAX/MIN函数原则上可以适用于任何数据类型的列
*/
select max(regist_date),min(regist_date)
from product;

使用聚合函数删除重复值(关键字DISTINCT)

-- 想要计算值的种类时,可以在COUNT函数的参数中使用DISTINCT
select count(distinct product_type)
from product;
-- 在聚合函数的参数中使用DISTINCT,可以删除重复数据
select sum(sale_price),sum(distinct sale_price)
from product;

3-2 对表进行分组

GROUP BY子句

SELECT <列名1>, <列名2>, <列名3>, ……
FROM <表名>
GROUP BY <列名1>, <列名2>, <列名3>, ……;

/*
不使用GROUP BY子句时,是将表中的所有数据作为一组来对待的
而使用GROUP BY子句时,会将表中的数据分为多个组进行处理
*/
select product_type,count(*)
from product
group by product_type;

GROUP BY 子句中指定的列称为聚合键或者分组列
子句的书写顺序(暂定):SELECT → FROM → WHERE → GROUP BY
SQL子句的顺序不能改变,也不能互相替换

聚合键中包含NULL的情况

-- 聚合键中包含NULL时,在结果中会以“不确定”行(空行)的形式表现出来
select purchase_price,count(*)
from product
group by purchase_price;

使用WHERE子句时GROUP BY的执行结果

SELECT <列名1>, <列名2>, <列名3>, ……
FROM <表名>
WHERE
GROUP BY <列名1>, <列名2>, <列名3>, ……;

-- 使用WHERE子句进行汇总处理时,会先根据WHERE子句指定的条件进行过滤,然后再进行汇总处理
select purchase_price,count(*)
from product
where product_type='衣服'
group by purchase_price;

GROUP BY和WHERE并用时SELECT语句的执行顺序:
FROM → WHERE → GROUP BY → SELECT
在SQL语句中,书写顺序和DBMS内部的执行顺序并不相同

与聚合函数和GROUP BY子句有关的常见错误

使用聚合函数时,SELECT子句中只能存在以下三种元素:
常数
聚合函数
GROUP BY子句中指定的列名(也就是聚合键)

/*
常见错误① —— 把聚合键之外的列名书写在SELECT子句之中(只有MySQL认同这种语法)
*/
select product_name,purchase_price,count(*)
from product
group by purchase_price;
/*
常见错误② ——在GROUP BY子句中写了列的别名(MySQL也认同这种语法)
错误原因:SELECT子句在GROUP BY子句之后执行,在执行GROUP BY子句时,SELECT子句中定义的别名,DBMS还并不知道
*/
select product_type as pt,count(*)
from product
group by pt;

常见错误③——GROUP BY子句结果的显示是无序的

-- 常见错误④——在WHERE子句中使用聚合函数
select product_type,count(*)
from product
where count(*)=2 
-- 只有SELECT子句和HAVING子句(以及ORDER BY子句)中能够使用聚合函数
group by product_type;
/*
DISTINCT和GROUP BY都能够删除后续列中的重复数据
除此之外,它们还都会把NULL作为一个独立的结果返回,对多列使用时也会得到完全相同的结果
其实不仅处理结果相同,执行速度也基本上差不多

在“想要删除选择结果中的重复记录”时使用DISTINCT,
在“想要计算汇总结果”时使用GROUP BY
*/
select distinct product_type
from product;

3-3 为聚合结果指定条件

HAVING子句

SELECT <列名1>, <列名2>, <列名3>, ……
FROM <表名>
GROUP BY <列名1>, <列名2>, <列名3>, ……
HAVING <分组结果对应的条件>

/*
使用COUNT函数等对表中数据进行汇总操作时,为其指定条件的不是WHERE子句,而是HAVING子句
WHERE子句用来指定数据行的条件,HAVING子句用来指定分组的条件
使用HAVING子句时SELECT语句的顺序:
SELECT → FROM → WHERE → GROUP BY → HAVING
*/
select product_type,count(*)
from product
group by product_type
having count(*)=2;

HAVING子句的构成要素

● 常数
● 聚合函数
● GROUP BY子句中指定的列名(即聚合键)

/*
在思考HAVING子句的使用方法时,把一次汇总后的结果作为HAVING子句起始点的话更容易理解
汇总之后得到的表中并不存在product_name这个列,SQL当然无法为表中不存在的列设定条件了
*/
select product_type,count(*)
from product
group by product_type
having product_name='圆珠笔';

相对于HAVING子句,更适合写在WHERE子句中的条件

聚合键所对应的条件既可以写在 HAVING 子句当中,又可以写在 WHERE 子句中

select product_type,count(*) from product
where product_type='衣服'
group by product_type;
/*
聚合键所对应的条件不应该书写在HAVING子句当中,而应该书写在WHERE子句当中
①根本原因是WHERE子句和HAVING子句的作用不同:
WHERE子句 = 指定行所对应的条件
HAVING子句 = 指定组所对应的条件

②通常情况下,为了得到相同的结果,将条件写在WHERE子句
中要比写在HAVING子句中的处理速度更快,返回结果所需的时间更短

③WHERE子句更具速度优势的另一个理由是,可以对WHERE子句指定条
件所对应的列创建索引,这样也可以大幅提高处理速度
*/

3-4 对查询结果进行排序

ORDER BY子句

SELECT <列名1>, <列名2>, <列名3>, ……
FROM <表名>
ORDER BY <排序基准列1>, <排序基准列2>, ……

select product_id,product_name,sale_price,purchase_price
from product
order by sale_price;
/*
ORDER BY子句中书写的列名称为排序键

子句的书写顺序:
SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY
*/

指定升序或降序

/*
未指定ORDER BY子句中排列顺序时会默认使用升序进行排列

由于ASC和DESC这两个关键字是以列为单位指定的,因此可以
同时指定一个列为升序,指定其他列为降序
*/
select product_id,product_name,sale_price,purchase_price
from product
order by sale_price desc;

指定多个排序键

select product_id,product_name,sale_price,purchase_price
from product
order by sale_price,product_id;
/*
可以在ORDER BY子句中同时指定多个排序键
规则是优先使用左侧的键,如果该列存在相同值的话,再接着参考右侧的键
也可以同时使用3个以上的排序键
*/

NULL的顺序

/*
不能对NULL使用比较运算符,也就是说,不能对NULL和数字进行排序,也不能与字符串和日期比较大小
因此,使用含有NULL的列作为排序键时,NULL会在结果的开头或末尾汇总显示
*/
select product_id,product_name,sale_price,purchase_price
from product
order by purchase_price;

在排序键中使用显示用的别名

/*
GROUP BY子句中不能使用SELECT子句中定义的别名,
但是在ORDER BY子句中却是允许使用别名的

使用HAVING子句时SELECT语句的顺序:
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY

SELECT子句的执行顺序在GROUP BY子句之后,ORDER BY子句之前
因此,在执行GROUP BY子句时,SELECT语句中定义的别名无法被识别
也是因为这一原因,HAVING子句也不能使用别名
*/
select product_id as id,product_name,sale_price as sp,purchase_price 
from product
order by sp,id;

ORDER BY子句中可以使用的列

-- ORDER BY子句中也可以使用存在于表中、但并不包含在SELECT子句之中的列
select product_name,sale_price,purchase_price
from product
order by product_id;

-- 除此之外,还可以使用聚合函数(SELECT子句中未使用的聚合函数也行)
select product_type,count(*)
from product
group by product_type
order by count(*);

不要使用列编号

/*
在ORDER BY子句中,还可以使用在SELECT子句中出现的列所对应的编号
列编号是指SELECT子句中的列按照从左到右的顺序进行排列时所对应的编号(1, 2, 3, …)
*/
select product_id,product_name,sale_price,purchase_price
from product
order by 3 desc,1;

/*
虽然列编号使用起来非常方便,但我们并不推荐使用,原因有以下两点:
第一,代码阅读起来比较难
第二,在SQL-92A中已经明确指出该排序功能将来会被删除
*/

相关文章:

  • 2022-02-20
  • 2022-01-17
  • 2021-09-30
  • 2021-09-06
  • 2021-11-30
  • 2021-11-12
  • 2022-03-06
  • 2021-11-11
猜你喜欢
  • 2021-04-28
  • 2022-01-12
  • 2022-12-23
  • 2021-11-12
  • 2021-10-06
  • 2021-09-14
相关资源
相似解决方案