【MySQL】对表数据的操作:增删查改(CRUD)

目录


前言

前面我们介绍了对"表的属性 "的操作,今天来介绍对表里面的数据的相关操作,无非就是"增删改查"。

  • C reate,关键字 INSERT
  • R ead,关键字 SELECT
  • U pdate,关键字 UPDATE
  • D elete,关键字 DELETEE

一、Create(新增)

1.1 指定列属性插入与批量插入

sql 复制代码
mysql> create table student (
    -> id int primary key auto_increment,
    -> name varchar(20) not null,
    -> gender char(2) not null
    -> );
sql 复制代码
#  ----------------------全列插入-----------------------------------
mysql> insert into student values (1, 'hds', '男');
#  ----------------------全列插入-----------------------------------
mysql> insert into student (id, name, gender) values (2, 'zm', '女');
#  ----------------------指定列属性插入------------------------------
mysql> insert into student (name, gender) values ('cg', '男');
#  ----------------------批量插入多条数据----------------------------
mysql> insert into student (name, gender) values ('wl', '男'), ('swc', '男'), ...;

1.2 插入否则更新

如果一个字段为主键或唯一键,而你又插入了一个重复的字段,就会直接报错!

就可以在插入时在后面带上 ON DUPLICATE KEY UPDATE col1 = value, col2 = value, ...;

sql 复制代码
+----+------+--------+
| id | name | gender |
+----+------+--------+
|  1 | hds  | 男     |
|  2 | zm   | 女     |
|  3 | cg   | 男     |
+----+------+--------+
insert into student values (3, 'wl', '男') on duplicate key update id = 4, name = 'wl', gender = '男';

1.3 替换

replace into:根据主键或唯一键进行判断,如果相关字段不同,直接插入;否则,先删除表中重复数据,然后插入。

sql 复制代码
+----+------+--------+
| id | name | gender |
+----+------+--------+
|  1 | hds  | 男     |
|  2 | zm   | 女     |
|  5 | wl   | 男     |
+----+------+--------+
# 主键id值重复
mysql> replace into student (id, name, gender) values (5, 'cg', '女');
+----+------+--------+
| id | name | gender |
+----+------+--------+
|  1 | hds  | 男     |
|  2 | zm   | 女     |
|  5 | cg   | 女     |
+----+------+--------+

二、Read(读取)

案例:

sql 复制代码
-- 创建表结构
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);

2.1 SELECT 查询

(1)全列查询

sql 复制代码
select * from exam_result;  

实际查询时,不建议直接全列查询,因为数据表中数据可能非常多,这样不仅不方便观察,也比较耗时。

(2)查询指定列

比如就想看一下语文成绩:

sql 复制代码
select name, chinese from exam_reult; 

(3)查询字段为表达式

sql 复制代码
// 加法
select 1 + 1;
// 计算总分
select name, chinese + math + english from exam_result;
// 英语加10分
select name, english + 10 from exam_result;

(4)为查询结果取别名

sql 复制代码
// 给总分取别名
select name, chinese + math + english as 总分 from exam_result;
// 或者
select name, chinese + math + english 总分 from exam_result;

+-----------+--------+
| name      | 总分   |
+-----------+--------+
| 唐三藏    |    221 |
| 孙悟空    |    242 |
| 猪悟能    |    276 |
| 曹孟德    |    233 |
| 刘玄德    |    185 |
| 孙权      |    221 |
| 宋公明    |    170 |
+-----------+--------+

(5)查询结果去重

sql 复制代码
mysql> select math from exam_result;
+------+
| math |
+------+
|   98 |
|   78 |
|   98 |
|   84 |
|   85 |
|   73 |
|   65 |
+------+

mysql> select distinct math from exam_result;
+------+
| math |
+------+
|   98 |
|   78 |
|   84 |
|   85 |
|   73 |
|   65 |
+------+

2.2 where 条件

运算符:

  • 比较运算符:
运算符 说明
><>=<= 大于,小于,大于等于,小于等于
= 等于,判断是否相等,NULL 不安全(null=null,结果为 null)
<=> 等于,NULL 安全(null <=> null,结果为 true)
!=<> 不等于
BETWEEN a0 AND a1 范围匹配,a0, a1
IN(option, ...) 如果是 option 中任意一个,返回 true
IS NULL 为空返回 true
IS NOT NULL 不为空返回true
LIKE 模糊匹配,例如 X%,匹配以 X 开头的;X_ 匹配 X*。
  • 逻辑运算符:
运算符 说明
AND 所有条件都为 true,才为 true
OR 有一个为 true,即为 true
NOT

