Kodewick
第 7 课,共 21 课目录

聚合函数

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

到现在为止,查询查出来的都是一行一行的记录。可很多问题要的是一个数:一共有几件商品?库存加起来有多少?最贵的多少钱?这种「把很多行算成一个数」的函数叫聚合函数。

main.sql
1SELECT COUNT(*) FROM products;
输出
COUNT(*)
10
1 行

商品表有 10 行,结果只有一行一列:10。

数一数:COUNT

COUNT(*) 数的是行数。COUNT(列) 数的是这一列里不是 NULL 的格子有几个。客户表有 10 个客户,可有 3 个没留邮箱:

main.sql
1SELECT COUNT(*), COUNT(email) FROM customers;
输出
COUNT(*)COUNT(email)
107
1 行

同一张表,两个数不一样:COUNT(*) 是 10,COUNT(email) 是 7。想知道「有多少行」,用 COUNT(*);想知道「这一列填了多少」,用 COUNT(列)。

COUNT(DISTINCT 列) 只数不同的值。订单表里 12 个订单,下单的客户有几位?

main.sql
1SELECT COUNT(*), COUNT(DISTINCT customer_id) FROM orders;
输出
COUNT(*)COUNT(DISTINCT customer_id)
127
1 行

12 个订单,来自 7 位客户:有的客户下了不止一单。

加起来和平均:SUM、AVG

SUM 把一列加起来,AVG 算平均值:

main.sql
1SELECT SUM(stock), AVG(price) FROM products;
输出
SUM(stock)AVG(price)
58519.080000
1 行

所有商品的库存一共 585 件,平均价格 19.08。MySQL 把平均值显示成 19.080000,多了几个 0。用第 6 课的 ROUND 和 AS 整理一下:

main.sql
1SELECT SUM(stock) AS total_stock,2    ROUND(AVG(price), 2) AS avg_price3FROM products;
输出
total_stockavg_price
58519.08
1 行

括号里也可以是一个算式。订单明细表里每一行是「几件 × 单价」,全部加起来就是网店一共卖了多少钱:

main.sql
1SELECT SUM(quantity * unit_price) AS sales FROM order_items;
输出
sales
433.70
1 行

最大和最小:MAX、MIN

MAX 和 MIN 找最大和最小的值。数字、日期、文字都可以:

main.sql
1SELECT MIN(price), MAX(price) FROM products;2SELECT MIN(order_date), MAX(order_date) FROM orders;
输出
第 1 行的语句
MIN(price)MAX(price)
4.5059.00
1 行
第 2 行的语句
MIN(order_date)MAX(order_date)
2025-09-022025-10-25
1 行

最便宜的商品 4.50,最贵的 59.00;第一个订单在 2025-09-02,最后一个在 2025-10-25。日期越早越「小」。

小技巧: 聚合函数都不管 NULL:SUM、AVG、MIN、MAX 遇到 NULL 的格子直接跳过,就像 COUNT(列) 不数它一样。

先 WHERE,再聚合

加上 WHERE,就只算留下来的那些行。数码类有几件商品,库存一共多少?

main.sql
1SELECT COUNT(*), SUM(stock)2FROM products3WHERE category = '数码';
输出
COUNT(*)SUM(stock)
4245
1 行

点单步看看:WHERE 先一行一行决定留不留,只剩 4 件数码商品;然后这 4 行才被算成一行。

要是一行都没留下呢?

main.sql
1SELECT COUNT(*), SUM(stock)2FROM products3WHERE price > 100;
输出
COUNT(*)SUM(stock)
0NULL
1 行

没有商品超过 100 块。COUNT(*) 是 0,SUM 却是 NULL:没有可以加的数,就没有和。

先猜一猜

上海有 3 个客户,其中陈静没留邮箱。结果是多少?

1SELECT COUNT(email)2FROM customers3WHERE city = '上海';

很多行变成一行

聚合函数把很多行变成一行。所以它不能和普通的列放在一起:

main.sql
1SELECT name, MAX(price) FROM products;
输出
ERROR 1140 (42000): In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'town.products.name'; this is incompatible with sql_mode=only_full_group_by

用了 COUNT / SUM 这类聚合函数又没写 GROUP BY 时,不能再单独选「name」这种普通的列:要么加 GROUP BY name,要么把它也放进聚合函数里。

MAX(price) 只有一个值,name 却有 10 个,该显示哪一个?MySQL 不替你猜,给出 ERROR 1140。想知道最贵的是哪件商品,第 4 课的写法就行:ORDER BY price DESC LIMIT 1。下一课的 GROUP BY 会让聚合不只算出一行,而是每组一行。

练一练

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

练习 1

价格一览

查出商品的最低价、最高价和平均价,列名分别叫 lowest、highest、average,平均价保留两位小数。

要显示
lowesthighestaverage
4.5059.0019.08
1 行
exercise1.sql
1SELECT price FROM products;
输出
点「运行」或按 Ctrl+Enter
练习 2

3 号订单

从 order_items 里算出 3 号订单一共买了几件(列名 items)、一共多少钱(列名 total)。

要显示
itemstotal
376.00
1 行
exercise2.sql
1SELECT * FROM order_items;
输出
点「运行」或按 Ctrl+Enter
练习 3

修好它:数人数

这条查询想数北京有几个客户,结果却是 1,可北京明明有 2 个客户。改好它(列名就用 COUNT(*))。

要显示
COUNT(*)
2
1 行
exercise3.sql
1SELECT COUNT(email) FROM customers WHERE city = '北京';
输出
点「运行」或按 Ctrl+Enter

这一课学会了

  • 聚合函数把很多行算成一个值:COUNT、SUM、AVG、MIN、MAX。
  • COUNT(*) 数行数;COUNT(列) 不数 NULL;COUNT(DISTINCT 列) 只数不同的值。
  • SUM、AVG、MIN、MAX 跳过 NULL;一行都没有时 COUNT(*) 是 0,SUM 是 NULL。
  • 括号里可以写算式,比如 SUM(quantity * unit_price)。
  • WHERE 先挑行,聚合函数再算剩下的。
  • 不分组时,聚合函数不能和普通的列放在一起(ERROR 1140)。