MySQL分组数据和子查询
分组数据
创建分组
mysql> SELECT vend_id,COUNT(*) AS num_prods
FROM products
GROUP BY vend_id;
+---------+-----------+
| vend_id | num_prods |
+---------+-----------+
| 1001 | 3 |
| 1002 | 2 |
| 1003 | 7 |
| 1005 | 2 |
+---------+-----------+
4 rows in set (0.01 sec)
- 上面的SELECT语句指定了两个列,vend_id包含产品供应商的ID,
num_prods为计算字段(用COUNT(*)函数建立)。GROUP BY子句指
示MySQL按vend_id排序并分组数据。这导致对每个vend_id而不是整个表
计算num_prods一次。从输出中可以看到,供应商1001有3个产品,供应商
1002有2个产品,供应商1003有7个产品,而供应商1005有2个产品。 - 因为使用了GROUP BY,就不必指定要计算和估值的每个组了。系统
会自动完成。GROUP BY子句指示MySQL分组数据,然后对每个组而不是
整个结果集进行聚集。 - 在具体使用GROUP BY子句前,需要知道一些重要的规定。
- GROUP BY子句可以包含任意数目的列。这使得能对分组进行嵌套,
为数据分组提供更细致的控制。 - 如果在GROUP BY子句中嵌套了分组,数据将在最后规定的分组上
进行汇总。换句话说,在建立分组时,指定的所有列都一起计算
(所以不能从个别的列取回数据)。 - GROUP BY子句中列出的每个列都必须是检索列或有效的表达式
(但不能是聚集函数)。如果在SELECT中使用表达式,则必须在
GROUP BY子句中指定相同的表达式。不能使用别名。 - 除聚集计算语句外,SELECT语句中的每个列都必须在GROUP BY子
句中给出。 - 如果分组列中具有NULL值,则NULL将作为一个分组返回。如果列
中有多行NULL值,它们将分为一组。 - GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。
- GROUP BY子句可以包含任意数目的列。这使得能对分组进行嵌套,
过滤分组
- HAVING非常类似于WHERE。事实上,目前为止所学过的所有类型的WHERE子句都可以用HAVING来替代。唯一的差别是WHERE过滤行,而HAVING过滤分组。
mysql> SELECT cust_id,COUNT(*) AS orders
FROM orders
GROUP BY cust_id
HAVING COUNT(*)>=2;
+---------+--------+
| cust_id | orders |
+---------+--------+
| 10001 | 2 |
+---------+--------+
1 row in set (0.00 sec)
- HAVING和WHERE的差别 这里有另一种理解方法,WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。这是一个重要的区别,WHERE排除的行不包括在分组中。这可能会改变计算值,从而影响HAVING子句中基于这些值过滤掉的分组。
分组和排序
mysql> SELECT order_num,SUM(quantity*item_price)AS ordertotal
FROM orderitems
GROUP BY order_num
HAVING SUM(quantity*item_price)>=50
ORDER BY ordertotal;
+-----------+------------+
| order_num | ordertotal |
+-----------+------------+
| 20006 | 55.00 |
| 20008 | 125.00 |
| 20005 | 149.87 |
| 20007 | 1000.00 |
+-----------+------------+
4 rows in set (0.01 sec)
- 它检索总计订单价格大于等于50的订单的订单号和总计订单价格,按总计订单价格排序输出。
SELECT 子句顺序
字句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FORM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算聚集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
LIMIT | 要检索的行数 | 否 |
子查询
- 子查询就是把一条SELECT语句返回的结果用于另一条SELECT语句的WHERE子句。
利用子查询进行过滤
mysql> SELECT order_num
FROM orderitems
WHERE prod_id='TNT2';
+-----------+
| order_num |
+-----------+
| 20005 |
| 20007 |
+-----------+
2 rows in set (0.01 sec)
mysql> SELECT cust_id
FROM orders
where order_num IN(20005,20007);
+---------+
| cust_id |
+---------+
| 10001 |
| 10004 |
+---------+
2 rows in set (0.00 sec)
mysql> SELECT cust_id
FROM orders
where order_num IN(SELECT order_num
FROM orderitems
WHERE prod_id='TNT2');
+---------+
| cust_id |
+---------+
| 10001 |
| 10004 |
+---------+
2 rows in set (0.01 sec)
- 在SELECT语句中,子查询总是从内向外处理。在处理上面的SELECT语句时,MySQL实际上执行了两个操作。首先,它执行下面的查询:
SELECT order_num FROM orderitems WHERE prod_id='TNT2';
- 此查询返回两个订单号:20005和20007。然后,这两个值以IN操作符要求的逗号分隔的格式传递给外部查询的WHERE子句。外部查询变成:
SELECT cust_id FROM orders where order_num IN(20005,20007);
-
可以看到,输出是正确的并且与前面硬编码WHERE子句所返回的值相同。
-
列必须匹配 在WHERE子句中使用子查询(如这里所示),应该保证SELECT语句具有与WHERE子句中相同数目的列。通常,子查询将返回单个列并且与单个列匹配,但如果需要也可以使用多个列。
-
虽然子查询一般与IN操作符结合使用,但也可以用于测试等于(=)、不等于(<>)等。
作为计算字段使用子查询
mysql> SELECT COUNT(*) AS orders FROM orders WHERE cust_id=10001;
+--------+
| orders |
+--------+
| 2 |
+--------+
1 row in set (0.00 sec)
- 为了对每个客户执行COUNT()计算,应该将COUNT()作为一个子查询。请看下面的代码:
mysql> SELECT cust_name,cust_state,(SELECT COUNT(*)
FROM orders
WHERE orders.cust_id=customers.cust_id) AS orders
FROM customers
ORDER BY cust_name;
+----------------+------------+--------+
| cust_name | cust_state | orders |
+----------------+------------+--------+
| Coyote Inc. | MI | 2 |
| E Fudd | IL | 1 |
| Mouse House | OH | 0 |
| Wascals | IN | 1 |
| Yosemite Place | AZ | 1 |
+----------------+------------+--------+
5 rows in set (0.01 sec)
-
这 条 SELECT 语句对 customers 表中每个客户返回 3 列 :cust_name、cust_state和orders。orders是一个计算字段,它是由圆括号中的子查询建立的。该子查询对检索出的每个客户执行一次。在此例子中,该子查询执行了5次,因为检索出了5个客户。
-
子查询中的WHERE子句与前面使用的WHERE子句稍有不同,因为它使用了完全限定列名(在第4章中首次提到)。下面的语句告诉SQL比较orders表中的cust_id与当前正从customers表中检索的cust_id:
WHERE orders.cust_id=customers.cust_id
-
相关子查询(correlated subquery) 涉及外部查询的子查询。这种类型的子查询称为相关子查询。