0%

MySQL查询

MySQL 查询深度解析:执行顺序、高级排序与 NULL 值处理

MySQL 查询是数据库操作的核心,理解其执行机制、掌握高级排序技巧及正确处理 NULL 值,能显著提升查询效率和准确性。本文基于查询执行顺序,详解高级用法及常见陷阱。

MySQL 查询的执行顺序

MySQL 执行 SELECT 语句时,并非按语句书写顺序执行,而是遵循固定的内部逻辑流程。正确理解执行顺序,是优化查询和排查问题的基础。

执行顺序详解(从先到后)

  1. FROM 子句:确定查询的数据来源(表、视图或子查询),是查询的起点。
    例:FROM employees 表示数据来自 employees 表。
  2. WHERE 子句:对 FROM 获取的原始数据进行行级筛选,排除不满足条件的记录(在分组前执行)。
    例:WHERE department = 'Sales' 筛选出销售部门的记录。
  3. GROUP BY 子句:将 WHERE 筛选后的结果按指定字段分组(相同值的记录归为一组)。
    例:GROUP BY department 按部门分组。
  4. 聚集函数计算:对每个分组应用聚集函数(SUMMAXCOUNT 等),生成分组的统计结果。
    例:COUNT(employee_id) 计算每个部门的员工数。
  5. HAVING 子句:对分组后的结果进行筛选(在分组后执行,可使用聚集函数)。
    例:HAVING COUNT(employee_id) > 10 筛选出员工数超过 10 人的部门。
  6. 计算表达式:处理查询中的算术运算或函数(如 salary * 1.1UPPER(name))。
  7. SELECT 子句:提取需要返回的字段(或表达式结果),此时才确定最终输出的列。
  8. ORDER BY 子句:对 SELECT 提取的结果按指定字段排序(ASC 升序,DESC 降序)。

示例:执行顺序验证

1
2
3
4
5
6
7
8
SELECT 
department,
COUNT(employee_id) AS emp_count -- 步骤7:提取字段
FROM employees -- 步骤1:数据来源
WHERE hire_year >= 2020 -- 步骤2:筛选2020年后入职的员工
GROUP BY department -- 步骤3:按部门分组
HAVING emp_count > 5 -- 步骤5:筛选员工数>5的部门
ORDER BY emp_count DESC; -- 步骤8:按员工数降序

执行流程:

  1. employees 表获取所有记录;
  2. 筛选出 2020 年及以后入职的员工;
  3. department 分组;
  4. 计算每个部门的员工数(emp_count);
  5. 保留 emp_count > 5 的分组;
  6. 无额外表达式计算;
  7. 提取 departmentemp_count
  8. emp_count 降序排序。

高级排序:ORDER BYCASE 表达式

ORDER BY 支持静态字段排序,结合 CASE 表达式可实现动态排序逻辑,根据条件调整排序依据。

语法:ORDER BY CASE ... END

1
2
3
4
5
6
7
SELECT <字段>
FROM <表名>
ORDER BY CASE
WHEN <条件1> THEN <排序字段1>
WHEN <条件2> THEN <排序字段2>
ELSE <默认排序字段>
END [ASC|DESC];

示例:按职位动态排序

需求:查询员工信息,销售岗位(job = 'SALES')按佣金(comm)排序,其他岗位按薪资(sal)排序。

1
2
3
4
5
6
SELECT emp_id, name, job, sal, comm
FROM emp
ORDER BY CASE
WHEN job = 'SALES' THEN comm -- 销售岗按佣金排序
ELSE sal -- 其他岗按薪资排序
END DESC; -- 降序(佣金/薪资从高到低)
  • job = 'SALES' 时,排序依据为 comm
  • 其他情况,排序依据为 sal
  • 整体按降序排列,实现 “同岗位内按关键指标排序” 的需求。

进阶:多条件动态排序

1
2
3
4
5
6
-- 先按部门分组,部门为'Sales'的按佣金,其他按薪资,再按姓名升序
SELECT * FROM emp
ORDER BY
department, -- 一级排序:部门
CASE WHEN department = 'Sales' THEN comm ELSE sal END DESC, -- 二级排序:动态字段
name ASC; -- 三级排序:姓名

特殊的 NULL 值处理

NULL 表示 “未知值”,并非 “空字符串” 或 “0”,其参与逻辑运算时结果特殊,容易导致查询陷阱。

1. NULL 参与逻辑运算的规则

  • NULL = NULL 结果为 NULL(未知是否相等),需用 IS NULL 判断。
  • TRUE OR NULL 结果为 TRUE(已知一真则整体为真)。
  • FALSE OR NULL 结果为 NULL(未知是否为真)。
  • TRUE AND NULL 结果为 NULL(未知是否整体为真)。
  • FALSE AND NULL 结果为 FALSE(已知一假则整体为假)。

2. INNOT IN 中的 NULL 陷阱

陷阱 1:IN 包含 NULL 时的结果
1
2
-- 表 app 中 name 有 '测试'、NULL、'其他' 三条记录
SELECT name FROM app WHERE name IN ('测试', NULL);
  • 结果:返回 '测试'NULL
  • 实际:仅返回 '测试'
  • 原因:name = NULL 结果为 NULL(不满足 IN 条件),但 name IS NULL 才会匹配,IN 中包含 NULL 时无法匹配 NULL 值。
陷阱 2:NOT IN 包含 NULL 时的结果
1
SELECT name FROM app WHERE name NOT IN ('测试', NULL);
  • 预期:返回除 '测试'NULL 外的记录(如 '其他')。
  • 实际:无任何结果
  • 原因:
    name = '其他' 而言,需满足 '其他' != '测试''其他' != NULL
    '其他' != NULL 结果为 NULL,导致整体条件为 TRUE AND NULL = NULL,不满足 NOT IN 条件。

3. 正确处理 NULL 的方法

  • 判断 NULL:用 IS NULLIS NOT NULL,而非 = NULL!= NULL

    1
    2
    3
    4
    5
    6
    7
    -- 正确匹配 NULL
    SELECT * FROM app WHERE name IS NULL;

    -- 正确排除 NULL 和 '测试'
    SELECT * FROM app
    WHERE name NOT IN ('测试')
    AND name IS NOT NULL; -- 单独排除 NULL
  • 处理 NOT IN 包含 NULL:先过滤 NULL 值,再使用 NOT IN

  • 聚合函数忽略 NULLCOUNT(name) 不统计 NULL 值,COUNT(*) 统计所有行(包括 NULL)。

欢迎关注我的其它发布渠道