(1)英语成绩不及格的同学及成绩

sql 复制代码
select name, english from exam_result where english < 60;
+-----------+---------+
| name      | english |
+-----------+---------+
| 唐三藏    |      56 |
| 刘玄德    |      45 |
| 宋公明    |      30 |
+-----------+---------+

该命令的执行过程:

  • from 找到需要查询的表 exam_result
  • exam_result 中找到满足 english < 60
  • 然后 select,回显指定的列

(2)语文成绩在 80, 90 分的同学及成绩

sql 复制代码
// 方法一:
select name, chinese from exam_result where chinese >= 80 AND chinese <= 90;
// 方法二:
select name, chinese from exam_result where chinese between 80 and 90;
+-----------+---------+
| name      | chinese |
+-----------+---------+
| 孙悟空    |      87 |
| 猪悟能    |      88 |
| 曹孟德    |      82 |
+-----------+---------+

(3)数学成绩是 58 或者 98 或者 99 分的同学及数学成绩

sql 复制代码
// 方法一:
select name, math from exam_result where math = 58 OR math = 98 OR math = 99;
// 方法二:
select name, math from exam_result where math in(58, 98, 99);
+-----------+------+
| name      | math |
+-----------+------+
| 唐三藏    |   98 |
| 猪悟能    |   98 |
+-----------+------+

(4)姓孙的同学 及 孙某同学

sql 复制代码
// 姓孙: like '孙%'
// 孙某: like '孙_'
select name from exam_result where name like '孙%';
+-----------+
| name      |
+-----------+
| 孙悟空    |
| 孙权      |
+-----------+

(5)语文成绩好于英语成绩的同学

sql 复制代码
select name, chinese, english from exam_result where chinese > english;

(6)总分在 200 分以下的同学

sql 复制代码
select name, chinese + math + english from exam_result where chinese + math + english < 200;

// --------------------------错误示范----------------------------
select name, chinese + math + english as 总分 from exam_result where 总分 < 200;

该 SQL 执行顺序:先 from 找到表,然后直接根据 where 条件筛选。而总分是在 select 查询时才执行,即重命名是在where 执行之后,所以,语法上有问题。

(7)语文成绩 > 80 并且不姓孙的同学

sql 复制代码
// 姓孙: like '孙%'
select name, chinese from exam_result where chinese > 80 AND NOT name like '孙%';
+-----------+---------+
| name      | chinese |
+-----------+---------+
| 猪悟能    |      88 |
| 曹孟德    |      82 |
+-----------+---------+

(8)孙某同学,否则要求总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80

sql 复制代码
// 孙某: like '孙_'
select name from exam_result where name like '孙_' OR (chinese + math + english > 200 AND chinese < math AND english > 80);

(9)NULL 的查询

sql 复制代码
mysql> select * from t;
+------+------+
| id   | name |
+------+------+
|    1 | zm   |
|    2 | NULL |
|    3 | hds  |
|    5 | NULL |
+------+------+

mysql> select * from t where name is not null;
mysql> select * from t where name is null;

2.3 结果排序

语法:

sql 复制代码
-- ASC: 升序排列
-- DESC: 降序排列
--- 默认:ASC

select ... from table_name [where ...] ORDER BY column [ASC / DESC], [...] 

说明:null 值在排序时,比任何值都小!

(1)查询同学及数学成绩,按照数学成绩升序显示

sql 复制代码
select name, math from exam_result order by math asc;

(2)查询同学各门成绩,依次按 数学降序,英语升序,语文升序的方式显示

sql 复制代码
select * from exam_result order by math desc, english asc, chinese asc;

(3)查询同学及总分,由高到低

sql 复制代码
// 方法一:
select name, chinese + math + english 总分 from exam_result order by chinese + math + english desc;
// 方法二:
select name, chinese + math + english 总分 from exam_reslut order by 总分 desc;

这里为什么可以用别名排序呢?

因为该 SQL 的执行顺序为:

  • 先 from 找到表 exam_result
  • 然后 select 查询,并执行表达式
  • 最后,排序(即此时别名就是表的列名,相当于已经有了一张name-总分的表)。

所以,MySQL 的表并不只有最后显示的表,在执行 SQL 的过程中表也是不断更新变化的(临时表)

(4)查询姓孙的同学或者姓曹的同学数学成绩,结果按数学成绩由高到低显示

sql 复制代码
select name, math from exam_result where name like '孙%' OR name like '曹%' order by math desc;
+-----------+------+
| name      | math |
+-----------+------+
| 曹孟德    |   84 |
| 孙悟空    |   78 |
| 孙权      |   73 |
+-----------+------+

