having用法

场景描述

一家在线教育平台有两张表:students 和 courses

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

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


表结构和示例数据

表1:students

| studentid | name | courseid |
|------------|------------|-----------|
| 1 | Alice | 101 |
| 2 | Bob | 102 |
| 3 | Alice | 103 |
| 4 | Charlie | 104 |
| 5 | Alice | 105 |
| 6 | Bob | 106 |

表2:courses

| courseid | coursename | difficulty |
|-----------|-------------------|------------|
| 101 | Mathematics | Medium |
| 102 | Physics | Hard |
| 103 | Chemistry | Medium |
| 104 | Biology | Easy |
| 105 | Computer Science | Hard |
| 106 | English | Easy |


问题

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


SQL 最优解

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复制

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

    • 按照学生的姓名分组,每个学生是一个独立的分组。

    • 分组后,每个分组的数据如下:

      • Alice:[1, 2]

      • Bob:[3]

      • Charlie:[4, 5, 6]

  2. 聚合(COUNT(DISTINCT course_id)

    • 对每个分组计算选修的不同课程数量:

      • Alice:2

      • Bob:1

      • Charlie:3

  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;

4. HAVING 的关键点

  • 与聚合函数配合HAVING 条件中可以使用聚合函数(如 COUNTSUMAVG 等),而 WHERE 条件中不能使用聚合函数。

  • 筛选分组HAVING 是在分组之后对分组结果进行筛选,而 WHERE 是在分组之前对行进行筛选。

  • 灵活性HAVING 可以结合多个聚合函数进行复杂的筛选条件。

5. 示例对比

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

使用 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 不能使用聚合函数。

6. 总结

  • HAVING 是在分组后对分组结果进行筛选,通常与聚合函数配合使用。

  • WHERE 是在分组前对行进行筛选,不能使用聚合函数。

  • 如果需要根据聚合结果进行筛选,必须使用 HAVING

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

1. 作用范围

  • WHERE

    • 作用对象WHERE 是在分组(GROUP BY)之前对单行数据进行筛选。

    • 筛选时机:在数据分组之前,逐行判断是否满足条件。

    • 使用场景:用于筛选表中的行,不能使用聚合函数(如 COUNTSUM 等)。

  • HAVING

    • 作用对象HAVING 是在分组(GROUP BY)之后对分组结果进行筛选。

    • 筛选时机:在数据分组并计算聚合函数之后,对分组进行筛选。

    • 使用场景:用于筛选分组后的结果,可以使用聚合函数。

2. 是否支持聚合函数

  • WHERE

    • 不支持聚合函数WHERE 子句中不能使用聚合函数(如 COUNTSUMAVG 等),因为这些函数需要在分组之后才有意义。

    • 示例

      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 可以使用聚合函数

3. 使用场景

  • 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;  -- 筛选出选修了至少两门课程的学生

4. 执行顺序

SQL 查询的执行顺序如下:

  1. FROM:确定数据来源表。

  2. WHERE:在分组之前筛选行。

  3. GROUP BY:对筛选后的数据进行分组。

  4. 聚合函数计算:对每个分组计算聚合结果。

  5. HAVING:在分组之后筛选分组。

  6. SELECT:选择需要的列并返回结果。

5. 总结

  • 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的分组

说明

  1. CASE 语句的

    • CASE 语句的逻辑分支正确嵌套,并添加了 ELSE '其他',以避免未匹配的情况。

    • 修正了 CASE 语句的语法错误,确保每个分支以 THEN 结束,并在最后以 END 结束。

  2. 列名冲突的解决

    • COUNT(code_first_name) 改为 COUNT(*),并命名为 first_name_count,避免与 CASE 表达式的结果列名冲突。

  3. GROUP BY 的修正

    • GROUP BY 中使用了 CASE 表达式的结果列 code_first_namelevel,确保分组逻辑正确。

  4. HAVING 的修正

    • HAVING 子句中使用了 COUNT(*) > 10,筛选出姓氏出现次数大于 10 的分组。

修正后的逻辑

  • 查询的目的是统计每个姓氏(如“王”、“邓”)的出现次数,并筛选出出现次数大于 10 的姓氏。

  • 如果 code 不属于“王”或“邓”,则归类为“其他”。

  • 分组基于姓氏和 level,并筛选出满足条件的分组。

你可能感兴趣的:(Mybatis,SQL学习,数据库)