前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >having用法

having用法

原创
作者头像
芥藍
发布2025-03-05 18:50:48
发布2025-03-05 18:50:48
8500
代码可运行
举报
运行总次数:0
代码可运行

场景描述

一家在线教育平台有两张表:studentscourses

  • students 表记录了学生的基本信息以及他们报名的课程。
  • courses 表记录了课程的详细信息,包括课程的难度等级。

现在需要找出报名了至少两门课程的学生,并列出他们的姓名以及报名的课程数量。


表结构和示例数据

表1:students

student_id

name

course_id

1

Alice

101

2

Bob

102

3

Alice

103

4

Charlie

104

5

Alice

105

6

Bob

106

表2: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


问题

找出报名了至少两门课程的学生,并列出他们的姓名以及报名的课程数量。


SQL 最优解

代码语言:javascript
代码运行次数:0
复制
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

1. HAVING 的基本语法

HAVING 通常与 GROUP BY 一起使用,其语法结构如下:

sql复制

代码语言:javascript
代码运行次数:0
复制
SELECT column1, column2, AGG_FUNC(column3) AS alias
FROM table_name
GROUP BY column1, column2
HAVING condition;
  • GROUP BY:将数据按指定列分组。
  • HAVING:对分组后的结果进行筛选,筛选条件可以包含聚合函数(如 COUNTSUMAVG 等)。

2. HAVING 的作用

HAVING 的主要作用是筛选满足特定条件的分组。它与 WHERE 的区别在于:

  • WHERE:在分组之前筛选行,不能使用聚合函数。
  • HAVING:在分组之后筛选分组,可以使用聚合函数。

3. 示例解释

假设我们有一个 students 表,记录学生的姓名和选修的课程编号:

name

course_id

Alice

1

Alice

2

Bob

3

Charlie

4

Charlie

5

Charlie

6

需求:找出选修了至少两门不同课程的学生
  1. 分组(**GROUP BY**)
代码语言:txt
复制
-  按照学生的姓名分组,每个学生是一个独立的分组。
代码语言:txt
复制
-  分组后,每个分组的数据如下:
代码语言:txt
复制
    -  Alice:[1, 2]
代码语言:txt
复制
    -  Bob:[3]
代码语言:txt
复制
    -  Charlie:[4, 5, 6]
  1. 聚合(**COUNT(DISTINCT course_id)**)
代码语言:txt
复制
-  对每个分组计算选修的不同课程数量:
代码语言:txt
复制
    -  Alice:2
代码语言:txt
复制
    -  Bob:1
代码语言:txt
复制
    -  Charlie:3
  1. 筛选(**HAVING**)
代码语言:txt
复制
-  使用 `HAVING COUNT(DISTINCT course_id) >= 2` 筛选出课程数量大于等于 2 的分组。
代码语言:txt
复制
-  结果:
代码语言:txt
复制
    -  Alice:2
代码语言:txt
复制
    -  Charlie:3

最终的 SQL 查询如下:

sql复制

代码语言:javascript
代码运行次数:0
复制
SELECT 
    name,
    COUNT(DISTINCT course_id) AS course_count
FROM 
    students
GROUP BY 
    name
HAVING 
    COUNT(DISTINCT course_id) >= 2;

4. HAVING 的关键点

  • 与聚合函数配合HAVING 条件中可以使用聚合函数(如 COUNTSUMAVG 等),而 WHERE 条件中不能使用聚合函数。
  • 筛选分组HAVING 是在分组之后对分组结果进行筛选,而 WHERE 是在分组之前对行进行筛选。
  • 灵活性HAVING 可以结合多个聚合函数进行复杂的筛选条件。

5. 示例对比

假设需求是“找出选修了至少两门不同课程的学生”,以下两种写法的区别:

使用 HAVING

sql复制

代码语言:javascript
代码运行次数:0
复制
SELECT 
    name,
    COUNT(DISTINCT course_id) AS course_count
FROM 
    students
GROUP BY 
    name
HAVING 
    COUNT(DISTINCT course_id) >= 2;
  • 先分组,再筛选分组。
错误的 WHERE 写法

sql复制

代码语言:javascript
代码运行次数:0
复制
SELECT 
    name,
    COUNT(DISTINCT course_id) AS course_count
FROM 
    students
WHERE 
    COUNT(DISTINCT course_id) >= 2  -- 错误:WHERE 不能使用聚合函数
GROUP BY 
    name;
  • 这是错误的,因为 WHERE 不能使用聚合函数。

6. 总结

  • HAVING 是在分组后对分组结果进行筛选,通常与聚合函数配合使用。
  • WHERE 是在分组前对行进行筛选,不能使用聚合函数。
  • 如果需要根据聚合结果进行筛选,必须使用 HAVING

HAVINGWHERE 是 SQL 中用于数据筛选的两个重要的子句,但它们在作用范围、使用场景和语法上有明显的区别。以下是它们的主要区别:

1. 作用范围

  • WHERE
