Mysql从入门到入魔——5. 聚集分组子查询

Posted Dream丶Killer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql从入门到入魔——5. 聚集分组子查询相关的知识,希望对你有一定的参考价值。

❤ 系列内容 ❤

Mysql从入门到入魔——1. 数据准备(起航篇)
Mysql从入门到入魔——2. 操纵数据库和表
Mysql从入门到入魔——3. 查询、排序、WHERE过滤
Mysql从入门到入魔——4. 通配符、计算字段、函数
Mysql从入门到入魔——5. 聚集、分组、子查询
Mysql从入门到入魔——6. 表联结、组合查询(待更新…)
Mysql从入门到入魔——7. 插入、更新和删除(待更新…)
Mysql从入门到入魔——8. 视图、存储过程、事务处理(待更新…)
Mysql从入门到入魔——9. 游标、高级SQL特性(待更新…)
Mysql从入门到入魔——10. 知识点速记(完结)(待更新…)


本篇主要内容

六种聚集函数的使用,ORDER BY 数据分组,子查询( 相关子查询 与 不相关子查询 )。内容稍稍有些多,需要静下心来慢慢看,如果条件允许可以动手实践一下(数据库文件在系列首页)。话不多说,进入正题!



1. 数据聚集

1.1 聚集函数

先来看一下,我们常用的聚集函数和它的说明。

函数说明
AVG()返回某列的平均值
COUNT()返回某列的行数
MAX()返回某列的最大值
MIN()返回某列的最小值
SUM()返回某列值之和

1.1.1 AVG()函数

查询供应商 id'DLL01' 的所有商品的平均价格。

mysql> SELECT AVG(prod_price) AS avg_price
    -> FROM Products
    -> WHERE vend_id = 'DLL01';
+-----------+
| avg_price |
+-----------+
|  3.865000 |
+-----------+

1.1.2 COUNT()函数

两种使用方式:

  • COUNT(*) 对表中行的数目进行计数,包括 空值

查询 Customers 表中的顾客数量。

mysql> SELECT COUNT(*) AS num_cust
    -> FROM Customers;
+----------+
| num_cust |
+----------+
|        5 |
+----------+
  • COUNT(column) 对特定列中非 NULL 行进行计数。

查询 Customers 表中非空邮箱数。

mysql> SELECT COUNT(cust_email) AS num_cust
    -> FROM Customers;
+----------+
| num_cust |
+----------+
|        3 |
+----------+

1.1.3 MAX()函数

column 列为数值列, MAX(column) 返回 column 列中的最大值。

column 列为文本数据, MAX(column) 返回 column 列数据 排序 后的最后一行。


1.1.4 MIN()函数

column 列为数值列, MIN(column) 返回 column 列中的最小值。

column 列为文本数据, MIN(column) 返回 column 列数据 排序 后的最前面的行。


1.1.5 SUM()函数

SUM() 用来返回指定列值的和(总计)(忽略列值为 NULL 的行)。
查询 OrderItems 表中的商品总价之和。先计算出(数量 * 物品单价),再对其求和。

mysql> SELECT SUM(quantity * item_price) AS total_price
    -> FROM OrderItems;
+-------------+
| total_price |
+-------------+
|     5730.70 |
+-------------+

1.2 组合聚集函数

计算 Products 表中物品的数目,产品价格的最高值、最低值以及平均值。

mysql> SELECT COUNT(*) AS num_items,
    -> MIN(prod_price) AS price_min,
    -> MAX(prod_price) AS price_max,
    -> AVG(prod_price) AS price_avg
    -> FROM Products;
+-----------+-----------+-----------+-----------+
| num_items | price_min | price_max | price_avg |
+-----------+-----------+-----------+-----------+
|         9 |      3.49 |     11.99 |  6.823333 |
+-----------+-----------+-----------+-----------+


2. 数据分组

2.1 数据分组

使用分组将数据分为多个逻辑组, 对每个组进行聚集计算。使用聚集函数才能够

统计各个供应商的产品数量。

mysql> SELECT vend_id, COUNT(*) AS num_prods
    -> FROM Products
    -> GROUP BY vend_id;
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| BRS01   |         3 |
| DLL01   |         4 |
| FNG01   |         2 |
+---------+-----------+

