SQL 聚合终于变得易懂:GROUP BY、HAVING、MIN、MAX、AVG

发布: (2026年1月14日 GMT+8 07:02)
5 min read
原文: Dev.to

Source: Dev.to

思维转变:从行到组

到目前为止,我的大多数 SQL 查询都像这样:

“显示满足 X 条件的行”

GROUP BY 改变了游戏规则。你不再只考虑行,而是开始考虑行的组。这是一开始最不舒服的部分,所以让我展示一下我的做法。

我今天练习的内容

我处理的查询会提出以下问题:

  • 每个国家有多少用户?
  • 每个部门的平均工资是多少?
  • 哪个部门的平均分最高?

所有这些查询都需要分组——而不是简单的“获取满足条件的行”。

GROUP BY(终于点通了)

一开始,我不断收到类似的错误:

column must appear in the GROUP BY clause or be used in an aggregate function

这个错误信息让我很烦恼……直到我开始多思考并尝试更好地理解这条信息和任务。

帮助我的方法:
如果一个列没有出现在聚合函数中,它必须出现在 GROUP BY 中。

SELECT country, COUNT(*)
FROM users
GROUP BY country;
  • country → 用于分组
  • COUNT(*) → 对每个组进行汇总

但下面这种写法不行

SELECT country, email
FROM users
GROUP BY country;

因为 SQL 现在在问:“每个国家你想让我挑选哪一个 email?”
这就是我第一次真正的“好吧…合理”的时刻。

MIN、MAX 和 AVG(直接但强大)

当分组概念清晰后,这些函数就显得更自然了。

MIN

SELECT department, MIN(salary)
FROM employees
GROUP BY department;

每个部门的最低工资

MAX

SELECT department, MAX(salary)
FROM employees
GROUP BY department;

每个部门的最高工资

AVG

SELECT department, AVG(salary)
FROM employees
GROUP BY department;

每个部门的平均工资

看到每个组都有实际数值输出,SQL 变得更贴近真实业务。

HAVING(我最初把它和 WHERE 混淆了)

一开始,我写的查询像这样:

SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000
GROUP BY department;

SQL 基本上直接说不行

最终让人记住的区别

  • WHERE 在分组之前过滤行。
  • HAVING 在分组之后过滤组。

正确的写法:

SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;

一旦把 HAVING 看作是“针对分组数据的 WHERE”,它就不再神秘。

执行顺序

了解执行顺序后,一切都清晰了:

  1. FROM
  2. WHERE – 过滤行
  3. GROUP BY – 创建组
  4. HAVING – 过滤组
  5. SELECT
  6. ORDER BY

这解释了为什么会有 HAVINGWHERE 只是执行得太早了。

仍然感觉怪异的地方

我仍在适应:

  • 用组而不是行来思考
  • 确定何时真的需要 GROUP BY
  • 阅读分组查询时不迷失

但与昨天相比?已经是进步了。

我为何分享这些

GROUP BYHAVING 往往被快速教授,例如:

“这里是语法,继续往下走。”

对于初学者来说,这是一种思维转变,而不仅仅是语法。如果你也在学习 SQL,起初对 GROUP BY 感到困惑,你并不孤单。

接下来

我正在慢慢、踏实、诚实地学习 SQL,并记录整个过程,以免其他初学者觉得自己是唯一在挣扎的人。如果你走在同一条路上,你并没有落后——你只是在以正确的方式学习。

Back to Blog

相关文章

阅读更多 »

如何解答 LeetCode 1193

问题描述 表 Transactions 具有以下列: - id 主键 - country - state 枚举:'approved' 或 'declined' - amount - trans...

SQLite 中的系统事务到用户事务

用户事务:逃离自动提交 默认情况下,SQLite 运行在 autocommit 模式。每个非 SELECT 语句都会被包装在它自己的事务中:!Autocommit tr...