好的,各位观众老爷们,咱们今天就来聊聊SQL里一对让人又爱又恨的黄金搭档:GROUP BY
和 HAVING
!
想象一下,你开了家水果店,每天进货各种水果,最后你需要统计一下:
- 哪种水果卖得最好?
- 哪些水果的平均单价超过了你的预期?
- 哪些供应商提供的水果总价值最高?
这时候,GROUP BY
和 HAVING
就派上大用场了!它们就像水果店里的智能分析系统,能帮你把杂乱的数据整理成井井有条的报表。
一、GROUP BY
:化繁为简的魔法棒
GROUP BY
子句,顾名思义,就是把数据按照一个或多个列进行分组。它就像一个超级分类器,能把相同特征的数据归为一类,方便我们进行统计分析。
1. 基础用法:按水果种类分组
假设我们有张 fruit_sales
表,记录了每天的水果销售情况:
date | fruit_name | supplier | quantity | price |
---|---|---|---|---|
2023-10-26 | Apple | Farmer John | 10 | 5 |
2023-10-26 | Banana | Banana Corp | 20 | 2 |
2023-10-26 | Apple | Farmer John | 5 | 5 |
2023-10-27 | Banana | Banana Corp | 15 | 2 |
2023-10-27 | Orange | Citrus Co | 12 | 4 |
2023-10-27 | Apple | Farmer John | 8 | 5 |
如果我们想知道每种水果的总销量,就可以使用 GROUP BY
:
SELECT
fruit_name,
SUM(quantity) AS total_quantity
FROM
fruit_sales
GROUP BY
fruit_name;
这条SQL语句就像在说:“把 fruit_sales
表里的数据按照 fruit_name
分组,然后计算每组的 quantity
总和,并命名为 total_quantity
。”
结果会是这样的:
fruit_name | total_quantity |
---|---|
Apple | 23 |
Banana | 35 |
Orange | 12 |
2. 多列分组:更精细的分析
GROUP BY
还可以同时按照多个列进行分组,从而实现更精细的分析。比如,我们想知道每个供应商提供的每种水果的总销量:
SELECT
supplier,
fruit_name,
SUM(quantity) AS total_quantity
FROM
fruit_sales
GROUP BY
supplier,
fruit_name;
这样,我们就能看到每个供应商提供的每种水果的具体销售情况了。
3. 与聚合函数搭配:数据洞察的利器
GROUP BY
经常和聚合函数(如 SUM
, AVG
, COUNT
, MAX
, MIN
)一起使用,从而对分组后的数据进行统计分析。
SUM()
:计算总和AVG()
:计算平均值COUNT()
:计算数量MAX()
:计算最大值MIN()
:计算最小值
例如,我们可以计算每种水果的平均单价:
SELECT
fruit_name,
AVG(price) AS average_price
FROM
fruit_sales
GROUP BY
fruit_name;
二、HAVING
:分组后的筛选器
HAVING
子句是 GROUP BY
的好搭档,它用于对分组后的数据进行筛选,就像一个过滤器,只留下符合条件的分组。
1. WHERE
vs. HAVING
:傻傻分不清楚?
很多新手容易把 WHERE
和 HAVING
搞混,它们的主要区别在于:
WHERE
:在分组 之前 进行筛选,针对的是原始数据行。HAVING
:在分组 之后 进行筛选,针对的是分组后的结果。
你可以这样理解:WHERE
是在切水果之前挑水果,HAVING
是在切完水果后挑水果拼盘。
2. 基础用法:筛选销量超过阈值的水果
假设我们想找出总销量超过 20 的水果种类:
SELECT
fruit_name,
SUM(quantity) AS total_quantity
FROM
fruit_sales
GROUP BY
fruit_name
HAVING
SUM(quantity) > 20;
这条SQL语句就像在说:“把 fruit_sales
表里的数据按照 fruit_name
分组,计算每组的 quantity
总和,然后只保留 total_quantity
大于 20 的分组。”
3. 复杂条件:更灵活的筛选
HAVING
也可以使用复杂的条件表达式,进行更灵活的筛选。例如,我们可以找出平均单价超过 3 且总销量超过 15 的水果种类:
SELECT
fruit_name,
AVG(price) AS average_price,
SUM(quantity) AS total_quantity
FROM
fruit_sales
GROUP BY
fruit_name
HAVING
AVG(price) > 3 AND SUM(quantity) > 15;
三、GROUP BY
和 HAVING
的高级用法
掌握了基础用法,咱们再来看看 GROUP BY
和 HAVING
的一些高级技巧,让你的SQL技能更上一层楼。
1. ROLLUP
:生成汇总数据
ROLLUP
是 GROUP BY
的一个扩展,它可以生成各个分组的汇总数据,方便我们进行多层次的分析。
例如,我们可以统计每个供应商提供的每种水果的总销量,并生成每个供应商的总销量:
SELECT
supplier,
fruit_name,
SUM(quantity) AS total_quantity
FROM
fruit_sales
GROUP BY
supplier,
fruit_name WITH ROLLUP;
结果会是这样的:
supplier | fruit_name | total_quantity |
---|---|---|
Banana Corp | Banana | 35 |
Banana Corp | NULL | 35 |
Citrus Co | Orange | 12 |
Citrus Co | NULL | 12 |
Farmer John | Apple | 23 |
Farmer John | NULL | 23 |
NULL | NULL | 70 |
其中,fruit_name
为 NULL
的行表示该供应商的总销量,supplier
和 fruit_name
都为 NULL
的行表示所有水果的总销量。
2. CUBE
:生成所有可能的汇总数据
CUBE
比 ROLLUP
更加强大,它可以生成所有可能的分组组合的汇总数据。
SELECT
supplier,
fruit_name,
SUM(quantity) AS total_quantity
FROM
fruit_sales
GROUP BY
CUBE(supplier, fruit_name);
CUBE
会生成以下几种分组的汇总数据:
- (supplier, fruit_name)
- (supplier)
- (fruit_name)
- () (总计)
3. GROUPING SETS
:自定义汇总维度
GROUPING SETS
允许我们自定义需要生成汇总数据的分组组合。
例如,我们只想统计每个供应商的总销量和每种水果的总销量:
SELECT
supplier,
fruit_name,
SUM(quantity) AS total_quantity
FROM
fruit_sales
GROUP BY
GROUPING SETS ((supplier), (fruit_name));
这样,我们就只得到了每个供应商的总销量和每种水果的总销量,而没有生成 (supplier, fruit_name) 的汇总数据。
四、GROUP BY
和 HAVING
的优化技巧
GROUP BY
和 HAVING
在处理大量数据时可能会比较慢,因此我们需要掌握一些优化技巧,提高查询效率。
1. 索引优化:加速分组和筛选
在 GROUP BY
和 HAVING
中使用的列,最好建立索引,这样可以加速分组和筛选的过程。
2. 减少数据量:提前筛选
如果可能,尽量在 WHERE
子句中提前筛选掉不需要的数据,减少 GROUP BY
和 HAVING
的处理量。
3. 避免在 HAVING
中使用复杂的表达式
尽量避免在 HAVING
子句中使用复杂的表达式,这会降低查询效率。如果必须使用,可以考虑将表达式的结果存储在一个临时表中,然后再进行筛选。
4. 使用 EXPLAIN
分析查询计划
使用 EXPLAIN
命令可以查看SQL查询的执行计划,从而了解查询的瓶颈在哪里,并进行相应的优化。
五、总结
GROUP BY
和 HAVING
是SQL中非常重要的两个子句,它们可以帮助我们对数据进行分组、统计和筛选,从而发现数据背后的规律和价值。掌握了 GROUP BY
和 HAVING
的高级用法和优化技巧,你就能成为真正的SQL高手!
希望这篇文章对你有所帮助,祝你学习愉快!🎉