MySQL 从入门到精通(二):DML 数据操作与 DQL 数据查询详解
在数据库操作中,数据的增删改查(CRUD)是最基础也最常用的功能。MySQL 作为最流行的关系型数据库之一,其数据操作语言(DML)和数据查询语言(DQL)是每个开发者必须掌握的核心技能。本文将结合详细语法、示例代码和实践场景,全面解析 MySQL 的 DML 与 DQL 操作。
一、DML:数据操作语言(Data Manipulation Language)
DML 用于对数据库中的表数据进行 ** 增(Insert)、删(Delete)、改(Update)** 操作,是日常开发中使用频率最高的 SQL 类型。
1.1 插入数据:INSERT 与 REPLACE
1.1.1 INSERT 语句基础用法
INSERT 语句用于向表中添加新记录,支持单条插入、批量插入和指定字段插入。
(1)全字段单条插入
当表的所有字段都需要赋值时,可省略字段列表,直接按表结构顺序插入值:
-- 语法:INSERT [INTO] 表名 VALUES (值1, 值2, ..., 值n);
-- 示例:向student2表插入一条全字段记录
INSERT INTO student2 VALUES (1001, '李成峰', '男', '2000-10-1');
- 注意:值的顺序必须与表字段定义顺序完全一致;字符串和日期类型需用单引号(
' '
)或双引号(" "
)包裹;若字段有默认值(如sex
默认女
),可跳过该字段(但需显式指定字段列表)。
(2)指定字段单条插入
当只需要插入部分字段时,需显式指定字段列表:
-- 语法:INSERT [INTO] 表名 (字段1, 字段2, ...) VALUES (值1, 值2, ...);
-- 示例:向student2表插入id和name字段(sex使用默认值'女',birthday为NULL)
INSERT INTO student2 (id, name) VALUES (1002, '薛佳颖');
- 效果:未指定的字段会使用默认值(若有)或
NULL
(无默认值时)。
(3)批量插入多条数据
通过逗号分隔多个(值列表)
,可一次性插入多条记录,提升插入效率(减少网络交互次数):
-- 语法(全字段):INSERT [INTO] 表名 VALUES (值1), (值2), ...;
-- 语法(指定字段):INSERT [INTO] 表名 (字段列表) VALUES (值1), (值2), ...;
-- 示例:批量插入3条记录(全字段)
INSERT student2 VALUES
(1003, '孙德胜', '男', '1998-12-31'),
(1004, '凤飞飞', '女', '2009-03-15'),
(1005, '张无忌', '男', NOW()); -- NOW()获取当前时间
- 提示:
INTO
关键字可省略;批量插入时若某条记录格式错误,可能导致全部失败(取决于事务设置)。
1.1.2 REPLACE 语句:冲突时覆盖插入
REPLACE 的功能与 INSERT 类似,但当插入的记录主键或唯一约束冲突时,会先删除旧记录,再插入新记录(相当于DELETE+INSERT
的原子操作)。
(1)语法格式
-- 格式1:直接插入值列表
REPLACE INTO 表名 [(字段列表)] VALUES (值列表);-- 格式2:从其他表复制数据插入
REPLACE [INTO] 目标表 [(字段列表)] SELECT 字段列表 FROM 源表 WHERE 条件;-- 格式3:通过SET赋值
REPLACE [INTO] 表名 SET 字段1=值1, 字段2=值2;
(2)示例演示
假设student2
表的id
是主键,插入id=1005
的记录时冲突:
-- 直接插入冲突会报错(Duplicate entry)
INSERT student2 VALUES (1005, '尹志平', '男', '1235-09-01'); -- 报错-- 使用REPLACE覆盖旧记录
REPLACE student2 VALUES (1005, '尹志平', '男', '1235-09-01'); -- 成功,旧记录被删除
- 效果:执行后
id=1005
的记录被更新为新值,影响行数为 2(删除 1 行 + 插入 1 行)。
1.2 更新数据:UPDATE
UPDATE 语句用于修改表中已存在的记录,核心是通过WHERE
条件定位需要修改的行。
1.2.1 基础语法
-- 语法:UPDATE 表名 SET 字段1=值1, 字段2=值2 [WHERE 条件];
-- 示例:将id=1002的学生生日修改为2012-12-12
UPDATE student2 SET birthday = '2012-12-12' WHERE id = 1002;
1.2.2 注意事项
- 必须加 WHERE 条件:若省略
WHERE
,会修改表中所有记录!-- 危险操作:清空所有生日字段(设置为当前时间) UPDATE student2 SET birthday = NOW();
- 多字段更新:多个字段用逗号分隔,支持表达式(如
salary = salary * 1.1
)。 - 事务保护:生产环境中建议在事务中执行 UPDATE,避免误操作后无法回滚。
1.3 删除数据:DELETE 与 TRUNCATE
删除数据有两种常用方式:DELETE
逐行删除和TRUNCATE
全表清空。
1.3.1 DELETE 语句
-- 语法:DELETE FROM 表名 [WHERE 条件];
-- 示例1:删除id=1005的记录
DELETE FROM student2 WHERE id = 1005;-- 示例2:清空表(危险!需谨慎)
DELETE FROM student2; -- 无WHERE条件会删除所有记录
- 特点:逐行删除,记录操作日志(可回滚);适合删除部分数据;删除大表时效率低(逐行扫描)。
1.3.2 TRUNCATE 语句
-- 语法:TRUNCATE TABLE 表名;
-- 示例:清空student2表
TRUNCATE TABLE student2;
- 特点:直接删除表数据并重置自增主键(若有);不记录日志(不可回滚);速度远快于
DELETE
(直接释放数据页);适合全表清空。
1.3.3 DELETE、TRUNCATE、DROP 的区别
操作 | 功能 | 表结构 | 可回滚 | 效率 |
---|---|---|---|---|
DELETE | 删除部分或全部数据 | 保留 | 是 | 低(逐行) |
TRUNCATE | 清空全部数据 | 保留 | 否 | 高(页级) |
DROP | 删除表(数据 + 结构) | 删除 | 否 | 最高 |
二、DQL:数据查询语言(Data Query
Language)
DQL 是数据库的 “查询引擎”,通过SELECT
语句从表中提取数据,支持复杂的条件过滤、聚合统计、分组排序和多表关联。
2.1 SELECT 基础语法
SELECT 的完整语法结构如下(子句执行顺序关键!):
SELECT [DISTINCT] 字段列表
FROM 表名列表
[WHERE 条件]
[GROUP BY 分组字段]
[HAVING 分组过滤条件]
[ORDER BY 排序字段]
[LIMIT 分页参数];
- 执行顺序:
FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT
2.2 基本查询
2.2.1 查询所有字段
-- 语法:SELECT * FROM 表名;
-- 示例:查询student3表所有数据
SELECT * FROM student3;
- 注意:生产环境中不建议使用
*
(影响性能且可读性差),应明确指定字段。
2.2.2 查询指定字段
-- 语法:SELECT 字段1, 字段2 FROM 表名;
-- 示例:查询student3表的姓名和数学成绩
SELECT name, math FROM student3;
2.2.3 字段别名
通过AS
为字段或表起别名,提升可读性:
-- 语法:SELECT 字段1 AS 别名1, 字段2 AS 别名2 FROM 表名;
-- 示例:查询姓名(别名"姓名")和语文成绩(别名"语文")
SELECT name AS "姓名", chinese AS "语文" FROM student3;
- 提示:
AS
可省略;别名含特殊字符时需用双引号(" "
)。
2.2.4 去重查询(DISTINCT)
DISTINCT
用于去除重复的记录(所有指定字段组合相同视为重复):
-- 语法:SELECT DISTINCT 字段列表 FROM 表名;
-- 示例:查询不同姓名和英语成绩的组合
SELECT DISTINCT name, english FROM student3;
2.3 条件查询(WHERE 子句)
通过WHERE
子句过滤符合条件的记录,支持算术、比较、逻辑、模糊等运算符。
2.3.1 算术运算符
MySQL 支持+
、-
、*
、/
、%
(取模)运算:
-- 示例:计算总分(语文+数学+英语)
SELECT name, chinese + math + english AS "总分" FROM student3;
- 注意:字符串与数字运算时,非数字字符串转为 0(如
"china"+80=80
);NULL
参与运算结果为NULL
。
2.3.2 比较运算符
常用=
、>
、<
、>=
、<=
、!=
(或<>
)、BETWEEN...AND
、IN
、LIKE
等:
运算符 | 说明 | 示例 |
---|---|---|
BETWEEN a AND b | 值在 a 到 b 之间(包含边界) | english BETWEEN 80 AND 90 |
IN (值列表) | 值在指定列表中 | math IN (85, 90) |
LIKE 模式 | 模糊匹配(% 任意字符,_ 单个字符) | name LIKE '李%' (姓李) |
IS NULL | 值为 NULL | chinese IS NULL |
示例:
-- 查询英语成绩>90的学生
SELECT name, english FROM student3 WHERE english > 90;-- 查询数学成绩在85或90的学生
SELECT * FROM student3 WHERE math IN (85, 90);-- 查询姓名以"李"开头的学生
SELECT name FROM student3 WHERE name LIKE '李%';-- 查询语文成绩为NULL的学生(未考试)
SELECT * FROM student3 WHERE chinese IS NULL;
2.3.3 逻辑运算符
AND
(与)、OR
(或)、NOT
(非)用于组合多个条件:
-- 查询性别为女且总分>200的学生
SELECT name FROM student3 WHERE gender = '女' AND (chinese + math + english) > 200;-- 查询数学<60或英语<60的学生
SELECT * FROM student3 WHERE math < 60 OR english < 60;
2.4 聚合函数(纵向统计)
聚合函数用于对一列数据进行统计计算,常见函数:COUNT
(计数)、SUM
(求和)、AVG
(平均)、MAX
(最大值)、MIN
(最小值)。
2.4.1 语法与示例
-- 语法:SELECT 聚合函数(字段) FROM 表名 [WHERE 条件];
-- 示例1:统计student3表总人数
SELECT COUNT(*) FROM student3; -- COUNT(*)统计所有行(含NULL)-- 示例2:计算男生数学平均分
SELECT AVG(math) FROM student3 WHERE gender = '男';-- 示例3:查询最高英语成绩
SELECT MAX(english) FROM student3;
2.4.2 注意事项
COUNT(字段)
统计非 NULL 值的数量;COUNT(*)
统计所有行(推荐主键字段COUNT(主键)
提升效率)。- 聚合函数不处理
NULL
值(如AVG
忽略NULL
)。 - 聚合函数不能在
WHERE
子句中使用(需用HAVING
)。
2.5 分组查询(GROUP BY)
GROUP BY
用于将数据按某字段分组,通常与聚合函数配合统计各组的信息。
2.5.1 基础语法
-- 语法:SELECT 分组字段, 聚合函数 FROM 表名 [WHERE 条件] GROUP BY 分组字段 [HAVING 分组条件];
-- 示例1:按性别统计人数
SELECT gender, COUNT(*) AS "人数" FROM student3 GROUP BY gender;-- 示例2:按班级统计平均分(transcript表)
SELECT class_id AS "班级", ROUND(AVG(score), 2) AS "平均分" FROM transcript GROUP BY class_id;
2.5.2 分组过滤(HAVING 子句)
HAVING
用于对分组后的结果进一步过滤(可使用聚合函数):
-- 示例:查询平均分>90的班级
SELECT class_id, AVG(score) FROM transcript GROUP BY class_id HAVING AVG(score) > 90;
2.5.3 WHERE 与 HAVING 的区别
子句 | 执行时机 | 作用对象 | 能否使用聚合函数 |
---|---|---|---|
WHERE | 分组前 | 原始行 | 否 |
HAVING | 分组后 | 分组后的组 | 是 |
2.6 排序查询(ORDER BY)
ORDER BY
用于对查询结果按字段排序,支持升序(ASC
,默认)和降序(DESC
)。
2.6.1 基础语法
-- 语法:SELECT 字段列表 FROM 表名 ORDER BY 字段1 [ASC/DESC], 字段2 [ASC/DESC];
-- 示例1:按数学成绩降序排序
SELECT name, math FROM student3 ORDER BY math DESC;-- 示例2:先按性别升序,再按语文降序(多字段排序)
SELECT * FROM student3 ORDER BY gender ASC, chinese DESC;
2.6.2 中文排序问题
由于 MySQL 默认按字符编码(如utf8mb4
)排序,中文可能出现乱序,需用CONVERT
指定编码:
-- 按姓名的GBK编码排序(更符合中文习惯)
SELECT * FROM student3 ORDER BY CONVERT(name USING gbk) ASC;
2.7 分页查询(LIMIT)
LIMIT
用于限制查询结果的数量,是实现分页(如 “第 2 页显示 10 条”)的关键。
2.7.3 语法格式
-- 格式1:LIMIT 记录数 (从第1条开始取n条)
-- 格式2:LIMIT 偏移量, 记录数 (从偏移量+1条开始取n条)
-- 格式3:LIMIT 记录数 OFFSET 偏移量 (同格式2)
-- 示例:
SELECT * FROM student3 LIMIT 3; -- 取前3条
SELECT * FROM student3 LIMIT 2, 3; -- 从第3条开始取3条(索引从0开始)
SELECT * FROM student3 LIMIT 3 OFFSET 2; -- 同上
三、多表关联查询
实际业务中,数据通常分散在多张表中(如学生表、课程表、成绩表),需通过关联查询整合数据。
3.1 表间关系
- 一对一:一张表的一条记录对应另一张表的一条记录(如用户表与身份证表)。
- 一对多:一张表的一条记录对应另一张表的多条记录(如班级表与学生表)。
- 多对多:两张表的记录相互对应多条(如学生表与课程表,需中间表
sc
关联)。
3.2 关联查询类型
3.2.1 内连接(INNER JOIN)
仅返回两张表中匹配的记录(最常用):
-- 语法:SELECT 字段 FROM 表1 INNER JOIN 表2 ON 表1.字段=表2.字段;
-- 示例:查询学生姓名、课程名和成绩(student→sc→course)
SELECT s.sname, c.cname, sc.score
FROM student s
INNER JOIN sc ON s.sno = sc.sno
INNER JOIN course c ON sc.cno = c.cno;
3.2.2 左连接(LEFT JOIN)
返回左表所有记录,右表无匹配时用NULL
填充:
-- 语法:SELECT 字段 FROM 表1 LEFT JOIN 表2 ON 表1.字段=表2.字段;
-- 示例:查询所有学生的成绩(包括未选课的学生)
SELECT s.sname, sc.score
FROM student s
LEFT JOIN sc ON s.sno = sc.sno;
3.2.3 右连接(RIGHT JOIN)
与左连接相反,返回右表所有记录:
-- 语法:SELECT 字段 FROM 表1 RIGHT JOIN 表2 ON 表1.字段=表2.字段;
3.2.4 全连接(FULL JOIN)
返回两张表所有记录,无匹配时用NULL
填充(MySQL 不直接支持,需用UNION
模拟):
SELECT * FROM 表1 LEFT JOIN 表2 ON 条件
UNION
SELECT * FROM 表1 RIGHT JOIN 表2 ON 条件;
3.3 多表关联示例
假设需要查询 “学生姓名、课程名、教师姓名、成绩”,涉及student
(学生)、sc
(成绩)、course
(课程)、teacher
(教师)四张表:
SELECT s.sname AS "学生姓名",c.cname AS "课程名",t.tname AS "教师姓名",sc.score AS "成绩"
FROM student s
JOIN sc ON s.sno = sc.sno -- 学生与成绩关联
JOIN course c ON sc.cno = c.cno -- 成绩与课程关联
JOIN teacher t ON c.tno = t.tno; -- 课程与教师关联
四、总结
本文全面解析了 MySQL 的 DML(增删改)和 DQL(查询)操作,涵盖从基础语法到复杂查询的全场景。实际开发中,需注意以下几点:
- 性能优化:避免
SELECT *
,合理使用索引,分页时LIMIT
偏移量过大需优化(如记录上次查询的最大 ID)。 - 事务安全:重要操作(如转账)需在事务中执行,避免数据不一致。
- 字段约束:合理使用主键、唯一约束,减少
REPLACE
或UPDATE
的冲突概率。
掌握这些技能后,你可以高效地操作 MySQL 数据库,处理各种业务场景的数据需求。
附:示例表结构与数据(可直接复制到 MySQL 执行)
-- 创建student3表
CREATE TABLE student3(id INT,name VARCHAR(20),gender VARCHAR(4),chinese INT,math INT,english INT
);-- 插入示例数据
INSERT INTO student3 VALUES
(1,'张三丰','男',87,68,92),
(2,'林诗音','女',67,99,34),
(3,'孙长胜','男',88,90,100),
(4,'黄蓉','女',90,69,81),
(5,'李守财','男',82,32,74),
(6,'赵子龙','男',76,85,97),
(7,'钱多多','女',75,65,32),
(8,'钱多多','女',75,65,32);