group by 注意事项:

  • GROUP BY 可以嵌套使用。
  • GROUP BY 子句中列出的每一列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在 SELECT 中使用表达式,则必须在 GROUP BY 子句中指定相同的表达式。不能使用别名
  • 除聚集计算语句外,SELECT 语句中的每一列都必须在 GROUP BY 子句 中给出。
  • 如果分组列中包含具有 NULL 值的行,则 NULL 将作为一个分组返回。 如果列中有多行 NULL 值,它们将分为一组。
  • GROUP BY 子句必须出现在 WHERE 子句之后,ORDER BY 子句之前。

2.2 过滤分组

使用 HAVING 子句在数据分组后进行过滤。

例:查询 Products 表中有两个以上产品且其价格大于等于 4 的供应商。

mysql> SELECT vend_id, COUNT(*) AS num_prods
    -> FROM Products
    -> WHERE prod_price >= 4
    -> GROUP BY vend_id
    -> HAVING COUNT(*) >= 2;
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| BRS01   |         3 |
| FNG01   |         2 |
+---------+-----------+

WHEREHAVING 的主要区别:

  • WHERE 是一个约束声明,HAVING 是一个过滤声明。
  • WHERE 在数据分组前进行过滤,HAVING 在数据分组后进行过滤。

SELECT 子句顺序:

子句说明是否必须使用
SELECT要返回的列或表达式
FROM从中检索数据的表仅在从表选择数据时使用
WHERE行级过滤
GROUP BY分组说明仅在按组计算聚集时使用
HAVING组级过滤
ORDER BY输出排序顺序


3. 子查询

3.1 子查询的划分

按照相关性来分:分为 相关子查询不相关子查询

  • 相关子查询

  • 描述:子查询可独立执行,不依赖于父查询表的查询,称为不相关子查询。

  • 执行过程:
    (1)先执行子查询,其结果不会被显示,而是返回给外部查询来作为外部查询的查询条件;
    (2)根据子查询的结果,执行外部查询。

  • 不相关子查询

  • 描述:子查询的查询条件中需要用到父查询的某一个属性列,这样的查询称为相关子查询。相关子查询是无法独立执行的,因为它必然含有对外部查询表中元组分量的引用。

  • 执行过程:
    (1)按顺序从外部查询中取出一个元组,将元组的相关分量值传递给子查询;
    (2)执行子查询,得到结果值;
    (3)外部查询根据子查询返回的结果或结果集确定取出的这一行元组是否满足条件;若外层的 where 子句返回真值,说明符合;否则不符合,则舍弃。
    (4)重复上面散步,直到外部查询表中的所有元组都被处理完毕。

按照子查询所在位置分:WHEREHAVING 后、SELECT 后、FROM 后、EXISTS 后的子查询。
下面按照位置划分来进行介绍,期间会标注示例是否为相关子查询。

3.2 WHERE 或 HAVING后的子查询

3.2.1 单行子查询

在订单表中查找最早日期的订单信息。子查询返回一行查询结果。(该示例为不相关子查询)

mysql> SELECT *
    -> FROM Orders
    -> WHERE order_date = (SELECT MIN(order_date)
    ->                     FROM Orders);
+-----------+---------------------+------------+
| order_num | order_date          | cust_id    |
+-----------+---------------------+------------+
|     20006 | 2012-01-12 00:00:00 | 1000000003 |
+-----------+---------------------+------------+

3.2.2 多行子查询

查询订购物品 RGAN01 的所有顾客。这时子查询先从 OrderItems 表中找出包含 RGAN01 物品的订单号,再根据这些订单号在 Orders 表中找到对应的顾客 id 。(该示例为不相关子查询)

mysql> SELECT cust_id
    -> FROM Orders
    -> WHERE order_num IN (SELECT order_num
    ->                     FROM OrderItems
    ->                     WHERE prod_id = 'RGAN01');
+------------+
| cust_id    |
+------------+
| 1000000004 |
| 1000000005 |
+------------+
操作符含义
IN / NOT IN在列表中 / 不在列表中
ANY / SOME和子查询返回的某一个值比较
ALL同子查询结果中所有值进行比较