代码语言:txt
复制
-  **作用对象**:`WHERE` 是在分组(`GROUP BY`)之前对**单行数据**进行筛选。
代码语言:txt
复制
-  **筛选时机**:在数据分组之前,逐行判断是否满足条件。
代码语言:txt
复制
-  **使用场景**:用于筛选表中的行,不能使用聚合函数(如 `COUNT`、`SUM` 等)。
  • HAVING
代码语言:txt
复制
-  **作用对象**:`HAVING` 是在分组(`GROUP BY`)之后对**分组结果**进行筛选。
代码语言:txt
复制
-  **筛选时机**:在数据分组并计算聚合函数之后,对分组进行筛选。
代码语言:txt
复制
-  **使用场景**:用于筛选分组后的结果,可以使用聚合函数。

2. 是否支持聚合函数

  • WHERE
代码语言:txt
复制
-  **不支持聚合函数**:`WHERE` 子句中不能使用聚合函数(如 `COUNT`、`SUM`、`AVG` 等),因为这些函数需要在分组之后才有意义。
代码语言:txt
复制
-  **示例**: sql复制
  SELECT name, COUNT(course_id) AS course_count FROM students WHERE COUNT(course_id) >= 2;  -- 错误:WHERE 不能使用聚合函数
 
  • HAVING
代码语言:txt
复制
-  **支持聚合函数**:`HAVING` 子句中可以使用聚合函数,因为它是对分组后的结果进行筛选。
代码语言:txt
复制
-  **示例**: sql复制
  SELECT name, COUNT(course_id) AS course_count FROM students GROUP BY name HAVING COUNT(course_id) >= 2;  -- 正确:HAVING 可以使用聚合函数
 

3. 使用场景

  • WHERE
代码语言:txt
复制
-  **行级筛选**:用于在分组之前筛选出符合条件的行。
代码语言:txt
复制
-  **示例**: sql复制
  SELECT name, course_id FROM students WHERE course_id > 10;  -- 筛选出课程编号大于10的行
 
  • HAVING
代码语言:txt
复制
-  **分组级筛选**:用于在分组之后筛选出符合条件的分组。
代码语言:txt
复制
-  **示例**: sql复制
  SELECT name, COUNT(course_id) AS course_count FROM students GROUP BY name HAVING COUNT(course_id) >= 2;  -- 筛选出选修了至少两门课程的学生
 

4. 执行顺序

SQL 查询的执行顺序如下:

  1. FROM:确定数据来源表。
  2. WHERE:在分组之前筛选行。
  3. GROUP BY:对筛选后的数据进行分组。
  4. 聚合函数计算:对每个分组计算聚合结果。
  5. HAVING:在分组之后筛选分组。
  6. SELECT:选择需要的列并返回结果。

5. 总结

  • WHERE
代码语言:txt
复制
-  作用于单行数据。
代码语言:txt
复制
-  不能使用聚合函数。
代码语言:txt
复制
-  用于在分组之前筛选行。
  • HAVING
代码语言:txt
复制
-  作用于分组后的结果。
代码语言:txt
复制
-  可以使用聚合函数。
代码语言:txt
复制
-  用于在分组之后筛选分组。

案例二

代码语言:javascript
代码运行次数:0
复制
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的分组

说明

  1. CASE 语句的
代码语言:txt
复制
-  将 `CASE` 语句的逻辑分支正确嵌套,并添加了 `ELSE '其他'`,以避免未匹配的情况。
代码语言:txt
复制
-  修正了 `CASE` 语句的语法错误,确保每个分支以 `THEN` 结束,并在最后以 `END` 结束。
  1. 列名冲突的解决
代码语言:txt
复制
-  将 `COUNT(code_first_name)` 改为 `COUNT(*)`,并命名为 `first_name_count`,避免与 `CASE` 表达式的结果列名冲突。
  1. GROUP BY 的修正
代码语言:txt
复制
-  `GROUP BY` 中使用了 `CASE` 表达式的结果列 `code_first_name` 和 `level`,确保分组逻辑正确。
  1. HAVING 的修正
代码语言:txt
复制
-  `HAVING` 子句中使用了 `COUNT(*) > 10`,筛选出姓氏出现次数大于 10 的分组。

修正后的逻辑

  • 查询的目的是统计每个姓氏(如“王”、“邓”)的出现次数,并筛选出出现次数大于 10 的姓氏。
  • 如果 code 不属于“王”或“邓”,则归类为“其他”。
  • 分组基于姓氏和 level,并筛选出满足条件的分组。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景描述
  • 表结构和示例数据
    • 表1:students
    • 表2:courses
  • 问题
  • SQL 最优解
  • 结果验证
  • 1. HAVING 的基本语法
  • 2. HAVING 的作用
  • 3. 示例解释
    • 需求:找出选修了至少两门不同课程的学生
  • 4. HAVING 的关键点
  • 5. 示例对比
    • 使用 HAVING
    • 错误的 WHERE 写法
  • 6. 总结
  • 1. 作用范围
  • 2. 是否支持聚合函数
  • 3. 使用场景
  • 4. 执行顺序
  • 5. 总结
  • 说明
  • 修正后的逻辑
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档