MySQL 从入门到实战:DQL 查询语言详解(附案例 + 练习)

作为后端开发的必备技能,MySQL 的核心操作绕不开数据查询(DQL)。本文从基础概念到实战场景,全方位拆解 DQL 语法,搭配「案例 + 课后练习」,帮你彻底吃透 SELECT 核心用法~

一、先理清:SQL 的四大分类

在深入 DQL 之前,先明确 SQL99 标准的四大分类(避免语法混淆,这是入门基础):

分类 全称 核心作用 常用语法示例
DDL 数据定义语言 定义 / 修改 / 删除库、表结构 CREATE(建表)、ALTER(改表)、DROP(删表)
DML 数据操作语言 增删改表中数据 INSERT(插数据)、UPDATE(改数据)、DELETE(删数据)
DQL 数据查询语言 查询表中数据(本文核心) SELECT(查询)
DCL 数据控制语言 管理数据库权限 GRANT(赋权)、REVOKE(回收权限)

二、DQL 核心:SELECT 基础语法

DQL 的核心是 SELECT 语句,完整语法结构(加粗为必选,[] 内为可选):

复制代码
SELECT [DISTINCT] 字段1 [AS 别名1], 字段2 [AS 别名2]...  -- DISTINCT去重,AS自定义别名
FROM 表名 [AS 表别名]                                   -- 指定查询的表
[WHERE 行过滤条件]                                      -- 分组前过滤行数据
[GROUP BY 分组字段1, 分组字段2...]                      -- 按字段分组统计
[HAVING 分组后过滤条件]                                 -- 分组后过滤结果(仅对分组生效)
[ORDER BY 排序字段1 [ASC/DESC], 排序字段2...]           -- 排序(ASC升序,DESC降序)
[LIMIT 起始索引, 每页条数]                              -- 分页(索引从0开始)

三、DQL 实战案例:基于学生表 + 成绩表

前置准备:创建测试表并插入数据

所有案例均基于以下两张表,先执行建表和插数语句(直接复制即可运行):

复制代码
-- 1. 学生表(student):存储学生基础信息
CREATE TABLE student (
  id INT PRIMARY KEY AUTO_INCREMENT,  -- 主键自增,学生唯一标识
  name VARCHAR(20) NOT NULL,         -- 姓名,非空
  age INT,                            -- 年龄
  gender CHAR(1),                     -- 性别(男/女)
  class_id INT                        -- 班级ID(关联班级,简化演示)
);

-- 2. 成绩表(score):存储学生各科成绩
CREATE TABLE score (
  id INT PRIMARY KEY AUTO_INCREMENT,
  stu_id INT,                         -- 关联学生表的id
  subject VARCHAR(20),                -- 科目名称
  score INT,                          -- 分数
  -- 外键约束:保证stu_id必须是student表中存在的id
  FOREIGN KEY (stu_id) REFERENCES student(id)
);

-- 插入学生数据
INSERT INTO student(name, age, gender, class_id) VALUES
('张三', 18, '男', 1),
('李四', 17, '女', 1),
('王五', 18, '男', 2),
('赵六', 19, '男', 2),
('钱八', 17, '女', 2);

-- 插入成绩数据
INSERT INTO score(stu_id, subject, score) VALUES
(1, '数学', 90),
(1, '英语', 85),
(2, '数学', 95),
(2, '英语', 80),
(3, '数学', 70),
(3, '英语', 65),
(4, '数学', 88),
(4, '英语', 92),
(5, '数学', 82),
(5, '英语', 78);

案例 1:简单查询(指定列 / 全列 / 去重 / 别名)

场景 1.1:查询指定列

需求:查询所有学生的姓名、年龄

复制代码
SELECT name, age FROM student;

执行结果:

name age
张三 18
李四 17
王五 18
赵六 19
钱八 17
场景 1.2:查询全列(慎用 *)

需求:查询 1 班所有学生的完整信息

