MySQL 学习笔记(第三期):SQL 语言之数据操作与单表查询
本笔记承接第二期,进入数据操作(DML)和单表查询(DQL)的核心内容。涵盖:插入(INSERT)、更新(UPDATE)、删除(DELETE)数据,以及强大的 SELECT 查询语句,包括条件过滤、分组聚合、排序、分页等。所有代码均加以整理和注释。
一、DML 语句:数据操纵语言
DML 用于对表中的数据进行增加、修改、删除操作。
1.1 插入数据(INSERT)
基本语法:
sql
INSERT [INTO] tbl_name [(col1, col2, ...)] VALUES (val1, val2, ...), (val1, val2, ...), ...;
范例表(后续所有操作基于此表):
sql
-- 创建测试表 stu
CREATE TABLE stu (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
age TINYINT UNSIGNED,
gender ENUM('M','F') DEFAULT 'M',
is_del BOOL DEFAULT FALSE
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4;
插入单条记录
sql
-- 方式1:给出所有字段值(顺序与表定义一致)
INSERT INTO stu VALUES(10, 'xiaoming', 20, 'M', 0);
-- 方式2:只给部分字段,其他使用默认值或 NULL
INSERT INTO stu (name, age) VALUES ('xiaohong', 18); -- gender 默认 'M',is_del 默认 0
插入多条记录
sql
INSERT INTO stu (name, age) VALUES
('test1', 20),
('test2', 21),
('test3', 22);
使用默认值或 NULL
sql
-- age 和 gender 有默认值或允许 NULL
INSERT INTO stu (age, is_del) VALUES (23, 0);
如果记录存在则更新(ON DUPLICATE KEY UPDATE)
sql
-- 假设 id=12 已存在,则更新 name 为 'zhangsan';否则插入新记录
INSERT INTO stu (id, name) VALUES (12, 'zhangsan')
ON DUPLICATE KEY UPDATE name = 'zhangsan';
将查询结果插入到表中
sql
-- 从原表复制数据到另一张表(字段必须匹配)
INSERT INTO stu (name, age, gender)
SELECT name, age, gender FROM stu WHERE id = 11;
1.2 更新数据(UPDATE)
语法:
sql
UPDATE tbl_name SET col1 = val1, col2 = val2 ... [WHERE condition];
⚠️ 重要 :如果不加 WHERE 条件,将更新表中所有记录!生产环境务必小心。
无条件更新(全表更新)
sql
UPDATE stu SET age = 30, gender = 'F'; -- 所有记录年龄改为 30,性别改为 F
条件更新
sql
-- 单条件
UPDATE stu SET age = 31 WHERE id > 15;
-- 多条件(OR)
UPDATE stu SET age = 21, gender = 'M' WHERE id = 15 OR name IS NULL;
-- 多条件(AND)
UPDATE stu SET age = 22, gender = 'M' WHERE id = 17 AND name IS NULL;
安全更新模式(防止误操作)
sql
-- 启动安全更新模式(要求 WHERE 条件必须使用索引列)
mysql -U
# 或在配置文件 /etc/my.cnf.d/client.cnf 中添加 safe-updates
-- 以下语句会被拒绝,因为没有使用主键条件
UPDATE stu SET age = 22; -- ERROR 1175
UPDATE stu SET age = 23 WHERE name = 'test3'; -- ERROR 1175(name 不是索引)
-- 只有使用主键条件才能执行
UPDATE stu SET age = 23 WHERE id = 17; -- OK
1.3 删除数据(DELETE)
语法:
sql
DELETE FROM tbl_name [WHERE condition];
⚠️ 同样重要:不加 WHERE 会删除表中所有数据。
条件删除
sql
DELETE FROM stu WHERE id = 10;
逻辑删除(推荐生产环境使用)
为避免物理删除导致数据不可恢复,通常增加一个"删除标记"字段,用 UPDATE 代替 DELETE。
sql
-- 添加标记字段(已有 is_del)
ALTER TABLE stu ADD is_del BOOL DEFAULT FALSE;
-- 逻辑删除:将标记设为 1 表示已删除
UPDATE stu SET is_del = 1 WHERE id = 11;
-- 查询时只取未删除的记录
SELECT * FROM stu WHERE is_del = 0;
清空表
sql
TRUNCATE TABLE tbl_name; -- DDL 语句,速度快,不可回滚,自增列重置
DELETE FROM tbl_name; -- DML 语句,可回滚,自增列不重置
二、DQL 语句:数据查询语言(单表查询)
SELECT 是 SQL 中最复杂的语句,用于从表中检索数据。
2.1 基本查询
sql
SELECT col1, col2, ... FROM tbl_name [WHERE condition];
查询常量、表达式、函数
sql
SELECT 123; -- 常量
SELECT 'gqd' AS 'laogao'; -- 字符串别名
SELECT 1+2*3; -- 表达式
SELECT NOW(); -- 函数
SELECT VERSION(); -- 版本函数
查询所有字段
sql
SELECT * FROM stu;
查询指定字段(可设置别名)
sql
SELECT id AS 学号, name AS 姓名, age AS 年纪 FROM stu;
2.2 WHERE 条件过滤
比较运算符
| 运算符 | 含义 |
|---|---|
= |
等于 |
<> 或 != |
不等于 |
> , >= , < , <= |
大小比较 |
BETWEEN ... AND ... |
范围之间(包含边界) |
IN (set) |
匹配集合中的任意一个 |
LIKE |
模糊匹配(% 任意长度,_ 单个字符) |
IS NULL |
空值判断 |
逻辑运算符
| 运算符 | 含义 |
|---|---|
AND (&&) |
与 |
OR (||) |
或 |
NOT (!) |
非 |
范例
sql
-- 等值条件
SELECT id, name FROM stu WHERE id = 11;
-- 多值匹配(IN)
SELECT id, name FROM stu WHERE id IN (11, 15, 19);
-- 范围匹配
SELECT id, name FROM stu WHERE id BETWEEN 12 AND 15;
-- 取反
SELECT id, name FROM stu WHERE id NOT BETWEEN 12 AND 15;
SELECT id, name FROM stu WHERE id NOT IN (12, 15, 19);
-- 组合条件
SELECT id, name FROM stu WHERE id >= 11 AND id < 14;
SELECT id, name FROM stu WHERE id <= 12 OR id > 19;
-- NULL 判断(注意不能用 = NULL)
SELECT id, name FROM stu WHERE name IS NULL;
SELECT id, name FROM stu WHERE name IS NOT NULL;
-- 模糊匹配
SELECT id, name FROM stu WHERE name LIKE 'x%'; -- 以 x 开头
SELECT id, name FROM stu WHERE name LIKE '%ng'; -- 以 ng 结尾
SELECT id, name FROM stu WHERE name LIKE '%n%'; -- 包含 n
2.3 聚合函数与分组(GROUP BY)
聚合函数对一组值进行计算并返回单个值。
| 函数 | 说明 |
|---|---|
COUNT(*) |
统计记录数(包括 NULL) |
COUNT(col) |
统计某列非 NULL 值的个数 |
SUM(col) |
求和 |
AVG(col) |
平均值 |
MAX(col) |
最大值 |
MIN(col) |
最小值 |
注意:聚合函数忽略 NULL 值(COUNT(*) 除外)。
统计总数
sql
SELECT COUNT(*) AS total FROM stu; -- 总记录数
SELECT COUNT(name) FROM stu; -- name 非空数量
SELECT COUNT(id) FROM stu; -- 主键非空数量
最大值、最小值、平均值、求和
sql
SELECT MAX(id), MIN(id), AVG(age), SUM(age) FROM stu;
分组统计(GROUP BY)
sql
-- 按性别分组,统计每组人数
SELECT COUNT(*), gender FROM stu GROUP BY gender;
-- 按性别分组,计算总年龄和平均年龄
SELECT SUM(age), AVG(age), gender FROM stu GROUP BY gender;
分组前过滤(WHERE)与分组后过滤(HAVING)
WHERE:在分组前对原始记录进行过滤HAVING:在分组后对聚合结果进行过滤
sql
-- 先过滤掉 name 为 NULL 的记录,再分组
SELECT COUNT(*), MAX(id), AVG(age), gender
FROM stu
WHERE name IS NOT NULL
GROUP BY gender;
-- 分组后只显示总数为 3 的分组
SELECT COUNT(*) AS total, MAX(id), AVG(age), gender
FROM stu
WHERE name IS NOT NULL
GROUP BY gender
HAVING total = 3;
规则 :
SELECT中出现的非聚合字段必须出现在GROUP BY子句中。
2.4 排序(ORDER BY)
sql
SELECT * FROM stu ORDER BY col1 [ASC|DESC], col2 [ASC|DESC], ...;
ASC:升序(默认)DESC:降序
sql
-- 按 id 升序
SELECT id, name, age FROM stu ORDER BY id;
-- 按 id 降序
SELECT id, name, age FROM stu ORDER BY id DESC;
-- 先按年龄降序,再按 id 升序
SELECT id, name, age FROM stu ORDER BY age DESC, id ASC;
2.5 去除重复行(DISTINCT)
sql
SELECT DISTINCT age FROM stu ORDER BY age DESC;
SELECT DISTINCT(age) FROM stu; -- 括号可选
2.6 分页查询(LIMIT)
sql
SELECT * FROM tbl_name LIMIT [offset,] row_count;
offset:跳过的行数(从 0 开始)row_count:返回的行数
sql
-- 第一页,每页 3 条
SELECT id, name FROM stu LIMIT 0, 3;
-- 第二页,每页 3 条
SELECT id, name FROM stu LIMIT 3, 3;
-- 第三页,每页 3 条
SELECT id, name FROM stu LIMIT 6, 3;
三、综合范例演练
基于以下数据(stu 表),请尝试写出查询语句。
sql
-- 查看全部数据
SELECT * FROM stu;
+----+----------+------+--------+--------+
| id | name | age | gender | is_del |
+----+----------+------+--------+--------+
| 11 | xiaohong | 30 | F | 1 |
| 12 | zhangsan | 30 | F | 0 |
| 13 | xiaozhou | 30 | F | 1 |
| 14 | test1 | 30 | F | 1 |
| 15 | test2 | 21 | M | 1 |
| 16 | test3 | 31 | F | 1 |
| 17 | NULL | 23 | F | 0 |
| 18 | xiaohong | 31 | F | 1 |
| 19 | xiaohong | 31 | F | 1 |
| 20 | zhangsan | 31 | F | 1 |
+----+----------+------+--------+--------+
查询练习:
sql
-- 1. 查询所有女性(gender='F')且年龄大于等于 30 的姓名和年龄
SELECT name, age FROM stu WHERE gender = 'F' AND age >= 30;
-- 2. 统计男女各自的人数
SELECT gender, COUNT(*) FROM stu GROUP BY gender;
-- 3. 查询年龄最大的前 3 名(按年龄降序,取前3)
SELECT name, age FROM stu ORDER BY age DESC LIMIT 3;
-- 4. 查询名字中包含 'hong' 的所有记录
SELECT * FROM stu WHERE name LIKE '%hong%';
-- 5. 查询未被逻辑删除(is_del=0)的记录
SELECT * FROM stu WHERE is_del = 0;
四、本期知识点归纳一览表
| 类别 | 知识点 | 关键语法/命令 |
|---|---|---|
| 插入数据 | 单条/多条插入,指定字段,默认值 | INSERT INTO ... VALUES ... |
| 存在则更新 | ON DUPLICATE KEY UPDATE |
|
| 查询结果插入 | INSERT ... SELECT ... |
|
| 更新数据 | 条件更新,全表更新(慎用) | UPDATE ... SET ... WHERE ... |
| 安全更新模式 | --safe-updates 或配置 safe-updates |
|
| 删除数据 | 条件删除,逻辑删除(推荐) | DELETE FROM ... WHERE ... |
| 清空表 | TRUNCATE TABLE vs DELETE FROM |
|
| 基本查询 | 常量/表达式/函数查询 | SELECT NOW(); |
| 字段别名 | col AS alias |
|
| 条件过滤 | 比较运算符 | =, <>, >, <, BETWEEN, IN |
| 逻辑运算符 | AND, OR, NOT |
|
| 空值判断 | IS NULL, IS NOT NULL |
|
| 模糊匹配 | LIKE (%, _) |
|
| 聚合与分组 | 聚合函数 | COUNT, SUM, AVG, MAX, MIN |
| 分组 | GROUP BY |
|
| 分组后过滤 | HAVING |
|
| 排序 | 升序/降序 | ORDER BY ... ASC/DESC |
| 去重 | 去除重复行 | SELECT DISTINCT ... |
| 分页 | 限制返回行数 | LIMIT offset, row_count |
下一期预告:SQL 语言之多表查询(内连接、外连接、自连接、子查询、联合查询,含王者荣耀实战案例)。