MySQL 核心必知:表的增删改查(CRUD)实战指南
📖 1. 引言
🎯 CRUD 是数据库操作的核心,代表 Create(创建)、Retrieve(读取)、Update(更新)、Delete(删除)。掌握 CRUD 是学好 MySQL 的第一步,也是面试中的高频考点。
本文将基于经典的学生成绩管理场景,带你从零掌握 MySQL 表的增删改查操作。所有代码均可直接复制运行,并配有详细的执行结果,助你快速上手,轻松应对面试。
🛠️ 2. 准备工作:创建学生表
在开始 CRUD 之前,我们先创建一张学生表作为操作对象。
-- 创建一张学生表
CREATE TABLE students (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
sn INT NOT NULL UNIQUE COMMENT '学号',
name VARCHAR(20) NOT NULL,
qq VARCHAR(20)
);
💡 字段说明:
id:主键,自增,无符号整数。sn:学号,唯一且非空。name:姓名,非空。
➕ 3. Create:插入数据
3.1 单行数据 + 全列插入
-- 插入一条记录,value_list 数量必须和定义表的列的数量及顺序一致
INSERT INTO students VALUES (101, 10001, '孙悟空', '11111');
Query OK, 1 row affected (0.02 sec)
-- 注意,这里在插入的时候,也可以不用指定 id(当然,那时候就需要明确插入数据到那些列了),那么 mysql 会使用默认的值进行自增。
INSERT INTO students VALUES (100, 10000, '唐三藏', NULL);
Query OK, 1 row affected (0.02 sec)
-- 查看插入结果
SELECT * FROM students;
执行结果:
| id | sn | name | |
|---|---|---|---|
| 100 | 10000 | 唐三藏 | NULL |
| 101 | 10001 | 孙悟空 | 11111 |
3.2 多行数据 + 指定列插入
-- 插入两条记录,value_list 数量必须和指定列数量及顺序一致
INSERT INTO students (id, sn, name) VALUES
(102, 20001, '曹孟德'),
(103, 20002, '孙仲谋');
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
-- 查看插入结果
SELECT * FROM students;
执行结果:
| id | sn | name | |
|---|---|---|---|
| 100 | 10000 | 唐三藏 | NULL |
| 101 | 10001 | 孙悟空 | 11111 |
| 102 | 20001 | 曹孟德 | NULL |
| 103 | 20002 | 孙仲谋 | NULL |
3.3 插入否则更新(ON DUPLICATE KEY UPDATE)
由于主键或者唯一键对应的值已经存在而导致插入失败,可以选择性的进行同步更新操作。
-- 主键冲突
INSERT INTO students (id, sn, name) VALUES (100, 10010, '唐大师');
ERROR 1062 (23000): Duplicate entry '100' for key 'PRIMARY'
-- 唯一键冲突
INSERT INTO students (sn, name) VALUES (20001, '曹阿瞒');
ERROR 1062 (23000): Duplicate entry '20001' for key 'sn'
-- 使用 ON DUPLICATE KEY UPDATE 解决冲突
INSERT INTO students (id, sn, name) VALUES (100, 10010, '唐大师')
ON DUPLICATE KEY UPDATE sn = 10010, name = '唐大师';
Query OK, 2 rows affected (0.47 sec)
📌 影响行数说明:
0 row affected:表中有冲突数据,但冲突数据的值和 update 的值相等。1 row affected:表中没有冲突数据,数据被插入。2 row affected:表中有冲突数据,并且数据已经被更新。
-- 通过 MySQL 函数获取受到影响的数据行数
SELECT ROW_COUNT();
执行结果:
| ROW_COUNT() |
|---|
| 2 |
3.4 替换(REPLACE)
-- 主键或者唯一键没有冲突,则直接插入;
-- 主键或者唯一键如果冲突,则删除后再插入
REPLACE INTO students (sn, name) VALUES (20001, '曹阿瞒');
Query OK, 2 rows affected (0.00 sec)
📌 影响行数说明:
1 row affected:表中没有冲突数据,数据被插入。2 row affected:表中有冲突数据,删除后重新插入。
🔍 4. Retrieve:查询数据(使用频率最高)
4.1 准备工作:创建考试成绩表
-- 创建表结构
CREATE TABLE exam_result (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL COMMENT '同学姓名',
chinese float DEFAULT 0.0 COMMENT '语文成绩',
math float DEFAULT 0.0 COMMENT '数学成绩',
english float DEFAULT 0.0 COMMENT '英语成绩'
);
-- 插入测试数据
INSERT INTO exam_result (name, chinese, math, english) VALUES
('唐三藏', 67, 98, 56),
('孙悟空', 87, 78, 77),
('猪悟能', 88, 98, 90),
('曹孟德', 82, 84, 67),
('刘玄德', 55, 85, 45),
('孙权', 70, 73, 78),
('宋公明', 75, 65, 30);
Query OK, 7 rows affected (0.00 sec)
Records: 7 Duplicates: 0 Warnings: 0
4.2 SELECT 列
4.2.1 全列查询
SELECT * FROM exam_result;
执行结果:
| id | name | chinese | math | english |
|---|---|---|---|---|
| 1 | 唐三藏 | 67 | 98 | 56 |
| 2 | 孙悟空 | 87 | 78 | 77 |
| 3 | 猪悟能 | 88 | 98 | 90 |
| 4 | 曹孟德 | 82 | 84 | 67 |
| 5 | 刘玄德 | 55 | 85 | 45 |
| 6 | 孙权 | 70 | 73 | 78 |
| 7 | 宋公明 | 75 | 65 | 30 |
⚠️ 注意:通常不建议使用
*进行全列查询,因为:
- 查询的列越多,意味着需要传输的数据量越大。
- 可能会影响到索引的使用。(索引待后面课程讲解)
4.2.2 指定列查询
-- 指定列的顺序不需要按定义表的顺序来
SELECT id, name, english FROM exam_result;
执行结果:
| id | name | english |
|---|---|---|
| 1 | 唐三藏 | 56 |
| 2 | 孙悟空 | 77 |
| 3 | 猪悟能 | 90 |
| 4 | 曹孟德 | 67 |
| 5 | 刘玄德 | 45 |
| 6 | 孙权 | 78 |
| 7 | 宋公明 | 30 |
4.2.3 查询字段为表达式
-- 表达式不包含字段
SELECT id, name, 10 FROM exam_result;
-- 表达式包含一个字段
SELECT id, name, english + 10 FROM exam_result;
-- 表达式包含多个字段
SELECT id, name, chinese + math + english FROM exam_result;
表达式包含多个字段的执行结果:
| id | name | chinese + math + english |
|---|---|---|
| 1 | 唐三藏 | 221 |
| 2 | 孙悟空 | 242 |
| 3 | 猪悟能 | 276 |
| 4 | 曹孟德 | 233 |
| 5 | 刘玄德 | 185 |
| 6 | 孙权 | 221 |
| 7 | 宋公明 | 170 |
4.2.4 为查询结果指定别名
SELECT id, name, chinese + math + english 总分 FROM exam_result;
执行结果:
| id | name | 总分 |
|---|---|---|
| 1 | 唐三藏 | 221 |
| 2 | 孙悟空 | 242 |
| 3 | 猪悟能 | 276 |
| 4 | 曹孟德 | 233 |
| 5 | 刘玄德 | 185 |
| 6 | 孙权 | 221 |
| 7 | 宋公明 | 170 |
4.2.5 结果去重
-- 98 分重复了
SELECT math FROM exam_result;
-- 去重结果
SELECT DISTINCT math FROM exam_result;
去重后的执行结果:
| math |
|---|
| 98 |
| 78 |
| 84 |
| 85 |
| 73 |
| 65 |
4.3 WHERE 条件
4.3.1 比较运算符与逻辑运算符
| 运算符 | 说明 |
|---|---|
>, >=, <, <= |
大于,大于等于,小于,小于等于 |
= |
等于,NULL 不安全,例如 NULL = NULL 的结果是 NULL |
<=> |
等于,NULL 安全,例如 NULL <=> NULL 的结果是 TRUE(1) |
!=, <> |
不等于 |
BETWEEN a0 AND a1 |
范围匹配,[a0, a1],如果 a0 <= value <= a1,返回 TRUE(1) |
IN (option, ...) |
如果是 option 中的任意一个,返回 TRUE(1) |
IS NULL |
是 NULL |
IS NOT NULL |
不是 NULL |
LIKE |
模糊匹配。% 表示任意多个(包括 0 个)任意字符;_ 表示任意一个字符 |
AND |
多个条件必须都为 TRUE(1),结果才是 TRUE(1) |
OR |
任意一个条件为 TRUE(1),结果为 TRUE(1) |
NOT |
条件为 TRUE(1),结果为 FALSE(0) |
4.3.2 英语不及格的同学及英语成绩(< 60)
SELECT name, english FROM exam_result WHERE english < 60;
执行结果:
| name | english |
|---|---|
| 唐三藏 | 56 |
| 刘玄德 | 45 |
| 宋公明 | 30 |
4.3.3 语文成绩在 [80, 90] 分的同学
-- 使用 AND 进行条件连接
SELECT name, chinese FROM exam_result WHERE chinese >= 80 AND chinese <= 90;
-- 使用 BETWEEN ... AND ... 条件
SELECT name, chinese FROM exam_result WHERE chinese BETWEEN 80 AND 90;
执行结果:
| name | chinese |
|---|---|
| 孙悟空 | 87 |
| 猪悟能 | 88 |
| 曹孟德 | 82 |
4.3.4 数学成绩是 58 或 59 或 98 或 99 分的同学
-- 使用 OR 进行条件连接
SELECT name, math FROM exam_result
WHERE math = 58
OR math = 59
OR math = 98
OR math = 99;
-- 使用 IN 条件
SELECT name, math FROM exam_result WHERE math IN (58, 59, 98, 99);
执行结果:
| name | math |
|---|---|
| 唐三藏 | 98 |
| 猪悟能 | 98 |
4.3.5 姓孙的同学及孙某同学
-- % 匹配任意多个(包括 0 个)任意字符
SELECT name FROM exam_result WHERE name LIKE '孙%';
-- _ 匹配严格的一个任意字符
SELECT name FROM exam_result WHERE name LIKE '孙_';
执行结果:
| name |
|---|
| 孙悟空 |
| 孙权 |
| name |
|---|
| 孙权 |
4.3.6 语文成绩好于英语成绩的同学
SELECT name, chinese, english FROM exam_result WHERE chinese > english;
执行结果:
| name | chinese | english |
|---|---|---|
| 唐三藏 | 67 | 56 |
| 孙悟空 | 87 | 77 |
| 曹孟德 | 82 | 67 |
| 刘玄德 | 55 | 45 |
| 宋公明 | 75 | 30 |
4.3.7 总分在 200 分以下的同学
-- WHERE 条件中使用表达式
-- 别名不能用在 WHERE 条件中
SELECT name, chinese + math + english 总分 FROM exam_result
WHERE chinese + math + english < 200;
执行结果:
| name | 总分 |
|---|---|
| 刘玄德 | 185 |
| 宋公明 | 170 |
4.3.8 语文成绩 > 80 并且不姓孙的同学
SELECT name, chinese FROM exam_result
WHERE chinese > 80 AND name NOT LIKE '孙%';
执行结果:
| id | name | chinese | math | english |
|---|---|---|---|---|
| 3 | 猪悟能 | 88 | 98 | 90 |
| 4 | 曹孟德 | 82 | 84 | 67 |
4.3.9 孙某同学,否则要求总成绩 > 200 且语文成绩 < 数学成绩且英语成绩 > 80
SELECT name, chinese, math, english, chinese + math + english 总分
FROM exam_result
WHERE name LIKE '孙_' OR (
chinese + math + english > 200 AND chinese < math AND english > 80
);
执行结果:
| name | chinese | math | english | 总分 |
|---|---|---|---|---|
| 猪悟能 | 88 | 98 | 90 | 276 |
| 孙权 | 70 | 73 | 78 | 221 |
4.3.10 NULL 的查询
-- 查询 students 表
SELECT * FROM students;
-- 查询 qq 号已知的同学姓名
SELECT name, qq FROM students WHERE qq IS NOT NULL;
-- NULL 和 NULL 的比较,= 和 <=> 的区别
SELECT NULL = NULL, NULL = 1, NULL = 0;
SELECT NULL <=> NULL, NULL <=> 1, NULL <=> 0;
NULL 比较的执行结果:
| NULL = NULL | NULL = 1 | NULL = 0 |
|---|---|---|
| NULL | NULL | NULL |
| NULL <=> NULL | NULL <=> 1 | NULL <=> 0 |
|---|---|---|
| 1 | 0 | 0 |
💡 核心要点:
NULL不参与运算,NULL和任何数据比较都是false。使用<=>进行 NULL 安全的等值比较。
4.4 结果排序(ORDER BY)
SELECT ... FROM table_name [WHERE ...]
ORDER BY column [ASC|DESC], [...]
-- ASC 为升序(从小到大)
-- DESC 为降序(从大到小)
-- 默认为 ASC
⚠️ 注意:没有
ORDER BY子句的查询,返回的顺序是未定义的,永远不要依赖这个顺序。
4.4.1 同学及数学成绩,按数学成绩升序显示
SELECT name, math FROM exam_result ORDER BY math;
执行结果:
| name | math |
|---|---|
| 宋公明 | 65 |
| 孙权 | 73 |
| 孙悟空 | 78 |
| 曹孟德 | 84 |
| 刘玄德 | 85 |
| 唐三藏 | 98 |
| 猪悟能 | 98 |
4.4.2 同学及 qq 号,按 qq 号排序显示
-- NULL 视为比任何值都小,升序出现在最上面
SELECT name, qq FROM students ORDER BY qq;
-- NULL 视为比任何值都小,降序出现在最下面
SELECT name, qq FROM students ORDER BY qq DESC;
升序执行结果:
| name | |
|---|---|
| 唐大师 | NULL |
| 孙仲谋 | NULL |
| 曹阿瞒 | NULL |
| 孙悟空 | 11111 |
4.4.3 多字段排序
-- 多字段排序,排序优先级随书写顺序
SELECT name, math, english, chinese FROM exam_result
ORDER BY math DESC, english, chinese;
执行结果:
| name | math | english | chinese |
|---|---|---|---|
| 唐三藏 | 98 | 56 | 67 |
| 猪悟能 | 98 | 90 | 88 |
| 刘玄德 | 85 | 45 | 55 |
| 曹孟德 | 84 | 67 | 82 |
| 孙悟空 | 78 | 77 | 87 |
| 孙权 | 73 | 78 | 70 |
| 宋公明 | 65 | 30 | 75 |
4.5 分页查询(LIMIT)
-- 从 0 开始,筛选 n 条结果
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT n;
-- 从 s 开始,筛选 n 条结果,比第二种用法更明确,建议使用
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT n OFFSET s;
💡 建议:对未知表进行查询时,最好加一条
LIMIT 1,避免因为表中数据过大,查询全表数据导致数据库卡死。
按 id 进行分页,每页 3 条记录
-- 第 1 页
SELECT id, name, math, english, chinese FROM exam_result
ORDER BY id LIMIT 3 OFFSET 0;
-- 第 2 页
SELECT id, name, math, english, chinese FROM exam_result
ORDER BY id LIMIT 3 OFFSET 3;
-- 第 3 页,如果结果不足 3 个,不会有影响
SELECT id, name, math, english, chinese FROM exam_result
ORDER BY id LIMIT 3 OFFSET 6;
第 1 页执行结果:
| id | name | math | english | chinese |
|---|---|---|---|---|
| 1 | 唐三藏 | 98 | 56 | 67 |
| 2 | 孙悟空 | 78 | 77 | 87 |
| 3 | 猪悟能 | 98 | 90 | 88 |
第 3 页执行结果:
| id | name | math | english | chinese |
|---|---|---|---|---|
| 7 | 宋公明 | 65 | 30 | 75 |
💡 核心要点:
n是步长,从指定位置开始,连续读取多少条记录;s是开始位置(下标从 0 开始)。LIMIT的本质功能是"显示",其执行顺序更靠后——需要数据才能排序,只有数据准备好了才能显示。
✏️ 5. Update:更新数据
UPDATE table_name SET column = expr [, column = expr ...]
[WHERE ...] [ORDER BY ...] [LIMIT ...]
5.1 将孙悟空同学的数学成绩变更为 80 分
-- 查看原数据
SELECT name, math FROM exam_result WHERE name = '孙悟空';
-- 数据更新
UPDATE exam_result SET math = 80 WHERE name = '孙悟空';
Query OK, 1 row affected (0.04 sec)
Rows matched: 1 Changed: 1 Warnings: 0
-- 查看更新后数据
SELECT name, math FROM exam_result WHERE name = '孙悟空';
更新后结果:
| name | math |
|---|---|
| 孙悟空 | 80 |
5.2 将曹孟德同学的数学成绩变更为 60 分,语文成绩变更为 70 分
-- 查看原数据
SELECT name, math, chinese FROM exam_result WHERE name = '曹孟德';
-- 数据更新(一次更新多个列)
UPDATE exam_result SET math = 60, chinese = 70 WHERE name = '曹孟德';
Query OK, 1 row affected (0.14 sec)
Rows matched: 1 Changed: 1 Warnings: 0
-- 查看更新后数据
SELECT name, math, chinese FROM exam_result WHERE name = '曹孟德';
更新后结果:
| name | math | chinese |
|---|---|---|
| 曹孟德 | 60 | 70 |
5.3 将总成绩倒数前三的 3 位同学的数学成绩加上 30 分
-- 查看原数据(别名可以在 ORDER BY 中使用)
SELECT name, math, chinese + math + english 总分 FROM exam_result
ORDER BY 总分 LIMIT 3;
-- 数据更新,不支持 math += 30 这种语法
UPDATE exam_result SET math = math + 30
ORDER BY chinese + math + english LIMIT 3;
-- 查看更新后数据
SELECT name, math, chinese + math + english 总分 FROM exam_result
WHERE name IN ('宋公明', '刘玄德', '曹孟德');
更新后结果:
| name | math | 总分 |
|---|---|---|
| 曹孟德 | 90 | 227 |
| 刘玄德 | 115 | 215 |
| 宋公明 | 95 | 200 |
5.4 将所有同学的语文成绩更新为原来的 2 倍
⚠️ 注意:更新全表的语句慎用!
-- 查看原数据
SELECT * FROM exam_result;
-- 数据更新(没有 WHERE 子句,则更新全表)
UPDATE exam_result SET chinese = chinese * 2;
Query OK, 7 rows affected (0.00 sec)
Rows matched: 7 Changed: 7 Warnings: 0
-- 查看更新后数据
SELECT * FROM exam_result;
更新后结果:
| id | name | chinese | math | english |
|---|---|---|---|---|
| 1 | 唐三藏 | 134 | 98 | 56 |
| 2 | 孙悟空 | 174 | 80 | 77 |
| 3 | 猪悟能 | 176 | 98 | 90 |
| 4 | 曹孟德 | 140 | 90 | 67 |
| 5 | 刘玄德 | 110 | 115 | 45 |
| 6 | 孙权 | 140 | 73 | 78 |
| 7 | 宋公明 | 150 | 95 | 30 |
🗑️ 6. Delete:删除数据
6.1 删除数据
DELETE FROM table_name [WHERE ...] [ORDER BY ...] [LIMIT ...]
6.1.1 删除孙悟空同学的考试成绩
-- 查看原数据
SELECT * FROM exam_result WHERE name = '孙悟空';
-- 删除数据
DELETE FROM exam_result WHERE name = '孙悟空';
Query OK, 1 row affected (0.17 sec)
-- 查看删除结果
SELECT * FROM exam_result WHERE name = '孙悟空';
Empty set (0.00 sec)
6.1.2 删除整张表数据
⚠️ 注意:删除整表操作要慎用!
-- 准备测试表
CREATE TABLE for_delete (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20)
);
Query OK, 0 rows affected (0.16 sec)
-- 插入测试数据
INSERT INTO for_delete (name) VALUES ('A'), ('B'), ('C');
Query OK, 3 rows affected (1.05 sec)
Records: 3 Duplicates: 0 Warnings: 0
-- 查看测试数据
SELECT * FROM for_delete;
-- 删除整表数据
DELETE FROM for_delete;
Query OK, 3 rows affected (0.00 sec)
-- 查看删除结果
SELECT * FROM for_delete;
Empty set (0.00 sec)
-- 再插入一条数据,自增 id 在原值上增长
INSERT INTO for_delete (name) VALUES ('D');
Query OK, 1 row affected (0.00 sec)
-- 查看数据
SELECT * FROM for_delete;
再插入后的结果:
| id | name |
|---|---|
| 4 | D |
💡 注意:
DELETE删除数据后,自增id不会重置,会在原值上继续增长。
6.2 截断表(TRUNCATE)
TRUNCATE [TABLE] table_name
⚠️ 注意:这个操作慎用!
- 只能对整表操作,不能像
DELETE一样针对部分数据操作。- 实际上 MySQL 不对数据操作,所以比
DELETE更快,但是TRUNCATE在删除数据的时候,并不经过真正的事务,所以无法回滚。- 会重置
AUTO_INCREMENT项。
-- 准备测试表
CREATE TABLE for_truncate (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20)
);
Query OK, 0 rows affected (0.16 sec)
-- 插入测试数据
INSERT INTO for_truncate (name) VALUES ('A'), ('B'), ('C');
Query OK, 3 rows affected (1.05 sec)
Records: 3 Duplicates: 0 Warnings: 0
-- 查看测试数据
SELECT * FROM for_truncate;
-- 截断整表数据,注意影响行数是 0,所以实际上没有对数据真正操作
TRUNCATE for_truncate;
Query OK, 0 rows affected (0.10 sec)
-- 查看删除结果
SELECT * FROM for_truncate;
Empty set (0.00 sec)
-- 再插入一条数据,自增 id 重新增长
INSERT INTO for_truncate (name) VALUES ('D');
Query OK, 1 row affected (0.00 sec)
-- 查看数据
SELECT * FROM for_truncate;
再插入后的结果:
| id | name |
|---|---|
| 1 | D |
💡 核心区别:
DELETE保留自增计数器,TRUNCATE重置自增计数器。
🔗 7. 插入查询结果
INSERT INTO table_name [(column [, column ...])] SELECT ...
案例:删除表中的重复记录,重复的数据只能有一份
-- 创建原数据表
CREATE TABLE duplicate_table (id int, name varchar(20));
Query OK, 0 rows affected (0.01 sec)
-- 插入测试数据
INSERT INTO duplicate_table VALUES
(100, 'aaa'),
(100, 'aaa'),
(200, 'bbb'),
(200, 'bbb'),
(200, 'bbb'),
(300, 'ccc');
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
-- 思路:
-- 1. 创建一张空表 no_duplicate_table,结构和 duplicate_table 一样
CREATE TABLE no_duplicate_table LIKE duplicate_table;
Query OK, 0 rows affected (0.00 sec)
-- 2. 将 duplicate_table 的去重数据插入到 no_duplicate_table
INSERT INTO no_duplicate_table SELECT DISTINCT * FROM duplicate_table;
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
-- 3. 通过重命名表,实现原子的去重操作
RENAME TABLE duplicate_table TO old_duplicate_table,
no_duplicate_table TO duplicate_table;
Query OK, 0 rows affected (0.00 sec)
-- 查看最终结果
SELECT * FROM duplicate_table;
最终结果:
| id | name |
|---|---|
| 100 | aaa |
| 200 | bbb |
| 300 | ccc |
💡 为什么最后是通过
RENAME方式进行的?
持久化方式有两种:1. 记录历史 SQL 语句;2. 记录数据本身。通过RENAME可以原子性地替换表,保证数据一致性。
📊 8. 聚合函数
| 函数 | 说明 |
|---|---|
COUNT([DISTINCT] expr) |
返回查询到的数据的数量 |
SUM([DISTINCT] expr) |
返回查询到的数据的总和,不是数字没有意义 |
AVG([DISTINCT] expr) |
返回查询到的数据的平均值,不是数字没有意义 |
MAX([DISTINCT] expr) |
返回查询到的数据的最大值,不是数字没有意义 |
MIN([DISTINCT] expr) |
返回查询到的数据的最小值,不是数字没有意义 |
8.1 统计班级共有多少同学
-- 使用 * 做统计,不受 NULL 影响
SELECT COUNT(*) FROM students;
-- 使用表达式做统计
SELECT COUNT(1) FROM students;
执行结果:
| COUNT(*) |
|---|
| 4 |
8.2 统计班级收集的 qq 号有多少
-- NULL 不会计入结果
SELECT COUNT(qq) FROM students;
执行结果:
| COUNT(qq) |
|---|
| 1 |
8.3 统计本次考试的数学成绩分数个数
-- COUNT(math) 统计的是全部成绩
SELECT COUNT(math) FROM exam_result;
-- COUNT(DISTINCT math) 统计的是去重成绩数量
SELECT COUNT(DISTINCT math) FROM exam_result;
去重统计结果:
| COUNT(DISTINCT math) |
|---|
| 5 |
8.4 统计数学成绩总分
SELECT SUM(math) FROM exam_result;
-- 不及格 < 60 的总分,没有结果,返回 NULL
SELECT SUM(math) FROM exam_result WHERE math < 60;
执行结果:
| SUM(math) |
|---|
| 569 |
8.5 统计平均总分
SELECT AVG(chinese + math + english) 平均总分 FROM exam_result;
执行结果:
| 平均总分 |
|---|
| 297.5 |
8.6 返回英语最高分
SELECT MAX(english) FROM exam_result;
执行结果:
| MAX(english) |
|---|
| 90 |
8.7 返回 > 70 分以上的数学最低分
SELECT MIN(math) FROM exam_result WHERE math > 70;
执行结果:
| MIN(math) |
|---|
| 73 |
🏷️ 9. GROUP BY 子句的使用
在 SELECT 中使用 GROUP BY 子句可以对指定列进行分组查询。
select column1, column2, .. from table group by column;
💡 核心要点:
- 分组的目的是为了进行分组之后,方便进行聚合统计。
- 指定列名,实际分组,使用该列的不同行数据来进行分组的。
- 分组的条件
deptno,组内一定是相同的,可以被聚合压缩。- 分组,不就是把一组按照条件拆成了多少个组,进行各自组内的统计。
- 分组(分表),不就是把一张表按照条件在逻辑上拆成了多个子表,然后分别对各自的子表进行聚合统计。
- 先对一列中的不同数据进行分组,再进行聚合统计。
- 规则:只有在
GROUP BY后面的列才能出现在SELECT后面。GROUP BY后面可以跟多个列来进行分组。
准备工作:创建雇员信息表
创建一个雇员信息表(来自 Oracle 9i 的经典测试表):EMP 员工表、DEPT 部门表、SALGRADE 工资等级表。
如何显示每个部门的平均工资和最高工资
select deptno, avg(sal), max(sal) from EMP group by deptno;
显示每个部门的每种岗位的平均工资和最低工资
select avg(sal), min(sal), job, deptno from EMP group by deptno, job;
显示平均工资低于 2000 的部门和它的平均工资
-- 统计各个部门的平均工资
select avg(sal) from EMP group by deptno;
-- having 经常和 group by 搭配使用,作用是对分组进行筛选,作用有些像 where
select avg(sal) as myavg from EMP group by deptno having myavg < 2000;
💡
HAVING和WHERE的区别:
HAVING是对分组聚合之后的结果进行条件筛选。WHERE是对具体的任意列进行条件筛选。- 执行顺序:
FROM>ON>JOIN>WHERE>GROUP BY>WITH>HAVING>SELECT>DISTINCT>ORDER BY>LIMIT
select deptno, job, avg(sal) myavg from emp
where ename != 'SMITH'
group by deptno, job
having myavg < 2000;
💡 MySQL 一切皆是表:不要单纯的认为,只有磁盘上表结构导入到 MySQL,真实存在的表才叫表。之间筛选出来的,包括最终的结果,全部都是逻辑上的表!只要我们能够处理好单表的 CURD,所有的 SQL 场景,我们全部都能用统一的方式执行。
🎯 10. 实战 OJ 练习
以下是一些经典的 SQL 面试题,建议动手练习:
| 平台 | 题目 |
|---|---|
| 牛客 | 批量插入数据 |
| 牛客 | 找出所有员工当前 (to_date='9999-01-01') 具体的薪水 salary |
更多推荐




所有评论(0)