Kodewick
第 8 课,共 21 课目录

GROUP BY 分组

第 8 课,共 21 课 · 第 2 部分 · 汇总 · 约 15 分钟

上一课的聚合函数把整张表算成一行。可常常要的是「每个……各有多少」:每个分类有几件商品?用 WHERE 一个分类一个分类地数,可以,但很麻烦:

main.sql
1SELECT COUNT(*) FROM products WHERE category = '厨房';2SELECT COUNT(*) FROM products WHERE category = '文具';
输出
第 1 行的语句
COUNT(*)
2
1 行
第 2 行的语句
COUNT(*)
2
1 行

4 个分类就要写 4 条,分类多了更写不完。GROUP BY 一条就够。

GROUP BY

GROUP BY 列 把这一列值相同的行放进同一组,聚合函数再在每一组里各算一次:

main.sql
1SELECT category, COUNT(*)2FROM products3GROUP BY category4ORDER BY category;
输出
categoryCOUNT(*)
厨房2
数码4
文具2
箱包2
4 行

点单步看看:先拿到 10 件商品,GROUP BY 把它们按分类分成 4 堆,每一堆变成结果里的一行,COUNT(*) 数的是这一堆有几行。

ORDER BY 不能省:MySQL 分完组,不保证按什么顺序给你。汉字按编码排,不是按拼音或笔画。

每组一行

一个 SELECT 里可以放好几个聚合函数,每个都在每组里算一次:

main.sql
1SELECT category,2    COUNT(*) AS product_count,3    MIN(price) AS cheapest,4    MAX(price) AS dearest,5    SUM(stock) AS total_stock6FROM products7GROUP BY category8ORDER BY category;
输出
categoryproduct_countcheapestdearesttotal_stock
厨房29.9012.00100
数码48.5059.00245
文具24.506.00200
箱包211.0039.9040
4 行

分组的列(category)每组只有一个值,所以可以放进 SELECT;聚合函数每组也只算出一个值。结果的每一行就是一组。

给结果排个名

每个客户下了几单?按客户编号分组,再按单数从多到少排,单数一样的按编号排:

main.sql
1SELECT customer_id, COUNT(*) AS order_count2FROM orders3GROUP BY customer_id4ORDER BY order_count DESC, customer_id;
输出
customer_idorder_count
13
22
42
52
31
71
81
7 行

1 号客户下了 3 单,最多。ORDER BY 可以用 SELECT 里起的别名,为什么可以,第 10 课细讲。

没下过单的 6、9、10 号客户不在结果里:订单表里根本没有他们的行,也就分不出他们的组。

每个订单多少钱

订单明细表里,一个订单有好几行。按 order_id 分组,把每组的「件数 × 单价」加起来,就是每个订单的金额:

main.sql
1SELECT order_id,2    SUM(quantity) AS items,3    SUM(quantity * unit_price) AS total4FROM order_items5GROUP BY order_id6ORDER BY order_id;
输出
order_iditemstotal
1419.50
2139.90
3376.00
4115.00
5443.80
6522.50
7361.90
8159.00
9421.00
10223.50
11112.00
12439.60
12 行

18 行明细变成了 12 行,一个订单一行。

先猜一猜

订单的状态只有 已发货、待发货、已取消 三种。结果有几行?

1SELECT status, COUNT(*)2FROM orders3GROUP BY status;

按几列分组

GROUP BY 后面可以写好几列,这几列的值全都一样的行才是一组。每个客户的订单,再按状态分开数:

main.sql
1SELECT customer_id, status, COUNT(*)2FROM orders3GROUP BY customer_id, status4ORDER BY customer_id, status;
输出
customer_idstatusCOUNT(*)
1已发货2
1待发货1
2已发货1
2待发货1
3已发货1
4已取消1
4待发货1
5已发货2
7已发货1
8已发货1
10 行

1 号客户有 2 单已发货、1 单待发货,分成了两行。

NULL 也自成一组。员工表里按上级分组,数每个人管着几个人:

main.sql
1SELECT manager_id, COUNT(*)2FROM employees3GROUP BY manager_id4ORDER BY manager_id;
输出
manager_idCOUNT(*)
NULL1
12
22
31
4 行

manager_id 是 NULL 的那一组就是没有上级的经理本人。

SELECT 里能放什么

分了组以后,SELECT 里只能放两种东西:分组的列和聚合函数。放别的列就会报错:

main.sql
1SELECT category, name, COUNT(*)2FROM products3GROUP BY category;
输出
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'town.products.name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

「name」没有写在 GROUP BY 里,每一组里它可能有好几个不同的值,不知道该显示哪一个。把它加进 GROUP BY,或者用 MAX(name) 这类聚合函数。

ERROR 1055:name 不在 GROUP BY 里。数码这一组有 4 件商品、4 个名字,结果却只有一行,显示哪个名字?MySQL 不替你挑。

改法看你要什么:要每组的数,就去掉 name;要每件商品一行,就不该分组;要每组「一个」名字,就用聚合函数挑一个,比如 MIN(name)。

小技巧: 有的数据库(还有很老的 MySQL)不报这个错,随便挑一个名字给你。结果看着没错,其实是碰巧,别依赖它。

练一练

写好查询,点检查。提示随便看,实在想不出来再看答案。

练习 1

每个城市的客户

查出每个城市有几个客户:两列 city 和 customers,按 city 排序。

要显示
citycustomers
上海3
北京2
广州2
成都2
杭州1
5 行
exercise1.sql
1SELECT city FROM customers;
输出
点「运行」或按 Ctrl+Enter
练习 2

每件商品卖了几件

从 order_items 里查出每件商品一共卖了几件:两列 product_id 和 sold,按 product_id 排序。

要显示
product_idsold
110
23
32
43
52
73
86
92
102
9 行
exercise2.sql
1SELECT product_id, quantity FROM order_items;
输出
点「运行」或按 Ctrl+Enter

台灯(6 号)没人买过,所以不在结果里。

练习 3

修好它:ERROR 1055

想查每个职位有几个人、最高工资多少,按最高工资从高到低排,却报了 ERROR 1055。改好它,列是 title、COUNT(*)、MAX(salary)(ORDER BY 里也可以写聚合函数,按每组算出的值排)。

要显示
titleCOUNT(*)MAX(salary)
经理16000
销售主管14500
仓库主管14200
销售23300
打包员12800
5 行
exercise3.sql
1SELECT title, name, COUNT(*), MAX(salary)2FROM employees3GROUP BY title4ORDER BY MAX(salary) DESC;
输出
点「运行」或按 Ctrl+Enter

这一课学会了

  • GROUP BY 列 把值相同的行分成一组,聚合函数在每组里各算一次,每组一行。
  • GROUP BY 后面可以写几列,这几列都一样才是一组;NULL 也自成一组。
  • 分组后的顺序不一定,要用 ORDER BY 定下来。
  • SELECT 里只能放分组的列和聚合函数,放别的列会得到 ERROR 1055。