1. MySQL基础操作入门
作为一名长期与MySQL打交道的开发者,我深知数据操作是数据库应用中最基础也最核心的部分。无论你是刚接触MySQL的新手,还是需要复习基础的老手,掌握增删改查(CRUD)操作都是必不可少的技能。本文将带你全面了解MySQL中的数据操作,从最简单的单表操作到复杂的多表查询,每个知识点我都会结合实际案例和多年使用经验进行讲解。
1.1 数据库环境准备
在开始之前,我们需要先准备好测试环境。我建议使用MySQL 8.0及以上版本,这是目前最稳定的版本。安装好MySQL后,创建一个测试数据库和几张示例表:
sql复制CREATE DATABASE school_db;
USE school_db;
-- 创建学生表
CREATE TABLE students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT,
height INT,
gender ENUM('男','女','保密'),
cls_id INT,
is_delete TINYINT DEFAULT 0
);
-- 创建教师表
CREATE TABLE teachers (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL
);
-- 创建班级表
CREATE TABLE classes (
id INT PRIMARY KEY AUTO_INCREMENT,
teacher_id INT,
cls_content TEXT,
cls_date DATETIME,
name VARCHAR(100),
FOREIGN KEY (teacher_id) REFERENCES teachers(id)
);
注意:在实际项目中,表结构设计要考虑字段类型、约束条件和索引优化。这里为了演示简化了一些细节。
2. 数据插入操作
2.1 基本INSERT语法
插入数据是数据库操作的第一步,MySQL提供了灵活的INSERT语句来满足不同场景的需求。最基本的语法格式如下:
sql复制INSERT INTO 表名 (字段1, 字段2, ...)
VALUES (值1, 值2, ...);
2.1.1 单行插入示例
向学生表中插入一条记录:
sql复制INSERT INTO students (name, age, height, gender, cls_id)
VALUES ('小明', 18, 180, '男', 1);
这里有几个需要注意的点:
- 字段列表和值列表必须一一对应
- 可以省略自增主键id的插入
- 对于允许NULL的字段,可以省略不写
2.1.2 多行插入技巧
MySQL支持一次插入多行数据,这在批量导入时非常高效:
sql复制INSERT INTO students (name, age, height, gender, cls_id) VALUES
('小月月', 18, 180, '女', 2),
('彭于晏', 29, 185, '男', 1),
('刘德华', 59, 175, '男', 2);
经验分享:在插入大量数据时,使用多行插入比单行插入效率高很多。我曾经测试过,插入1000条数据,多行插入比单行插入快10倍以上。
2.2 插入操作的注意事项
-
字段省略问题:如果省略字段列表,必须为所有列提供值(自增列除外)
sql复制-- 正确写法 INSERT INTO students VALUES (NULL, '黄蓉', 38, 160, '女', 1, 0); -- 错误写法(缺少字段) INSERT INTO students VALUES ('黄蓉', 38, 160, '女', 1, 0); -
特殊值处理:对于NULL值、空字符串和默认值的处理要特别注意
-
性能优化:大批量数据插入时,可以考虑以下优化方式:
- 使用多行INSERT语句
- 临时关闭索引更新(ALTER TABLE ... DISABLE KEYS)
- 使用LOAD DATA INFILE导入
3. 数据更新操作
3.1 基本UPDATE语法
更新数据的语法结构如下:
sql复制UPDATE 表名
SET 字段1=值1, 字段2=值2, ...
[WHERE 条件];
3.1.1 简单更新示例
将所有学生的姓名改为"邓超":
sql复制UPDATE students SET name = '邓超';
警告:这个操作会更新表中所有记录!在实际应用中,几乎总是需要加上WHERE条件来限定更新范围。
3.1.2 条件更新示例
更新id为1的学生的姓名和年龄:
sql复制UPDATE students
SET name = '彭于晏', age = 30
WHERE id = 1;
3.2 更新操作的高级技巧
3.2.1 基于当前值的更新
有时我们需要基于字段当前值进行更新,例如将所有学生年龄加1:
sql复制UPDATE students SET age = age + 1;
3.2.2 多表更新
MySQL支持通过JOIN进行多表更新,例如更新班级1所有学生的信息:
sql复制UPDATE students s
JOIN classes c ON s.cls_id = c.id
SET s.height = s.height + 1
WHERE c.name = 'python_01期';
3.2.3 使用子查询更新
sql复制UPDATE students
SET height = 180
WHERE cls_id IN (SELECT id FROM classes WHERE name LIKE 'python%');
注意事项:UPDATE操作没有"撤销"功能,执行前最好先用SELECT验证WHERE条件是否正确。我曾经因为WHERE条件写错,不小心更新了整个表的数据,不得不从备份恢复。
4. 数据删除操作
4.1 DELETE语句
DELETE用于删除表中的记录,基本语法:
sql复制DELETE FROM 表名 [WHERE 条件];
4.1.1 条件删除示例
删除id为1的学生记录:
sql复制DELETE FROM students WHERE id = 1;
4.1.2 删除所有记录
sql复制DELETE FROM students;
重要提示:不带WHERE条件的DELETE会删除表中所有数据,但不会重置自增计数器。
4.2 TRUNCATE语句
TRUNCATE用于快速清空整个表:
sql复制TRUNCATE TABLE students;
与DELETE的区别:
- TRUNCATE更快,因为它不记录单独的删除操作
- TRUNCATE会重置自增计数器
- TRUNCATE不能带WHERE条件
- TRUNCATE不能回滚(在某些数据库中)
4.3 删除操作的注意事项
- 外键约束:如果表有外键约束,删除操作可能会失败或被级联
- 性能考虑:删除大量数据时,TRUNCATE比DELETE更高效
- 备份习惯:执行删除操作前,考虑先备份数据
- 软删除模式:实际项目中常使用is_delete字段标记删除而非物理删除
5. 数据查询基础
5.1 基本SELECT语法
sql复制SELECT 列名 FROM 表名 [WHERE 条件];
5.1.1 查询所有列
sql复制SELECT * FROM students;
注意:生产环境中尽量避免使用SELECT *,明确列出需要的字段更好。
5.1.2 查询特定列
sql复制SELECT name, gender FROM students;
5.2 WHERE条件查询
WHERE子句用于过滤记录,支持多种运算符:
5.2.1 比较运算符
sql复制-- 年龄大于20的学生
SELECT * FROM students WHERE age > 20;
-- 身高等于180的学生
SELECT * FROM students WHERE height = 180;
-- 不是1班的学生
SELECT * FROM students WHERE cls_id <> 1;
5.2.2 逻辑运算符
sql复制-- 1班且年龄大于20的学生
SELECT * FROM students WHERE cls_id = 1 AND age > 20;
-- 1班或2班的学生
SELECT * FROM students WHERE cls_id = 1 OR cls_id = 2;
-- 不是1班的学生
SELECT * FROM students WHERE NOT cls_id = 1;
5.3 高级查询技巧
5.3.1 LIKE模糊查询
sql复制-- 名字包含"小"的学生
SELECT * FROM students WHERE name LIKE '%小%';
-- 姓"王"的学生
SELECT * FROM students WHERE name LIKE '王%';
通配符说明:
- %:匹配任意数量字符
- _:匹配单个字符
5.3.2 IN运算符
sql复制-- 查询id为1,3,5的学生
SELECT * FROM students WHERE id IN (1, 3, 5);
-- 查询不是1班也不是2班的学生
SELECT * FROM students WHERE cls_id NOT IN (1, 2);
5.3.3 BETWEEN范围查询
sql复制-- 查询年龄在18到20之间的学生
SELECT * FROM students WHERE age BETWEEN 18 AND 20;
-- 查询身高不在170到180之间的学生
SELECT * FROM students WHERE height NOT BETWEEN 170 AND 180;
6. 查询结果处理
6.1 DISTINCT去重
sql复制-- 查询所有不同的性别
SELECT DISTINCT gender FROM students;
-- 查询班级id不重复的组合
SELECT DISTINCT cls_id, gender FROM students;
6.2 ORDER BY排序
sql复制-- 按身高升序排列
SELECT * FROM students ORDER BY height ASC;
-- 按年龄降序排列
SELECT * FROM students ORDER BY age DESC;
-- 多列排序:先按班级升序,再按身高降序
SELECT * FROM students ORDER BY cls_id ASC, height DESC;
注意:ASC可以省略,因为它是默认值。对于文本排序,MySQL默认使用字符集的排序规则。
6.3 LIMIT分页
sql复制-- 查询前5条记录
SELECT * FROM students LIMIT 5;
-- 查询第6-10条记录(LIMIT 偏移量,数量)
SELECT * FROM students LIMIT 5, 5;
-- 查询身高前三的学生
SELECT * FROM students ORDER BY height DESC LIMIT 3;
分页技巧:在Web应用中,常用LIMIT实现分页,例如每页10条,第3页就是LIMIT 20, 10。
7. 聚合函数与分组
7.1 常用聚合函数
MySQL提供以下聚合函数:
- COUNT():计数
- SUM():求和
- AVG():平均值
- MAX():最大值
- MIN():最小值
7.1.1 基本使用
sql复制-- 学生总数
SELECT COUNT(*) FROM students;
-- 平均年龄
SELECT AVG(age) FROM students;
-- 最大身高
SELECT MAX(height) FROM students;
-- 1班学生的平均身高
SELECT AVG(height) FROM students WHERE cls_id = 1;
7.2 GROUP BY分组
sql复制-- 每个班级的学生人数
SELECT cls_id, COUNT(*) FROM students GROUP BY cls_id;
-- 每个班级不同性别的学生人数
SELECT cls_id, gender, COUNT(*)
FROM students
GROUP BY cls_id, gender;
7.2.1 HAVING子句
HAVING用于过滤分组后的结果:
sql复制-- 学生人数大于3的班级
SELECT cls_id, COUNT(*) as cnt
FROM students
GROUP BY cls_id
HAVING cnt > 3;
WHERE与HAVING的区别:WHERE在分组前过滤,HAVING在分组后过滤。
8. 多表连接查询
8.1 内连接(INNER JOIN)
内连接返回两表中匹配的记录:
sql复制-- 显示学生及其班级信息
SELECT s.name, c.name as class_name
FROM students s
INNER JOIN classes c ON s.cls_id = c.id;
8.2 外连接
8.2.1 左外连接(LEFT JOIN)
返回左表所有记录,即使右表没有匹配:
sql复制-- 所有学生及其班级信息(没有班级的学生也会显示)
SELECT s.name, c.name as class_name
FROM students s
LEFT JOIN classes c ON s.cls_id = c.id;
8.2.2 右外连接(RIGHT JOIN)
返回右表所有记录,即使左表没有匹配:
sql复制-- 所有班级及其学生信息(没有学生的班级也会显示)
SELECT c.name as class_name, s.name
FROM students s
RIGHT JOIN classes c ON s.cls_id = c.id;
8.3 多表连接
sql复制-- 学生、班级和教师信息
SELECT s.name as student, c.name as class, t.name as teacher
FROM students s
JOIN classes c ON s.cls_id = c.id
JOIN teachers t ON c.teacher_id = t.id;
性能提示:连接多个表时,确保连接字段有索引,否则性能会很差。
9. 子查询
9.1 标量子查询
返回单个值的子查询:
sql复制-- 查询比平均身高高的学生
SELECT * FROM students
WHERE height > (SELECT AVG(height) FROM students);
9.2 列子查询
返回一列值的子查询:
sql复制-- 查询赵老师班级的学生
SELECT * FROM students
WHERE cls_id IN (
SELECT id FROM classes
WHERE teacher_id = (
SELECT id FROM teachers WHERE name = '赵老师'
)
);
9.3 行子查询
返回一行数据的子查询:
sql复制-- 查询与刘德华同班同龄的学生
SELECT * FROM students
WHERE (cls_id, age) = (
SELECT cls_id, age FROM students WHERE name = '刘德华'
)
AND name != '刘德华';
9.4 EXISTS子查询
sql复制-- 查询有学生的班级
SELECT * FROM classes c
WHERE EXISTS (
SELECT 1 FROM students s WHERE s.cls_id = c.id
);
EXISTS通常比IN效率更高,特别是当子查询结果集很大时。
10. 实战经验分享
经过多年的MySQL使用,我总结了一些宝贵的经验:
- 索引优化:WHERE、JOIN、ORDER BY涉及的字段应该建立索引
- 避免全表扫描:尽量使用索引列作为查询条件
- EXPLAIN分析:复杂查询前先用EXPLAIN分析执行计划
- 批量操作:批量INSERT比单条INSERT效率高很多
- 事务控制:更新操作应该放在事务中,确保数据一致性
- 备份策略:定期备份重要数据,DELETE前先SELECT确认
- 字段选择:只查询需要的字段,避免SELECT *
- 连接优化:多表连接时,小表驱动大表效率更高
MySQL的功能非常丰富,本文涵盖了最常用的数据操作。要真正掌握这些知识,最好的方法是在实际项目中多加练习。遇到问题时,不要忘记查阅官方文档,它是最权威的参考资料。