该 SQL 执行顺序:

  • from 找到表
  • 根据 where 条件筛选出一个临时表1
  • 然后 select 查询指定列,形成一个临时表2
  • 最后,对临时表2 根据 math 降序排列

2.4 结果分页

在进行表的查询时,如果表中的数据比较多,而我们只需要查询结果的一部分,就可以进行分页操作。

语法:

sql 复制代码
-- 从 0 开始,拿 n 条数据
select ... from t_name [where ...] [order by ...] LIMIT n;
-- 从 s 开始,拿 n 条数据
select ... from t_name [where ...] [order by ...] LIMIT s, n;
-- 从 s 开始,拿 n 条数据
select ... from t_name [where ...] [order by ...] LIMIT n OFFSET s;

三、Update(更新)

语法:

sql 复制代码
update table_name SET column = value [, column = value]
    [where ...] [order by ...] [limit ...];

注意:更新数据需要谨慎,因为会覆盖原来的数据!!!

(1)将曹孟德同学的数学成绩变更为 60 分,语文成绩变更为 70 分

sql 复制代码
update exam_result set math = 60, chinese = 70 where name = '曹孟德';

(2)将总成绩倒数前三的 3 位同学的数学成绩加上 30 分

sql 复制代码
update exam_result set math = math + 30 order by chinese + math + english asc limit 3;

该 SQL 执行顺序:

  • update 找到表
  • 然后根据总分升序排序,形成 临时表1
  • 然后拿到 临时表1 的前三行形成临时表2
  • 最后对 临时表2 中的所有人的数学成绩加上 30

四、Delete(删除)

语法:

sql 复制代码
DELETE from table_name [where ...] [order by ...] [limit ...];

注意:删除表数据必须非常谨慎,不可恢复!!!

4.1 删除数据

(1)删除孙悟空的考试成绩

sql 复制代码
delete from exam_result where name = '孙悟空';

(2)删除整张表数据

sql 复制代码
delete from exam_result;

4.2 截断表

语法:TRUNCATE table_name;

sql 复制代码
mysql> create table for_truncate (
    -> id int primary key auto_increment,
    -> name char(8) not null
    -> );
    
mysql> select * from for_truncate;
+----+------+
| id | name |
+----+------+
|  1 | zm   |
|  2 | hds  |
+----+------+

mysql> truncate for_truncate;
Query OK, 0 rows affected (0.04 sec)

mysql> select * from for_truncate;
Empty set (0.00 sec)

mysql> insert into for_truncate (name) values ('wl');
Query OK, 1 row affected (0.01 sec)

mysql> select * from for_truncate;
+----+------+
| id | name |
+----+------+
|  1 | wl   |
+----+------+

那么 deletetruncate 有什么区别?

  • truncate 后,0 rows affected(0行被影响),即 truncate 实际不对数据操作,同时主键值被清空。
  • delete 后,主键值延续之前。进行递增。

五、其他

5.1 聚合函数

函数 说明
COUNT() 统计查询到的数量
SUM() 求和,不是数字没有意义
AVG() 求平均值,不是数字没有意义
MAX() 求最大值,不是数字没有意义
MIN() 求最小值,不是数字没有意义
  • 聚合函数通常结合 group by 子句使用
  • 聚合函数自动忽略 null值

(1)统计学生人数

sql 复制代码
select count(*) from exam_result;
select count(1) from exam_result;

(2)统计数学总分

sql 复制代码
select sum(math) from exam_result;

(3)统计数学平均分

sql 复制代码
select avg(math) from exam_result;

5.2 group by 子句

案例:员工信息表

sql 复制代码
-- 创建表
CREATE TABLE `emp` (
  `empno` int(6) unsigned zerofill NOT NULL COMMENT '雇员编号',
  `ename` varchar(10) DEFAULT NULL COMMENT '雇员姓名',
  `job` varchar(9) DEFAULT NULL COMMENT '雇员职位',
  `mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇员领导编号',
  `hiredate` datetime DEFAULT NULL COMMENT '雇佣时间',
  `sal` decimal(7,2) DEFAULT NULL COMMENT '工资月薪',
  `comm` decimal(7,2) DEFAULT NULL COMMENT '奖金',
  `deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部门编号'
);