复制代码
SELECT * FROM student WHERE class_id = 1;

⚠️ 注意:SELECT * 会返回所有列,实际开发中尽量指定字段(如 name, age),避免冗余数据传输。

场景 1.3:去重查询

需求:查询成

复制代码
SELECT DISTINCT subject FROM score;

执行结果:

subject
数学
英语
场景 1.4:自定义别名

需求:查询学生姓名和年龄,列名显示为「学生姓名」「学生年龄」

复制代码
SELECT name AS 学生姓名, age AS 学生年龄 FROM student;

执行结果:

学生姓名 学生年龄
张三 18
李四 17

案例 2:条件查询(WHERE)

WHERE 支持比较运算符 (=、>、<、>=、<=、!=)、逻辑运算符 (AND、OR、NOT)、范围运算符(IN、BETWEEN...AND),精准过滤行数据。

场景 2.1:基础条件

需求:查询 1 班的男生信息

复制代码
SELECT * FROM student 
WHERE class_id = 1 AND gender = '男';

执行结果:仅返回「张三」的信息。

场景 2.2:范围条件

需求:查询年龄在 17~18 岁之间的女生

复制代码
SELECT * FROM student 
WHERE age BETWEEN 17 AND 18 AND gender = '女';

执行结果:

id name age gender class_id
2 李四 17 1
5 钱八 17 2
场景 2.3:IN 条件

需求:查询班级 ID 为 1 或 2 的学生(等价于 OR)

复制代码
SELECT name, class_id FROM student 
WHERE class_id IN (1, 2);

案例 3:模糊查询(LIKE)

LIKE 用于模糊匹配,% 代表任意长度字符(0 个 / 多个),_ 代表单个字符。

场景 3.1:以指定字符开头

需求:查询姓名以「张」开头的学生

复制代码
SELECT * FROM student WHERE name LIKE '张%';
场景 3.2:包含指定字符

需求:查询姓名中包含「五」的学生

复制代码
SELECT * FROM student WHERE name LIKE '%五%';
场景 3.3:固定长度匹配

需求:查询姓名为 2 个字且第二个字是「四」的学生

sql

复制代码
SELECT * FROM student WHERE name LIKE '_四';

案例 4:排序查询(ORDER BY)

ORDER BY 对结果排序,默认升序(ASC),降序需指定 DESC。

需求:查询所有学生的英语成绩,按分数降序排列(关联学生表显示姓名)

复制代码
SELECT s.name, sc.score 
FROM student s
JOIN score sc ON s.id = sc.stu_id  -- 关联两张表,通过stu_id匹配
WHERE sc.subject = '英语'
ORDER BY sc.score DESC;

执行结果:

name score
赵六 92
张三 85
李四 80
钱八 78
王五 65

案例 5:统计函数 + 分组(GROUP BY)

常用统计函数:

函数 作用
COUNT (字段) 统计非 NULL 值的行数
SUM (字段) 求和
AVG (字段) 求平均值
MAX (字段) 求最大值
MIN (字段) 求最小值
场景 5.1:基础分组统计

需求:统计每个级的学生人数

复制代码
SELECT class_id AS 班级ID, COUNT(id) AS 学生人数 
FROM student
GROUP BY class_id;

执行结果:

班级 ID 学生人数
1 2
2 3
场景 5.2:分组统计 + 多字段

需求:统计每个班级的平均年龄、最大年龄、最小年龄

复制代码
SELECT 
  class_id AS 班级ID,
  AVG(age) AS 平均年龄,
  MAX(age) AS 最大年龄,
  MIN(age) AS 最小年龄
FROM student
GROUP BY class_id;

执行结果:

班级 ID 平均年龄 最大年龄 最小年龄
1 17.5 18 17
2 18 19 17

案例 6:分组后过滤(HAVING)

