Mysql从入门到入魔——5. 聚集、分组、子查询
本篇主要内容
六种聚集函数的使用,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 |
+---------+-----------+
WHERE
与 HAVING
的主要区别:
WHERE
是一个约束声明,HAVING
是一个过滤声明。WHERE
在数据分组前进行过滤,HAVING
在数据分组后进行过滤。
SELECT
子句顺序:
子句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算聚集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
3. 子查询
3.1 子查询的划分
按照相关性来分:分为 相关子查询 和 不相关子查询 。
-
不相关子查询
-
描述:子查询可独立执行,不依赖于父查询表的查询,称为不相关子查询。
-
执行过程:
(1)先执行子查询,其结果不会被显示,而是返回给外部查询来作为外部查询的查询条件;
(2)根据子查询的结果,执行外部查询。 -
相关子查询
-
描述:子查询的查询条件中需要用到父查询的某一个属性列,这样的查询称为相关子查询。相关子查询是无法独立执行的,因为它必然含有对外部查询表中元组分量的引用。
-
执行过程:
(1)按顺序从外部查询中取出一个元组,将元组的相关分量值传递给子查询;
(2)执行子查询,得到结果值;
(3)外部查询根据子查询返回的结果或结果集确定取出的这一行元组是否满足条件;若外层的where
子句返回真值,说明符合;否则不符合,则舍弃。
(4)重复上面三步,直到外部查询表中的所有元组都被处理完毕。
按照子查询所在位置分:WHERE
或 HAVING
后、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_id
和 cust.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从入门到入魔》系列文章,如果感兴趣的话可以点个关注不迷路哦~。
- 点赞
- 收藏
- 关注作者
评论(0)