MySQL 查询深度解析:执行顺序、高级排序与 NULL 值处理
MySQL 查询是数据库操作的核心,理解其执行机制、掌握高级排序技巧及正确处理 NULL 值,能显著提升查询效率和准确性。本文基于查询执行顺序,详解高级用法及常见陷阱。
MySQL 查询的执行顺序
MySQL 执行 SELECT 语句时,并非按语句书写顺序执行,而是遵循固定的内部逻辑流程。正确理解执行顺序,是优化查询和排查问题的基础。
执行顺序详解(从先到后)
FROM子句:确定查询的数据来源(表、视图或子查询),是查询的起点。
例:FROM employees表示数据来自employees表。WHERE子句:对FROM获取的原始数据进行行级筛选,排除不满足条件的记录(在分组前执行)。
例:WHERE department = 'Sales'筛选出销售部门的记录。GROUP BY子句:将WHERE筛选后的结果按指定字段分组(相同值的记录归为一组)。
例:GROUP BY department按部门分组。- 聚集函数计算:对每个分组应用聚集函数(
SUM、MAX、COUNT等),生成分组的统计结果。
例:COUNT(employee_id)计算每个部门的员工数。 HAVING子句:对分组后的结果进行筛选(在分组后执行,可使用聚集函数)。
例:HAVING COUNT(employee_id) > 10筛选出员工数超过 10 人的部门。- 计算表达式:处理查询中的算术运算或函数(如
salary * 1.1、UPPER(name))。 SELECT子句:提取需要返回的字段(或表达式结果),此时才确定最终输出的列。ORDER BY子句:对SELECT提取的结果按指定字段排序(ASC升序,DESC降序)。
示例:执行顺序验证
1 | SELECT |
执行流程:
- 从
employees表获取所有记录; - 筛选出 2020 年及以后入职的员工;
- 按
department分组; - 计算每个部门的员工数(
emp_count); - 保留
emp_count > 5的分组; - 无额外表达式计算;
- 提取
department和emp_count; - 按
emp_count降序排序。
高级排序:ORDER BY 与 CASE 表达式
ORDER BY 支持静态字段排序,结合 CASE 表达式可实现动态排序逻辑,根据条件调整排序依据。
语法:ORDER BY CASE ... END
1 | SELECT <字段> |
示例:按职位动态排序
需求:查询员工信息,销售岗位(job = 'SALES')按佣金(comm)排序,其他岗位按薪资(sal)排序。
1 | SELECT emp_id, name, job, sal, comm |
- 当
job = 'SALES'时,排序依据为comm; - 其他情况,排序依据为
sal; - 整体按降序排列,实现 “同岗位内按关键指标排序” 的需求。
进阶:多条件动态排序
1 | -- 先按部门分组,部门为'Sales'的按佣金,其他按薪资,再按姓名升序 |
特殊的 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. IN 与 NOT IN 中的 NULL 陷阱
陷阱 1:IN 包含 NULL 时的结果
1 | -- 表 app 中 name 有 '测试'、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 NULL或IS 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。聚合函数忽略 NULL:
COUNT(name)不统计 NULL 值,COUNT(*)统计所有行(包括 NULL)。