一家在线教育平台有两张表:students
和 courses
。
students
表记录了学生的基本信息以及他们报名的课程。courses
表记录了课程的详细信息,包括课程的难度等级。现在需要找出报名了至少两门课程的学生,并列出他们的姓名以及报名的课程数量。
students
student_id | name | course_id |
---|---|---|
1 | Alice | 101 |
2 | Bob | 102 |
3 | Alice | 103 |
4 | Charlie | 104 |
5 | Alice | 105 |
6 | Bob | 106 |
courses
course_id | course_name | difficulty |
---|---|---|
101 | Mathematics | Medium |
102 | Physics | Hard |
103 | Chemistry | Medium |
104 | Biology | Easy |
105 | Computer Science | Hard |
106 | English | Easy |
找出报名了至少两门课程的学生,并列出他们的姓名以及报名的课程数量。
SELECT
s.name,
COUNT(DISTINCT s.course_id) AS course_count
FROM
students s
GROUP BY
s.name
HAVING
COUNT(DISTINCT s.course_id) >= 2;
根据示例数据,执行上述SQL语句后,结果如下:
name | course_count |
---|---|
Alice | 3 |
Bob | 2 |
HAVING
的基本语法HAVING
通常与 GROUP BY
一起使用,其语法结构如下:
sql复制
SELECT column1, column2, AGG_FUNC(column3) AS alias
FROM table_name
GROUP BY column1, column2
HAVING condition;
GROUP BY
:将数据按指定列分组。HAVING
:对分组后的结果进行筛选,筛选条件可以包含聚合函数(如 COUNT
、SUM
、AVG
等)。HAVING
的作用HAVING
的主要作用是筛选满足特定条件的分组。它与 WHERE
的区别在于:
WHERE
:在分组之前筛选行,不能使用聚合函数。HAVING
:在分组之后筛选分组,可以使用聚合函数。假设我们有一个 students
表,记录学生的姓名和选修的课程编号:
name | course_id |
---|---|
Alice | 1 |
Alice | 2 |
Bob | 3 |
Charlie | 4 |
Charlie | 5 |
Charlie | 6 |
GROUP BY
**):- 按照学生的姓名分组,每个学生是一个独立的分组。
- 分组后,每个分组的数据如下:
- Alice:[1, 2]
- Bob:[3]
- Charlie:[4, 5, 6]
COUNT(DISTINCT course_id)
**):- 对每个分组计算选修的不同课程数量:
- Alice:2
- Bob:1
- Charlie:3
HAVING
**):- 使用 `HAVING COUNT(DISTINCT course_id) >= 2` 筛选出课程数量大于等于 2 的分组。
- 结果:
- Alice:2
- Charlie:3
最终的 SQL 查询如下:
sql复制
SELECT
name,
COUNT(DISTINCT course_id) AS course_count
FROM
students
GROUP BY
name
HAVING
COUNT(DISTINCT course_id) >= 2;
HAVING
的关键点HAVING
条件中可以使用聚合函数(如 COUNT
、SUM
、AVG
等),而 WHERE
条件中不能使用聚合函数。HAVING
是在分组之后对分组结果进行筛选,而 WHERE
是在分组之前对行进行筛选。HAVING
可以结合多个聚合函数进行复杂的筛选条件。假设需求是“找出选修了至少两门不同课程的学生”,以下两种写法的区别:
HAVING
sql复制
SELECT
name,
COUNT(DISTINCT course_id) AS course_count
FROM
students
GROUP BY
name
HAVING
COUNT(DISTINCT course_id) >= 2;
WHERE
写法sql复制
SELECT
name,
COUNT(DISTINCT course_id) AS course_count
FROM
students
WHERE
COUNT(DISTINCT course_id) >= 2 -- 错误:WHERE 不能使用聚合函数
GROUP BY
name;
WHERE
不能使用聚合函数。HAVING
是在分组后对分组结果进行筛选,通常与聚合函数配合使用。WHERE
是在分组前对行进行筛选,不能使用聚合函数。HAVING
。HAVING
和 WHERE
是 SQL 中用于数据筛选的两个重要的子句,但它们在作用范围、使用场景和语法上有明显的区别。以下是它们的主要区别:
WHERE
:- **作用对象**:`WHERE` 是在分组(`GROUP BY`)之前对**单行数据**进行筛选。
- **筛选时机**:在数据分组之前,逐行判断是否满足条件。
- **使用场景**:用于筛选表中的行,不能使用聚合函数(如 `COUNT`、`SUM` 等)。
HAVING
:- **作用对象**:`HAVING` 是在分组(`GROUP BY`)之后对**分组结果**进行筛选。
- **筛选时机**:在数据分组并计算聚合函数之后,对分组进行筛选。
- **使用场景**:用于筛选分组后的结果,可以使用聚合函数。
WHERE
:- **不支持聚合函数**:`WHERE` 子句中不能使用聚合函数(如 `COUNT`、`SUM`、`AVG` 等),因为这些函数需要在分组之后才有意义。
- **示例**: sql复制
SELECT name, COUNT(course_id) AS course_count FROM students WHERE COUNT(course_id) >= 2; -- 错误:WHERE 不能使用聚合函数
HAVING
:- **支持聚合函数**:`HAVING` 子句中可以使用聚合函数,因为它是对分组后的结果进行筛选。
- **示例**: sql复制
SELECT name, COUNT(course_id) AS course_count FROM students GROUP BY name HAVING COUNT(course_id) >= 2; -- 正确:HAVING 可以使用聚合函数
WHERE
:- **行级筛选**:用于在分组之前筛选出符合条件的行。
- **示例**: sql复制
SELECT name, course_id FROM students WHERE course_id > 10; -- 筛选出课程编号大于10的行
HAVING
:- **分组级筛选**:用于在分组之后筛选出符合条件的分组。
- **示例**: sql复制
SELECT name, COUNT(course_id) AS course_count FROM students GROUP BY name HAVING COUNT(course_id) >= 2; -- 筛选出选修了至少两门课程的学生
SQL 查询的执行顺序如下:
FROM
:确定数据来源表。WHERE
:在分组之前筛选行。GROUP BY
:对筛选后的数据进行分组。HAVING
:在分组之后筛选分组。SELECT
:选择需要的列并返回结果。WHERE
:- 作用于单行数据。
- 不能使用聚合函数。
- 用于在分组之前筛选行。
HAVING
:- 作用于分组后的结果。
- 可以使用聚合函数。
- 用于在分组之后筛选分组。
案例二
SELECT
COUNT(*) AS first_name_count, -- 统计每个姓氏的出现次数
CASE
WHEN code LIKE '王%' THEN '王'
WHEN code LIKE '邓%' THEN '邓'
ELSE '其他' -- 添加 ELSE 部分,避免未匹配的情况
END AS code_first_name,
level
FROM
dma.dma_dop_dim_project_manager
GROUP BY
code_first_name, level -- 按姓氏和 level 分组
HAVING
COUNT(*) > 10; -- 筛选出姓氏出现次数大于10的分组
CASE
语句的:- 将 `CASE` 语句的逻辑分支正确嵌套,并添加了 `ELSE '其他'`,以避免未匹配的情况。
- 修正了 `CASE` 语句的语法错误,确保每个分支以 `THEN` 结束,并在最后以 `END` 结束。
- 将 `COUNT(code_first_name)` 改为 `COUNT(*)`,并命名为 `first_name_count`,避免与 `CASE` 表达式的结果列名冲突。
GROUP BY
的修正:- `GROUP BY` 中使用了 `CASE` 表达式的结果列 `code_first_name` 和 `level`,确保分组逻辑正确。
HAVING
的修正:- `HAVING` 子句中使用了 `COUNT(*) > 10`,筛选出姓氏出现次数大于 10 的分组。
code
不属于“王”或“邓”,则归类为“其他”。level
,并筛选出满足条件的分组。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。