MySQL DQL从基础查询到实战优化全指南
作者:cyforkk
MySQL DQL 完全指南:从基础查询到实战优化
DQL(数据查询语言)是 SQL 中最常用、最核心的部分 ——80% 的数据库操作都是 “查询”。无论是业务报表、数据分析还是页面展示,都离不开SELECT语句。但很多开发者写查询时只追求 “能查出结果”,却忽视了效率和可读性,导致 “查询慢”“逻辑乱” 等问题。本文从基础查询到复杂分组,再到执行顺序和规范,系统拆解 DQL 的核心知识点,帮你写出 “既正确又高效” 的查询语句。
一、DQL 核心概念与通用规范
1. 什么是 DQL?
DQL(Data Query Language)是用于从表中查询数据的 SQL 语句,核心命令只有一个:SELECT。它不修改数据,只返回结果集,是业务开发中使用频率最高的 SQL 类型。
2. 通用语法框架
所有查询语句都遵循以下基本结构(按执行顺序排列,而非书写顺序):
SELECT [DISTINCT] 字段列表 -- 5. 确定返回哪些字段 FROM 表名 -- 1. 确定查询的表 [WHERE 条件] -- 2. 筛选行(分组前) [GROUP BY 分组字段] -- 3. 按字段分组 [HAVING 分组条件] -- 4. 筛选分组(分组后) [ORDER BY 排序字段 [ASC/DESC]] -- 6. 排序 [LIMIT 起始位置, 条数]; -- 7. 分页(限制返回条数)
3. 通用规范(提升可读性和效率)
- 字段明确化:禁止用SELECT *(冗余字段拖慢速度,表结构变更易出错),需指定具体字段;
- 关键字大写:SELECT、FROM等关键字大写,字段名 / 表名小写,区分清晰;
- 格式对齐:多条件 / 多字段时换行对齐(示例见下文),避免一行堆到底;
- 注释说明:复杂查询加注释,说明查询目的(如 “统计各部门月均薪资”);
- 优先索引:WHERE、GROUP BY、ORDER BY的字段尽量建索引(加速查询)。
二、基础查询:获取数据的第一步
基础查询是所有复杂查询的基础,核心是 “明确要查哪些字段”。
1. 语法与案例
(1)查询指定字段
-- 语法:SELECT 字段1, 字段2 FROM 表名; SELECT emp_id, emp_name, salary FROM employee; -- 查员工的ID、姓名、薪资
(2)查询所有字段(不推荐,仅临时调试用)
SELECT * FROM employee; -- 查所有字段(生产环境禁止!)
问题:返回冗余字段(如无需展示的create_time),表加新字段后会自动返回,可能导致业务异常。
(3)去重查询(DISTINCT)
-- 语法:SELECT DISTINCT 字段 FROM 表名; -- 去除字段重复值 SELECT DISTINCT dept_id FROM employee; -- 查所有有员工的部门ID(去重)
注意:DISTINCT作用于其后所有字段(如DISTINCT a,b会同时去重 a 和 b 的组合)。
(4)别名查询(AS)
用别名简化字段名或表名,提升可读性:
-- 语法:字段/表名 [AS] 别名(AS可省略) SELECT emp_id AS 员工ID, emp_name 姓名, -- 省略AS salary * 12 年薪 -- 表达式也可加别名 FROM employee e; -- 表别名e(后续可简写)
三、条件查询:筛选符合要求的数据
实际业务中很少查全表数据,而是按条件筛选(如 “查薪资> 10000 的员工”),WHERE子句是条件查询的核心。
1. 常用条件运算符
类型 | 运算符 / 关键字 | 说明 |
---|---|---|
比较运算 | =、!=、>、<、>=、<= | 基本比较(如salary > 10000) |
逻辑运算 | AND、OR、NOT | 多条件组合(如salary > 8000 AND dept_id=1) |
范围查询 | BETWEEN … AND … | 在指定范围内(如age BETWEEN 20 AND 30) |
集合查询 | IN (值1, 值2…) | 在指定集合中(如dept_id IN (1,2)) |
模糊查询 | LIKE | 匹配字符串(%通配任意字符,_通配单个字符) |
空值查询 | IS NULL / IS NOT NULL | 判断是否为空(如dept_id IS NULL) |
2. 实战案例
-- 案例1:查研发部(dept_id=1)薪资>10000的员工 SELECT emp_name, salary FROM employee WHERE dept_id = 1 AND salary > 10000; -- 案例2:查年龄在25-35岁之间(包含25和35)的员工 SELECT emp_name, age FROM employee WHERE age BETWEEN 25 AND 35; -- 等价于 age >=25 AND age <=35 -- 案例3:查部门ID为1、2、3的员工(用IN替代多个OR) SELECT emp_name, dept_id FROM employee WHERE dept_id IN (1, 2, 3); -- 比 dept_id=1 OR dept_id=2 更简洁 -- 案例4:查姓名以“张”开头的员工(模糊查询) SELECT emp_name FROM employee WHERE emp_name LIKE '张%'; -- %匹配任意字符(如“张三”“张三丰”) -- 案例5:查没有部门(dept_id为空)的员工 SELECT emp_name FROM employee WHERE dept_id IS NULL; -- 注意:不能用 = NULL(NULL需用IS判断)
3. 条件查询规范
- 多条件优先用AND/OR,避免嵌套过深(超过 3 层建议拆分);
- 范围判断优先用BETWEEN(比>=+<=更简洁);
- 模糊查询慎用%开头(如LIKE ‘%张’,会导致索引失效,查询变慢)。
四、聚合函数:对数据做统计分析
聚合函数用于 “汇总数据”(如 “计算平均薪资”“统计员工总数”),常与GROUP BY配合使用。
1. 常用聚合函数
函数 | 说明 | 示例 |
---|---|---|
COUNT() | 统计行数(非 NULL 值的数量) | COUNT(emp_id) 统计有效员工数 |
SUM() | 求和(仅数值类型) | SUM(salary) 计算总薪资 |
AVG() | 求平均值(仅数值类型) | AVG(salary) 计算平均薪资 |
MAX() | 求最大值 | MAX(salary) 查最高薪资 |
MIN() | 求最小值 | MIN(hire_date) 查最早入职日期 |
2. 实战案例
-- 案例1:统计员工总数(COUNT(*)包含NULL,COUNT(字段)排除NULL) SELECT COUNT(*) AS 总记录数, -- 包含所有行(包括字段为NULL的) COUNT(emp_id) AS 有效员工数 -- 仅统计emp_id非NULL的行(主键不会NULL) FROM employee; -- 案例2:计算研发部(dept_id=1)的薪资总和、平均值、最高值 SELECT SUM(salary) AS 总薪资, AVG(salary) AS 平均薪资, MAX(salary) AS 最高薪资 FROM employee WHERE dept_id = 1; -- 先筛选研发部,再聚合
3. 聚合函数注意事项
- COUNT(*) 统计所有行(包括 NULL),COUNT(字段) 统计该字段非 NULL 的行;
- 聚合函数会自动忽略NULL值(如SUM(salary) 不会计算salary为 NULL 的行);
- 不能在WHERE中使用聚合函数(如WHERE AVG(salary) > 8000 错误,需用HAVING)。
五、分组查询:按类别汇总数据
分组查询(GROUP BY)用于 “按某个字段分类统计”(如 “按部门统计员工数”),常与聚合函数配合。
1. 语法与案例
-- 基础语法:GROUP BY 分组字段 [HAVING 分组条件] -- 案例1:按部门分组,统计每个部门的员工数和平均薪资 SELECT dept_id AS 部门ID, COUNT(emp_id) AS 员工数, -- 每个部门的员工数 AVG(salary) AS 平均薪资 -- 每个部门的平均薪资 FROM employee GROUP BY dept_id; -- 按部门ID分组 -- 案例2:分组后筛选(HAVING):平均薪资>9000的部门 SELECT dept_id AS 部门ID, COUNT(emp_id) AS 员工数, AVG(salary) AS 平均薪资 FROM employee GROUP BY dept_id HAVING 平均薪资 > 9000; -- 筛选分组后的结果(用别名更简洁)
2. WHERE vs HAVING(核心区别)
子句 | 作用时机 | 能否用聚合函数 | 典型场景 |
---|---|---|---|
WHERE | 分组前筛选行 | 不能 | 筛选单个字段(如dept_id=1) |
HAVING | 分组后筛选组 | 能 | 筛选聚合结果(如AVG(salary)>9000) |
示例对比:
-- 先筛选2022年后入职的员工,再按部门分组统计平均薪资>8000的部门 SELECT dept_id, AVG(salary) AS 平均薪资 FROM employee WHERE hire_date >= '2022-01-01' -- 分组前:只留2022年后入职的 GROUP BY dept_id HAVING 平均薪资 > 8000; -- 分组后:只留平均薪资超8000的部门
六、排序查询:按规则排列结果
查询结果默认无序,ORDER BY用于按指定字段排序(如 “按薪资从高到低排列”)。
1. 语法与案例
-- 基础语法:ORDER BY 字段1 [ASC/DESC], 字段2 [ASC/DESC] -- ASC:升序(默认,可省略);DESC:降序 -- 案例1:按薪资降序排列(从高到低) SELECT emp_name, salary FROM employee ORDER BY salary DESC; -- 案例2:多字段排序:先按部门ID升序,再按薪资降序 SELECT emp_name, dept_id, salary FROM employee ORDER BY dept_id ASC, salary DESC; -- 部门相同则比薪资
2. 排序规范
- 字符串排序按字符编码顺序(如中文按拼音,需注意数据库字符集);
- 大表排序加索引(ORDER BY的字段建索引,避免全表扫描排序);
- 分页前必须排序(否则分页结果可能混乱,如第 1 页和第 2 页有重复数据)。
七、分页查询:限制返回结果条数
当数据量很大时(如 10 万条),需分页查询(如 “每页显示 10 条”),LIMIT是实现分页的核心。
1. 语法与案例
-- 语法:LIMIT 起始位置, 每页条数(起始位置从0开始) -- 计算方式:第N页的起始位置 = (N-1) * 每页条数 -- 案例1:查询第1页数据(每页10条,起始位置0) SELECT emp_id, emp_name FROM employee ORDER BY emp_id ASC -- 分页必须先排序,保证顺序一致 LIMIT 0, 10; -- 案例2:查询第2页数据(起始位置10,取10条) SELECT emp_id, emp_name FROM employee ORDER BY emp_id ASC LIMIT 10, 10;
2. 分页规范
- 分页必加ORDER BY(否则每次查询的 “第 1 页” 可能不同);
- 大表分页优化:用主键范围替代LIMIT(如WHERE emp_id > 100 LIMIT 10,比LIMIT 100,10快);
- 前端传递 “页码” 和 “每页条数”,后端计算起始位置(避免前端传错)。
八、案例练习:综合查询实战
结合以上所有知识点,实现一个 “多条件统计 + 排序 + 分页” 的综合查询:
需求:查询研发部(dept_id=1)2022 年后入职的员工,按薪资降序排列,统计每个薪资段的人数,只看人数≥2 的薪资段,最后取第 1 页(每页 5 条)。
-- 分步拆解: -- 1. 筛选研发部+2022年后入职(WHERE) -- 2. 按薪资段分组(GROUP BY) -- 3. 筛选人数≥2的组(HAVING) -- 4. 按人数降序(ORDER BY) -- 5. 分页取第1页(LIMIT) SELECT FLOOR(salary / 1000) * 1000 AS 薪资段, -- 按1000为单位分组(如8000-8999) COUNT(emp_id) AS 人数 FROM employee WHERE dept_id = 1 AND hire_date >= '2022-01-01' GROUP BY 薪资段 HAVING 人数 >= 2 ORDER BY 人数 DESC LIMIT 0, 5;
九、DQL 执行顺序:理解查询的 “幕后流程”
很多人困惑 “为什么WHERE不能用聚合函数”“别名在哪个阶段生效”,答案在执行顺序里。DQL 各部分的执行顺序如下(与书写顺序不同):
- FROM:确定查询的表;
- WHERE:筛选行(分组前);
- GROUP BY:按字段分组;
- HAVING:筛选分组(分组后);
- SELECT:确定返回的字段和别名;
- ORDER BY:按字段或别名排序(此时已能识别SELECT中的别名);
- LIMIT:分页限制返回条数。
举例说明:
SELECT salary12 AS 年薪 FROM employee WHERE 年薪 > 100000 会报错 —— 因为WHERE在SELECT之前执行,此时 “年薪” 别名还未生成。正确写法是WHERE salary12 > 100000。
十、DQL 小结:核心要点与规范总览
1. 核心操作清单
操作类型 | 关键字 / 语法 | 核心作用 | 注意事项 |
---|---|---|---|
基础查询 | SELECT 字段 FROM 表 | 获取指定字段数据 | 禁止SELECT *,用别名简化 |
条件查询 | WHERE 条件 | 筛选符合条件的行 | 支持AND/OR/BETWEEN/LIKE等 |
聚合查询 | COUNT()/SUM()/AVG()等 | 统计汇总数据 | 忽略 NULL,WHERE中不能用 |
分组查询 | GROUP BY 字段 [HAVING 条件] | 按类别统计 | HAVING用于筛选分组结果 |
排序查询 | ORDER BY 字段 [ASC/DESC] | 按规则排列结果 | 大表加索引,分页前必排序 |
分页查询 | LIMIT 起始位置, 条数 | 限制返回条数,实现分页 | 起始位置从 0 开始,需配合ORDER BY |
2. 规范与最佳实践
- 性能优化:
- 只查需要的字段(避免SELECT *);
- WHERE/GROUP BY/ORDER BY的字段建索引;
- 避免LIMIT大偏移量(如LIMIT 100000, 10,改用主键范围)。
- 可读性:
- 多字段 / 多条件换行对齐;
- 复杂查询加注释说明业务目的;
- 表和字段用别名简化(如e代表employee)。
- 正确性:
- 分页必加ORDER BY;
- 分组查询中,SELECT只能包含分组字段和聚合函数;
- 模糊查询慎用%开头(防止索引失效)。
DQL 是 SQL 的 “灵魂”,掌握它不仅能完成业务需求,更能通过优化查询提升系统性能。记住:写查询时多思考 “是否有更高效的方式”“别人能否看懂”,养成规范习惯,才能从 “会查” 变成 “查得好”。
到此这篇关于MySQL DQL 完全指南:从基础查询到实战优化的文章就介绍到这了,更多相关mysql dql内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!