HAVING 专门过滤分组后的结果,区别于 WHERE

  • WHERE:分组前过滤行数据,不能使用统计函数;
  • HAVING:分组后过滤分组结果,可使用统计函数。

需求:统计平均分 ≥ 80 的科目

复制代码
SELECT 
  subject AS 科目,
  AVG(score) AS 平均分
FROM score
GROUP BY subject  -- 先按科目分组
HAVING 平均分 >= 80;  -- 过滤平均分≥80的分组

执行结果:

科目 平均分
数学 85
英语 80

❌ 错误示例:用 WHERE 过滤统计函数(会报错)

复制代码
-- 错误:WHERE 不能使用 AVG(score)
SELECT subject, AVG(score) FROM score WHERE AVG(score) >=80 GROUP BY subject;

案例 7:分页查询(LIMIT)

LIMIT 起始索引, 条数 用于分页,索引从 0 开始(分页公式:起始索引 = (页码 - 1)* 每页条数)。

需求:查询学生列表,每页显示 2 条,查看第 2 页

复制代码
SELECT * FROM student LIMIT 2, 2;  -- 索引2开始,取2条

执行结果:

id name age gender class_id
3 王五 18 2
4 赵六 19 2

案例 8:多表联查(左连接)

左连接(LEFT JOIN):保留左表所有数据,右表匹配不到则显示 NULL。

需求:查询所有学生的数学成绩(包括没考数学的学生)

复制代码
SELECT s.name, sc.score AS 数学成绩 
FROM student s
LEFT JOIN score sc ON s.id = sc.stu_id 
AND sc.subject = '数学';  -- 条件写在ON后,保留左表所有学生

执行结果(若有学生没考数学,score 列显示 NULL):

name 数学成绩
张三 90
李四 95
王五 70
赵六 88
钱八 82

四、DQL 高频注意点(重点强调)

  1. 慎用 SELECT * :实际开发中必须指定需要的字段(如 name, age),减少网络传输和数据库开销;
  2. 索引优化 :多表联查时,外键字段(如 stu_id)、WHERE 条件字段(如 class_id)建议加索引,提升查询速度;
  3. WHERE vs HAVING 核心区别
    • WHERE 作用于「行」,分组前执行,不能用统计函数;
    • HAVING 作用于「分组」,分组后执行,可使用统计函数;
  4. 排序性能:大数据量排序时,排序字段建议加索引,避免全表排序。

五、课后练习(强化实战)

结合上述案例,完成以下练习(答案在文末):

  1. 查询 2 班年龄大于 18 岁的学生姓名;
  2. 查询数学成绩在 80~90 分之间的学生姓名;
  3. 统计每个学生的总分(关联学生表显示姓名);
  4. 查询总分 ≥ 170 分的学生姓名和总分;
  5. 查询学生列表,每页显示 3 条,查看第 1 页。

练习答案

复制代码
-- 1. 查询2班年龄大于18岁的学生姓名
SELECT name FROM student WHERE class_id = 2 AND age > 18;

-- 2. 查询数学成绩在80~90分之间的学生姓名
SELECT s.name 
FROM student s
JOIN score sc ON s.id = sc.stu_id
WHERE sc.subject = '数学' AND sc.score BETWEEN 80 AND 90;

-- 3. 统计每个学生的总分
SELECT s.name, SUM(sc.score) AS 总分
FROM student s
JOIN score sc ON s.id = sc.stu_id
GROUP BY s.id, s.name;

-- 4. 查询总分≥170分的学生姓名和总分
SELECT s.name, SUM(sc.score) AS 总分
FROM student s
JOIN score sc ON s.id = sc.stu_id
GROUP BY s.id, s.name
HAVING 总分 >= 170;

-- 5. 查询学生列表,每页显示3条,查看第1页
SELECT * FROM student LIMIT 0, 3;

六、工作中 DQL 高频实战场景

1. 分页查询(业务列表页标配)