-- 插入数据
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, null, 20)
,(7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30);
,(7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30)
,(7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, null, 20)
,(7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30)
,(7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, null, 30)
,(7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, null, 10)
,(7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000, null, 20)
,(7839, 'KING', 'PRESIDENT', null, '1981-11-17', 5000, null, 10)
,(7844, 'TURNER', 'SALESMAN', 7698,'1981-09-08', 1500, 0, 30)
,(7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100, null, 20)
,(7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, null, 30)
,(7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, null, 20);
,(7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, null, 10);

(1)显示每个部门的平均工资和最高工资

sql 复制代码
select deptno, avg(sal), max(sal) from emp group by deptno;

(2)显示每个部门的每种岗位的平均工资和最低工资

sql 复制代码
select deptno, job, avg(sal) 平均工资, min(sal) 最低工资 from emp group by deptno, job;
+--------+-----------+--------------+--------------+
| deptno | job       | 平均工资     | 最低工资     |
+--------+-----------+--------------+--------------+
|     10 | CLERK     |  1300.000000 |      1300.00 |
|     10 | MANAGER   |  2450.000000 |      2450.00 |
|     10 | PRESIDENT |  5000.000000 |      5000.00 |
|     20 | ANALYST   |  3000.000000 |      3000.00 |
|     20 | CLERK     |   950.000000 |       800.00 |
|     20 | MANAGER   |  2975.000000 |      2975.00 |
|     30 | CLERK     |   950.000000 |       950.00 |
|     30 | MANAGER   |  2850.000000 |      2850.00 |
|     30 | SALESMAN  |  1400.000000 |      1250.00 |
+--------+-----------+--------------+--------------+

(3)显示平均工资低于2000的部门和它的平均工资

  • 统计各个部门的平均工资
sql 复制代码
select deptno, avg(sal) from emp group by deptno;
+--------+-------------+
| deptno | avg(sal)    |
+--------+-------------+
|     10 | 2916.666667 |
|     20 | 2175.000000 |
|     30 | 1566.666667 |
+--------+-------------+
  • 结合 having 条件对 分组的结果进行筛选
sql 复制代码
select deptno, avg(sal) 平均工资 from emp group by deptno having avg(sal) < 2000;
+--------+--------------+
| deptno | 平均工资     |
+--------+--------------+
|     30 |  1566.666667 |
+--------+--------------+

该 SQL 的执行顺序:

  • from: 确定并加载数据
  • group by: 然后根据指定字段聚合分组,假设形成多个子表
  • having:对分组后的数据进行组级别的条件筛选
  • select:计算聚合函数,提取指定列,并应用列别名

5.3 插入查询结果

语法:

sql 复制代码
insert into ... select ...

在数据库内部高效完成数据的筛选、转换与批量写盘,避免将数据拉取到应用程序层再写回的开销。

应用场景:

  • 数据冷热分离:将主表中不常访问的历史数据筛选出来,批量插入到归档表,随后清理主表一提升日常查询效率
  • 数据备份:将满足特定条件的数据复制到临时表中作为安全备份。

六、总结

SQL 标准逻辑执行顺序中,各个子句的执行顺序:

  1. from:确定并加载数据源(表);
  2. where:对表中数据进行遍历,筛选出满足条件的数据;
  3. group by:按照指定的字段将整表划分为不同的聚合分组;
  4. having:对分组后的数据进行"组级别"的条件筛选;
  5. select:计算所需的聚合函数,提取指定的列,并应用列别名
  6. distinct:去重
  7. order by:按照指定属性排序
  8. limit:对结果进行截取

MySQL 中一切皆"表"

相关推荐
迷枫7121 小时前
SQL 优化排查记录:补充索引,利用 OR 展开消除大表重复扫描
sql
一笑的小酒馆10 小时前
Android中的tcp通信
android
默阳123413 小时前
2026年Android中高级面试题专栏(Android进阶篇)
android
这个DBA有点耶13 小时前
2026年分布式数据库有哪些主流选择?先评估这5个维度再决定
数据库·分布式·dba
古法安卓14 小时前
Android-日志系统源码解析
android·java·android studio
恋猫de小郭15 小时前
Android 17 + OkHttp 5.5.0 ,全新 ECH 下你的 HTTPS 域名可以请求时被安全隐藏
android·前端·flutter
小强闯江湖15 小时前
不只是让 AI 写代码:ViewCompose 把 Android UI 做成了可编译、可渲染的闭环
android·人工智能·kotlin
这个DBA有点耶16 小时前
从MySQL 5.7到8.0:JSON查询性能差在哪?虚拟列索引vs多值索引怎么选
数据库·mysql·架构
胡写代码16 小时前
数据库审计字段,别再每张表各写一套了——我统一成这 6 个字段
数据库