目录
- 一、Create(新增)
-
- [1.1 指定列属性插入与批量插入](#1.1 指定列属性插入与批量插入)
- [1.2 插入否则更新](#1.2 插入否则更新)
- [1.3 替换](#1.3 替换)
- 二、Read(读取)
-
- [2.1 SELECT 查询](#2.1 SELECT 查询)
- [2.2 where 条件](#2.2 where 条件)
- [2.3 结果排序](#2.3 结果排序)
- [2.4 结果分页](#2.4 结果分页)
- 三、Update(更新)
- 四、Delete(删除)
-
- [4.1 删除数据](#4.1 删除数据)
- [4.2 截断表](#4.2 截断表)
- 五、其他
-
- [5.1 聚合函数](#5.1 聚合函数)
- [5.2 group by 子句](#5.2 group by 子句)
- [5.3 插入查询结果](#5.3 插入查询结果)
- 六、总结
前言
前面我们介绍了对"表的属性 "的操作,今天来介绍对表里面的数据的相关操作,无非就是"增删改查"。
- 增 :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 |
+----+------+
那么 delete 和 truncate 有什么区别?
- 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 标准逻辑执行顺序中,各个子句的执行顺序:
- from:确定并加载数据源(表);
- where:对表中数据进行遍历,筛选出满足条件的数据;
- group by:按照指定的字段将整表划分为不同的聚合分组;
- having:对分组后的数据进行"组级别"的条件筛选;
- select:计算所需的聚合函数,提取指定的列,并应用列别名
- distinct:去重
- order by:按照指定属性排序
- limit:对结果进行截取
MySQL 中一切皆"表"!