后端接口返回列表数据时,分页是必做优化,核心用 LIMIT,结合业务参数动态传值:

复制代码
-- 通用分页模板(Java/PHP等后端可动态拼接参数)
SELECT id, name, create_time 
FROM user 
WHERE status = 1  -- 只查有效用户
ORDER BY create_time DESC 
LIMIT #{pageSize}*(#{pageNum}-1), #{pageSize};  -- pageNum:页码,pageSize:每页条数

2. 多表关联 + 条件过滤(订单 / 用户数据查询)

电商 / 后台系统中,常需关联「订单表 + 用户表 + 商品表」查询,示例:

复制代码
-- 查询2025年1月的有效订单,显示用户名、订单号、商品名称、支付金额
SELECT 
  u.username,
  o.order_no,
  g.goods_name,
  o.pay_amount
FROM order_info o
JOIN user u ON o.user_id = u.id
JOIN order_goods og ON o.id = og.order_id
JOIN goods g ON og.goods_id = g.id
WHERE 
  o.order_status = 2  -- 已支付
  AND o.create_time BETWEEN '2025-01-01' AND '2025-01-31';

3. 统计分析(运营报表常用)

按时间 / 维度统计数据,结合 DATE_FORMAT 处理时间字段:

复制代码
-- 统计近7天每日的订单量和交易额
SELECT 
  DATE_FORMAT(create_time, '%Y-%m-%d') AS 日期,
  COUNT(id) AS 订单量,
  SUM(pay_amount) AS 交易额
FROM order_info
WHERE create_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d')
ORDER BY 日期 ASC;

4. 子查询优化(替代复杂联查)

简单子查询可读性更高,工作中优先用「表子查询」替代多层联查:

复制代码
-- 查询近30天有下单行为的VIP用户
SELECT id, username, phone 
FROM user 
WHERE 
  vip_level >= 2 
  AND id IN (
    SELECT DISTINCT user_id 
    FROM order_info 
    WHERE create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
  );

5. 避免慢查询的核心技巧

  • 禁止 SELECT *:只查业务需要的字段,减少磁盘 IO 和网络传输;
  • 大表查询必加索引:WHERE 条件、JOIN 关联、ORDER BY 排序的字段必须建索引;
  • 分组 / 统计优先预处理:大数据量统计(如千万级),可提前用定时任务将结果存入「统计结果表」,查询时直接查结果表;
  • 慎用 LIKE '%xxx':前缀模糊匹配(%xxx)会失效索引,尽量用全文索引(Elasticsearch)或业务优化。

总结

DQL 是 MySQL 最核心的操作,掌握「SELECT + WHERE + GROUP BY + ORDER BY + LIMIT」的组合用法,能解决 90% 的日常查询需求。工作中需结合业务场景优化查询性能,重点关注索引、分页、联查的效率,避免慢查询影响系统性能。建议结合本文案例多动手练习,逐步提升复杂查询和性能优化能力。

相关推荐
无奈笑天下5 小时前
银河麒麟桌面OS使用分区编辑器将/backup分区删除并扩容至根分区参考教程
linux·数据库·经验分享·编辑器
tzhou6445211 小时前
MySQL备份与恢复
数据库·mysql·adb
一过菜只因11 小时前
MySql Jdbc
android·数据库·mysql
思成不止于此12 小时前
MySQL 查询实战(三):排序与综合练习
数据库·笔记·学习·mysql
茅坑的小石头12 小时前
数据库表设计,概念模型、逻辑模型、物理模型的区别,目标、主要内容、所处阶段、面向人群,数据库无关性
数据库
tebukaopu14812 小时前
mysql数据备份还原
数据库·mysql
zyxqyy&∞12 小时前
mysql代码小练-2
数据库·mysql
JIngJaneIL12 小时前
基于Java非遗传承文化管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot
+VX:Fegn089512 小时前
计算机毕业设计|基于springboot + vue心理健康管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计