COUNT、SUM、AVG、MAX、MIN、GROUP BY、HAVING
聚合函数对一组数据进行计算并返回单个结果值。其工作过程包括:
注意: 聚合函数会忽略NULL值(COUNT(*)除外),这是聚合函数的一个重要特性。
聚合函数对一组值执行计算并返回单个值,常用于统计分析。
-- 示例1: COUNT 计数
SELECT COUNT(*) AS 总用户数 FROM users; -- 统计所有行,包括NULL
SELECT COUNT(DISTINCT category) AS 分类数量 FROM products; -- 去重计数
SELECT COUNT(phone) AS 填写手机号的用户数 FROM users; -- NULL不计数
SELECT COUNT(1) AS 总记录数 FROM orders; -- 与COUNT(*)效果相同
-- 示例2: SUM 求和
SELECT SUM(total_amount) AS 总销售额 FROM orders;
SELECT SUM(quantity) AS 总销量 FROM order_items;
SELECT SUM(price * quantity) AS 总价值 FROM order_items;
-- 示例3: AVG 平均值
SELECT AVG(price) AS 平均价格 FROM products;
SELECT AVG(age) AS 平均年龄 FROM users WHERE age IS NOT NULL;
SELECT SUM(total_amount) / COUNT(*) AS 平均订单金额 FROM orders; -- 与AVG效果相同
-- 示例4: MAX 和 MIN 最大最小值
SELECT MAX(price) AS 最高价, MIN(price) AS 最低价 FROM products;
SELECT MAX(order_date) AS 最近订单时间, MIN(order_date) AS 最早订单时间 FROM orders;
SELECT MAX(LENGTH(username)) AS 最长用户名长度 FROM users;
-- 示例5: GROUP_CONCAT 字符串聚合
SELECT category, GROUP_CONCAT(product_name SEPARATOR ', ') AS 商品列表
FROM products
GROUP BY category;
-- 示例6: JSON_ARRAYAGG JSON数组聚合 (MySQL 5.7+)
SELECT category, JSON_ARRAYAGG(product_name) AS 商品数组
FROM products
GROUP BY category;
-- 示例7: STDDEV 和 VARIANCE 标准差和方差
SELECT STDDEV(price) AS 价格标准差, VARIANCE(price) AS 价格方差
FROM products;
GROUP BY 将结果集按一个或多个列进行分组,常与聚合函数配合使用。分组查询是数据分析的核心功能之一。
-- 示例5: 按分类统计商品数量
SELECT category, COUNT(*) AS 商品数量
FROM products
GROUP BY category;
-- 示例6: 按用户统计订单金额
SELECT
user_id,
COUNT(*) AS 订单数量,
SUM(total_amount) AS 总消费金额,
AVG(total_amount) AS 平均订单金额,
MAX(order_date) AS 最近订单时间
FROM orders
GROUP BY user_id
ORDER BY 总消费金额 DESC;
-- 示例7: 多字段分组
SELECT
category,
YEAR(created_at) AS 年份,
COUNT(*) AS 商品数量,
AVG(price) AS 平均价格
FROM products
GROUP BY category, YEAR(created_at)
ORDER BY category, 年份;
-- 示例8: 使用表达式分组
SELECT
CASE
WHEN price < 100 THEN '低价商品'
WHEN price < 500 THEN '中价商品'
ELSE '高价商品'
END AS 价格区间,
COUNT(*) AS 商品数量,
AVG(price) AS 平均价格
FROM products
GROUP BY 价格区间;
HAVING 用于过滤分组后的结果,WHERE 过滤分组前的数据。HAVING 可以使用聚合函数,而 WHERE 不能。
| 特性 | WHERE | HAVING |
|---|---|---|
| 执行时机 | 分组前 | 分组后 |
| 作用对象 | 行 | 组 |
| 使用聚合函数 | 不可以 | 可以 |
| 性能 | 通常更好(减少分组数据量) | 通常较差(分组后过滤) |
-- 示例9: 查询订单数量大于5的用户
SELECT
user_id,
COUNT(*) AS 订单数量,
SUM(total_amount) AS 总金额
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 5;
-- 示例10: WHERE 和 HAVING 组合使用
SELECT
category,
AVG(price) AS 平均价格,
COUNT(*) AS 商品数量
FROM products
WHERE stock > 0 -- 分组前过滤:只统计有库存的商品
GROUP BY category
HAVING AVG(price) > 100 -- 分组后过滤:平均价格大于100
ORDER BY 平均价格 DESC;
-- 示例11: 使用别名在HAVING中
SELECT
category AS 分类,
COUNT(*) AS 商品数量
FROM products
GROUP BY 分类
HAVING 商品数量 > 10; -- MySQL支持在HAVING中使用列别名
WITH ROLLUP 用于在分组查询的基础上添加汇总行,方便进行总计统计。
-- 示例12: WITH ROLLUP 汇总统计
SELECT
category,
COUNT(*) AS 商品数量,
SUM(price) AS 总价值
FROM products
GROUP BY category WITH ROLLUP;
-- 示例13: 多字段分组的 ROLLUP
SELECT
YEAR(order_date) AS 年份,
MONTH(order_date) AS 月份,
COUNT(*) AS 订单数量,
SUM(total_amount) AS 总销售额
FROM orders
GROUP BY 年份, 月份 WITH ROLLUP;
MySQL提供了多种字符串聚合函数,用于将分组内的字符串值合并为单个字符串。
-- 示例14: GROUP_CONCAT 字符串聚合
SELECT
category,
GROUP_CONCAT(product_name SEPARATOR ', ') AS 商品列表,
GROUP_CONCAT(DISTINCT price ORDER BY price DESC SEPARATOR ', ') AS 价格列表
FROM products
GROUP BY category;
-- 示例15: JSON_ARRAYAGG JSON数组聚合
SELECT
category,
JSON_ARRAYAGG(product_name) AS 商品数组,
JSON_ARRAYAGG(JSON_OBJECT('name', product_name, 'price', price)) AS 商品信息数组
FROM products
GROUP BY category;
窗口函数可以在聚合的同时保留原始数据,提供更灵活的分析能力。
-- 示例16: 使用窗口函数计算累计总和
SELECT
order_date,
total_amount,
SUM(total_amount) OVER (ORDER BY order_date) AS 累计销售额,
AVG(total_amount) OVER (ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS 移动平均
FROM orders
ORDER BY order_date;
-- 示例17: 按用户计算订单排名
SELECT
user_id,
order_id,
total_amount,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY total_amount DESC) AS 订单排名,
SUM(total_amount) OVER (PARTITION BY user_id) AS 用户总消费
FROM orders;
-- 每月销售统计
SELECT
DATE_FORMAT(order_date, '%Y-%m') AS 月份,
COUNT(*) AS 订单数量,
SUM(total_amount) AS 总销售额,
AVG(total_amount) AS 平均订单金额,
MAX(total_amount) AS 最大订单金额
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY DATE_FORMAT(order_date, '%Y-%m')
ORDER BY 月份;
-- 商品销售排行榜
SELECT
p.product_name,
COUNT(oi.order_item_id) AS 销售次数,
SUM(oi.quantity) AS 总销量,
SUM(oi.quantity * oi.price) AS 总销售额
FROM products p
LEFT JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY p.product_id, p.product_name
HAVING 总销量 > 0
ORDER BY 总销售额 DESC
LIMIT 10;
-- 用户消费等级分析
SELECT
CASE
WHEN 总消费 >= 10000 THEN 'VIP'
WHEN 总消费 >= 5000 THEN '高级会员'
WHEN 总消费 >= 1000 THEN '普通会员'
ELSE '新用户'
END AS 用户等级,
COUNT(*) AS 用户数量
FROM (
SELECT user_id, SUM(total_amount) AS 总消费
FROM orders
GROUP BY user_id
) AS user_stats
GROUP BY 用户等级;