用法对比
ANY / SOME

  • SELECT ... FROM ... WHERE num > ANY(num1, num2)
  • 等价于
  • SELECT ... FROM ... WHERE num > num1 OR num > num2

ALL

  • SELECT ... FROM ... WHERE num > ALL(num1, num2)
  • 等价于
  • SELECT ... FROM ... WHERE num > num1 AND num > num2

注意

  • SELECT 语句中,子查询总是从内向外处理。

3.3 SELECT后的子查询

假如需要显示 Customers 表中每个顾客的订单总数。首先,从 Customers 表中检索顾客列表,然后对于检索出的每个顾客,统计其在 Orders 表中的订单数目。(该示例为相关子查询)

mysql> SELECT cust_name,
    ->  (SELECT COUNT(*)
    ->   FROM Orders AS ord
    ->   WHERE ord.cust_id = cust.cust_id) AS order_count
    -> FROM Customers AS cust;
+---------------+-------------+
| cust_name     | order_count |
+---------------+-------------+
| Village Toys  |           2 |
| Kids Place    |           0 |
| Fun4All       |           1 |
| Fun4All       |           1 |
| The Toy Store |           1 |
+---------------+-------------+

注意:上面的例子中使用的是 Ord.cust_idcust.cust_id ,而不是直接使用 cust_id ,因为在两个表中都有 cust_id 列,在有可能混淆列名时必须使用这种语法。


3.4 FROM后的子查询

将子查询结果作为一张表,并必须要起别名。
下面的例子可能有点复杂,它还涉及到之后我们将学习的表联结,这里使用的联结方式是内联结,根据设定的条件进行等值联结。

查询目标为美国供应商名称和它的产品名称,我们一步步分析,首先先通过子查询获取供应商 Vendors 表中国家为 USA 的供应商信息,再将子查询的结果作为 USA_Vendors 表,将 Products 表与该表联结,联结条件为两表 vend_id 列相等。(该示例为不相关子查询)

mysql> SELECT vend_name, prod_name
    -> FROM Products, (SELECT *
    ->                 FROM Vendors
    ->                 WHERE vend_country = 'USA') AS USA_Vendors
    -> WHERE Products.vend_id = USA_Vendors.vend_id;
+-----------------+---------------------+
| vend_name       | prod_name           |
+-----------------+---------------------+
| Bears R Us      | 8 inch teddy bear   |
| Bears R Us      | 12 inch teddy bear  |
| Bears R Us      | 18 inch teddy bear  |
| Doll House Inc. | Fish bean bag toy   |
| Doll House Inc. | Bird bean bag toy   |
| Doll House Inc. | Rabbit bean bag toy |
| Doll House Inc. | Raggedy Ann         |
+-----------------+---------------------+

3.5 EXISTS后的子查询(相关子查询)

现在使用 EXISTS 来实现上面 IN 实现的查询。即查询订购物品 RGAN01 的所有顾客。

mysql> SELECT cust_id
    -> FROM Orders AS ord
    -> WHERE EXISTS (SELECT *
    ->               FROM OrderItems AS ori
    ->               WHERE ord.order_num = ori.order_num
    ->                AND prod_id = 'RGAN01');
+------------+
| cust_id    |
+------------+
| 1000000004 |
| 1000000005 |
+------------+

这就是本文所有的内容了,如果感觉还不错的话。❤ 点个赞再走吧!!!❤

在这里插入图片描述
后续会继续分享《Mysql从入门到入魔》系列文章,如果感兴趣的话可以点个关注不迷路哦~。

以上是关于Mysql从入门到入魔——5. 聚集分组子查询的主要内容,如果未能解决你的问题,请参考以下文章

Mysql从入门到入魔——3. 查询排序WHERE过滤

❤️吐血总结《Mysql从入门到入魔》,图文并茂(建议收藏)❤️

亿级数据MySQL分库分表从入门到入魔

Mysql从入门到入魔——9. 游标高级SQL特性

Mysql从入门到入魔——7. 插入更新和删除

Mysql从入门到入魔——4. 通配符计算字段函数