Files
java-book/3.javaEE/day07/MySQL约束-笔记.md
2025-08-27 14:54:36 +08:00

1341 lines
34 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MySQL-DQL&约束
## 学习目标
1. 能够使用SQL语句进行排序
2. 能够使用聚合函数
3. 能够使用SQL语句进行分组查询
4. 能够完成数据的备份和恢复
5. 能够使用SQL语句添加主键、外键、唯一、非空约束
6. 能够说出多表之间的关系及其建表原则
## 第一章 DQL语句
### DQL查询语句-条件查询
#### 目标
能够掌握条件查询语法格式
#### 讲解
前面我们的查询都是将所有数据都查询出来,但是有时候我们只想获取到`满足条件`的数据
语法格式:
```sql
SELECT 字段名... FROM 表名 WHERE 条件;
```
流程:取出表中的每条数据,满足条件的记录就返回,不满足条件的记录不返回
##### 准备数据
```sql
CREATE TABLE student3 (
id int,
name varchar(20),
age int,
sex varchar(5),
address varchar(100),
math int,
english int
);
INSERT INTO student3(id,NAME,age,sex,address,math,english) VALUES (1,'马云',55,'男','杭州',66,78),(2,'马化腾',45,'女','深圳',98,87),(3,'马景涛',55,'男','香港',56,77),(4,'柳岩',20,'女','湖南',76,65),(5,'柳青',20,'男','湖南',86,NULL),(6,'刘德华',57,'男','香港',99,99),(7,'马德',22,'女','香港',99,99),(8,'德玛西亚',18,'男','南京',56,65);
```
##### 比较运算符
`>`大于
`<`小于
`<=`小于等于
`>=`大于等于
`=`等于
`<>``!=`不等于
具体操作:
* 查询math分数大于80分的学生
```sql
SELECT * FROM student3 WHERE math>80;
```
![](img/where查询01.png)
* 查询english分数小于或等于80分的学生
```sql
SELECT * FROM student3 WHERE english<=80;
```
![](img/where查询02.png)
* 查询age等于20岁的学生
```sql
SELECT * FROM student3 WHERE age=20;
```
![](img/where查询03.png)
* 查询age不等于20岁的学生
```sql
SELECT * FROM student3 WHERE age!=20;
SELECT * FROM student3 WHERE age<>20;
```
![](img/where查询04.png)
##### 逻辑运算符
`and(&&)` 多个条件同时满足
`or(||)` 多个条件其中一个满足
`not(!)` 不满足
具体操作:
* 查询age大于35且性别为男的学生(两个条件同时满足)
```sql
SELECT * FROM student3 WHERE age>35 AND sex='男';
```
![](img/where查询05.png)
* 查询age大于35或性别为男的学生(两个条件其中一个满足)
```sql
SELECT * FROM student333 WHERE age>35 OR sex='男';
```
![](img/where查询06.png)
* 查询id是1或3或5的学生
```sql
SELECT * FROM student3 WHERE id=1 OR id=3 OR id=5;
```
![](img/where查询08.png)
**in关键字**
语法格式:
```sql
SELECT * FROM 表名 WHERE 字段 in (1, 2, 3);
```
`in`里面的每个数据都会作为一次条件,只要满足条件的就会显示
具体操作:
* 查询id是1或3或5的学生
```sql
SELECT * FROM student3 WHERE id IN (1,3,5);
```
![](img/where查询08.png)
* 查询id不是1或3或5的学生
```sql
SELECT * FROM student3 WHERE id NOT IN (1,3,5);
```
![](img/where查询07.png)
##### 范围
```sql
BETWEEN 1 AND 2 -- 表示从值1到值2范围包头又包尾
```
比如:`age BETWEEN 80 AND 100`
相当于: `age>=80 && age<=100`
具体操作:
* 查询english成绩大于等于75且小于等于90的学生
```sql
SELECT * FROM student3 WHERE english>=75 AND english<=90;
SELECT * FROM student3 WHERE english BETWEEN 75 AND 90;
```
![](img/where查询09.png)
### 模糊查询like
#### 目标
能够掌握模糊查询语法格式
![](img/where查询11.png)
#### 讲解
`LIKE`表示模糊查询
```sql
SELECT * FROM 表名 WHERE 字段名 LIKE '通配符字符串';
```
满足`通配符字符串`规则的数据就会显示出来
所谓的`通配符字符串`就是`含有通配符的字符串`
MySQL通配符有两个
`%`: 表示零个一个多个字符(任意多个字符)
`_`: 表示一个字符
具体操作:
* 查询姓马的学生
```sql
SELECT * FROM student3 WHERE NAME LIKE '马%';
```
![](img/where查询10.png)
* 查询姓名中包含'德'字的学生
```sql
SELECT * FROM student3 WHERE NAME LIKE '%德%';
```
![](img/where查询11.png)
* 查询姓马,且姓名有三个字的学生
```sql
SELECT * FROM student3 WHERE NAME LIKE '马__';
```
![](img/where查询12.png)
#### 小结
模糊查询格式: SELECT 字段 FROM 表名 WHERE 字段 LIKE '通配符字符串';
`%`:表示零个一个多个字符(任意多个字符)
`_`:表示一个字符
### DQL查询语句-排序
#### 目标
1. 能够掌握对查询的数据进行排序
![](img/orderby01.png)
#### 讲解
通过`ORDER BY`子句,可以将查询出的结果进行排序(排序只是显示方式,不会影响数据库中数据的顺序)
```sql
SELECT 字段 FROM 表名 ORDER BY 排序的字段 [ASC|DESC];
```
**ASC**: 升序排序(默认)
**DESC**: 降序排序
1.2.1 单列排序
单列排序就是使用一个字段排序
具体操作:
* 查询所有数据,使用年龄降序排序
```sql
SELECT * FROM student3 ORDER BY age DESC;
```
![](img/orderby01.png)
1.2.2 组合排序
组合排序就是先按第一个字段进行排序,如果第一个字段相同,才按第二个字段进行排序,依次类推。
上面的例子中年龄是有相同的。当年龄相同再使用math进行排序
```sql
SELECT 字段名 FROM 表名 WHERE 字段= ORDER BY 字段名1 [ASC|DESC], 字段名2 [ASC|DESC];
```
具体操作:
* 查询所有数据,在年龄降序排序的基础上,如果年龄相同再以数学成绩降序排序
```sql
SELECT * FROM student3 ORDER BY age DESC, math DESC;
```
![](img/orderby02.png)
#### 小结
1. 排序的关键字ORDER BY 字段名
2. 升序ASC
3. 降序DESC
### DQL查询语句-聚合函数
#### 目标
能够掌握五个聚合函数的使用
#### 讲解
之前我们做的查询都是横向查询,它们都是根据条件一行一行的进行判断,而使用聚合函数查询是纵向查询,它是对一列的值进行计算,然后返回一个结果值。另外聚合函数会忽略空值。
五个聚合函数:
`count` 统计指定列记录数记录为NULL的不统计
`sum` 计算指定列的数值和如果不是数值类型那么计算结果为0
`max` 计算指定列的最大值
`min` 计算指定列的最小值
`avg` 计算指定列的平均值
聚合函数的使用:写在 SQL语句`SELECT``字段名`的地方
```sql
SELECT 字段名... FROM 表名;
SELECT 聚合函数(字段) FROM 表名;
```
具体操作:
* 查询学生总数
```sql
SELECT COUNT(english) FROM student3;
```
![](img/聚合函数01.png)
我们发现对于NULL的记录不会统计
IFNULL(expr1, expr2)的用法假如expr1 不为 NULL则 IFNULL() 的返回值为 expr1; 否则其返回值为expr2
统计数量常用:
```sql
SELECT COUNT(*) FROM student3;
```
![](img/聚合函数02.png)
* 查询年龄大于40的总数
```sql
SELECT COUNT(*) FROM student3 WHERE age>40;
```
![](img/聚合函数03.png)
* 查询数学成绩总分
```sql
SELECT SUM(math) FROM student3;
```
![](img/聚合函数04.png)
* 查询数学成绩最高分
```sql
SELECT MAX(math) FROM student3;
```
![](img/聚合函数06.png)
* 查询数学成绩最低分
```sql
SELECT MIN(math) FROM student3;
```
![](img/聚合函数07.png)
* 查询数学成绩平均分
```sql
SELECT AVG(math) FROM student3;
```
![](img/聚合函数05.png)
#### 小结
| **sum(列名)** | 求和 |
| --------------- | ---- |
| **count(列名)** | 统计数量 |
| **max(列名)** | 最大值 |
| **min(列名)** | 最小值 |
| **avg(列名)** | 平均值 |
### DQL查询语句-分组
#### 目标
能够对查询后的结果进行分组
![](img/分组10.png)
#### 讲解
分组查询是指使用 `GROUP BY`语句对查询信息进行分组,`相同数据作为一组`
```sql
SELECT 字段 FROM 表名 WHERE 条件 GROUP BY 字段名;
```
GROUP BY怎么分组的
**将分组字段结果中相同内容作为一组**
```sql
SELECT * FROM student3 GROUP BY sex;
```
这句话会将sex相同的数据作为一组
![](img/分组01.png)
`GROUP BY`将分组字段的相同值作为一组,并且返回每组的第一条数据,所以单独分组没什么用处。分组的目的就是为了统计,一般分组会跟聚合函数一起使用。
分组后聚合函数的作用?不是操作所有数据,而是操作一组数据。
```
SELECT SUM(math), sex FROM student3 GROUP BY sex;
```
效果如下:
![](img/分组02.png)
实际上是将每组的math进行求和,返回每组统计的结果
![](img/分组03.png)
>注意事项:当我们使用某个字段分组,在查询的时候也需要将这个字段查询出来,否则看不到数据属于哪组的
>* 查询的时候没有查询出分组字段
> ![](img/分组04.png)
>* 查询的时候查询出分组字段
> ![](img/分组05.png)
具体步骤:
* 按性别分组
```sql
SELECT sex FROM student3 GROUP BY sex;
```
![](img/分组06.png)
* 查询男女各多少人
```sql
1.查询所有数据,按性别分组。
2.统计每组人数
SELECT sex, COUNT(*) FROM student3 GROUP BY sex;
```
![](img/分组06.png)
* 查询年龄大于25岁的人,按性别分组,统计每组的人数
```sql
1.先过滤掉年龄小于25岁的人。
2.再分组。
3.最后统计每组的人数
SELECT sex, COUNT(*) FROM student3 WHERE age > 25 GROUP BY sex;
```
![](img/分组08.png)
* 查询年龄大于25岁的人,按性别分组,统计每组的人数,并只显示性别人数大于2的数据
有很多同学可能会将SQL语句写出这样:
```sql
SELECT sex, COUNT(*) FROM student3 WHERE age > 25 GROUP BY sex WHERE COUNT(*) >2;
```
>注意: 并只显示性别人数>2的数据属于分组后的条件,对于分组后的条件需要使用`having`子句
```sql
SELECT sex, COUNT(*) FROM student3 WHERE age > 25 GROUP BY sex HAVING COUNT(*) >2;
只有分组后人数大于2的``这组数据显示出来
```
![](img/分组09.png)
>having与where的区别
>* having是在分组后对数据进行过滤.
>* where是在分组前对数据进行过滤
>* having后面可以使用聚合函数
>* where后面不可以使用聚合函数
#### 小结
1. 分组的语法格式SELECT 字段 FROM 表名 WHERE 条件 GROUP BY 字段 HAVING 条件;
2. 分组的原理?先将相同数据作为一组,返回每组的第一条数据,单独分组没有意义,分组后跟聚合函数操作
3. where和having的区别
having是在分组后对数据进行过滤.
where是在分组前对数据进行过滤
having后面可以使用聚合函数
where后面不可以使用聚合函数
### DQL查询语句-limit语句
#### 目标
能够掌握limit语句的使用
![](img/limit02.png)
#### 讲解
准备数据:
```sql
INSERT INTO student3(id,NAME,age,sex,address,math,english) VALUES
(9,'唐僧',25,'男','长安',87,78),
(10,'孙悟空',18,'男','花果山',100,66),
(11,'猪八戒',22,'男','高老庄',58,78),
(12,'沙僧',50,'男','流沙河',77,88),
(13,'白骨精',22,'女','白虎岭',66,66),
(14,'蜘蛛精',23,'女','盘丝洞',88,88);
```
`LIMIT`是`限制`的意思,所以`LIMIT`的作用就是限制查询记录的条数。
**LIMIT语句格式**:
```sql
SELECT 字段 FROM 表名 WHERE 条件 LIMIT offset, length;
```
`offset`是指偏移量可以认为是跳过的记录数量不写则默认为0。
`length`是指需要显示的总记录数
具体步骤:
* 查询学生表中数据跳过前面2条显示6条
```sql
我们可以认为跳过前面2条取6条数据
SELECT * FROM student3 LIMIT 2,6;
```
![](img/limit02.png)
**LIMIT的使用场景**:分页
比如我们登录京东淘宝返回的商品信息可能有几万条不是一次全部显示出来。是一页显示固定的条数。假设我们一每页显示5条记录的方式来分页。
![](img/limit04.png)
假设我们一每页显示5条记录的方式来分页SQL语句如下
```sql
-- 每页显示5条
-- 第一页: LIMIT 0,5; 跳过0条显示5条
-- 第二页: LIMIT 5,5; 跳过5条显示5条
-- 第三页: LIMIT 10,5; 跳过10条显示5条
SELECT * FROM student3 LIMIT 0,5;
SELECT * FROM student3 LIMIT 5,5;
SELECT * FROM student3 LIMIT 10,5;
```
![](img/limit03.png)
>**注意**
>
>* 如果第一个参数是0可以简写
> `SELECT * FROM student3 LIMIT 0,5;`
> `SELECT * FROM student3 LIMIT 5;`
>* LIMIT 105; -- 不够5条有多少显示多少
#### 小结
1. LIMIT语句的使用格式
```sql
SELECT 字段 FROM 表名 LIMIT 跳过的条数, 显示条数;
```
2. ```sql
SELECT 字段名 FROM 表名 WHERE 条件 GROUP BY 分组列名 HAVING 条件 ORDER BY 排序列名 LIMIT 跳过行数, 返回行数;
```
### 数据库备份
#### 目标
1. 能够使用命令行的方式备份和还原表中的数据
2. 能够使用图形客户端来备份和还原数据
#### 讲解
备份的应用场景
在服务器进行数据传输、数据存储和数据交换,就有可能产生数据故障。比如发生意外停机或存储介质损坏。这时,如果没有采取数据备份和数据恢复手段与措施,就会导致数据的丢失,造成的损失是无法弥补与估量的。
![1551281803999](img/1551281803999.png)
##### 命令行方式备份与还原
**备份格式**
> 注意:这个操作不用登录
```sql
mysqldump -u用户名 -p密码 数据库 > 文件的路径
```
**还原格式**
>注意还原的时候需要先登录MySQL,并选中对应的数据库
```sql
SOURCE 导入文件的路径
```
**具体操作**
* 备份day22数据库中的数据
```sql
mysqldump -uroot -proot day22 > C:\work\code\bak.sql
```
![](img/数据库导出01.png)
**数据库中的所有表和数据都会导出成SQL语句**
![](img/数据库导出02.png)
* 还原day22数据库中的数据
* 删除day22数据库中的所有表
![](img/数据库导出08.png)
* 登录MySQL
```sql
mysql -uroot -proot
```
* 选中数据库
```sql
use day22;
select database();
```
![](img/数据库导出03.png)
* 使用SOURCE命令还原数据
```sql
source C:\work\课改\MYSQL课改资料\Day02-MYSQL多表查询\code\bak.sql
```
![](img/数据库导出04.png)
##### 图形化界面备份与还原
* 备份day22数据库中的数据
选中数据库,右键 ”备份/导出”,指定导出路径,保存成.sql文件即可。
![](img/数据库导出05.png)
![](img/数据库导出06.png)
* **包含创建数据库的语句**
![](img/数据库导出07.png)
* 还原day22数据库中的数据
* 删除day22数据库
* 数据库列表区域右键“执行SQL脚本” 指定要执行的SQL文件执行即可
![](img/数据库导出09.png)
![](img/数据库导出10.png)
#### 小结
1. 使用命令行的方式备份和还原表中的数据
![1551342236636](img/1551342236636.png)
2. 使用图形客户端来备份和还原数据
## 第二章 MySQL约束
### 数据库约束的概述
#### 目标
能够说出数据库约束的作用
#### 讲解
##### 数据库约束的作用
对表中的数据进行进一步的限制,保证数据的**正确性**、**有效性**和**完整性**。
##### 约束种类
* `PRIMARY KEY`: 主键约束
* `UNIQUE`: 唯一约束
* `NOT NULL`: 非空约束
* `DEFAULT`: 默认值
* `FOREIGN KEY`: 外键约束
#### 小结
1. 数据库约束的作用?
对表中的数据进行进一步的限制,保证数据的**正确性**、**有效性**和**完整性**。
### 主键约束
#### 目标
1. 能够说出主键约束的作用
2. 能够添加和删除主键
#### 讲解
##### 主键的作用
**用来唯一标识一条记录**。
##### 为什么需要主键约束
有些记录的 name,age,score 字段的值都一样时,那么就没法区分这些数据,造成数据库的记录不唯一,这样就不方便管理数据。
![](img/主键01.png)
![](img/主键02.png)
每张表都应该有一个主键,并且每张表只能有一个主键。
##### 哪个字段作为表的主键
通常不用业务字段作为主键单独给每张表设计一个id的字段把id作为主键。主键是给数据库和程序使用的不是给最终的客户使用的。所以主键有没有含义没有关系只要不重复非空就行。
##### 创建主键
主键:`PRIMARY KEY`
**主键的特点**
* 主键必须包含唯一的值
* 主键列不能包含NULL值
**创建主键方式**
1. **在创建表的时候给字段添加主键**
```
字段名 字段类型 PRIMARY KEY
```
2. **在已有表中添加主键**
```sql
ALTER TABLE 表名 ADD PRIMARY KEY(字段名);
```
具体操作:
* 创建表学生表st5, 包含字段(id, name, age)将id做为主键
```sql
CREATE TABLE st5 (
id INT PRIMARY KEY, -- id是主键
NAME VARCHAR(20),
age INT
);
```
![](img/主键03.png)
* 添加数据
```sql
INSERT INTO st5 (id, NAME) VALUES (1, '唐伯虎');
INSERT INTO st5 (id, NAME) VALUES (2, '周文宾');
INSERT INTO st5 (id, NAME) VALUES (3, '祝枝山');
INSERT INTO st5 (id, NAME) VALUES (4, '文征明');
```
* 插入重复的主键值
```sql
-- 主键是唯一的不能重复Duplicate entry '1' for key 'PRIMARY'
INSERT INTO st5 (id, NAME) VALUES (1, '文征明2');
```
* 插入NULL的主键值
```sql
-- 主键是不能为空的Column 'id' cannot be null
INSERT INTO st5 (id, NAME) VALUES (NULL, '文征明3');
```
##### 删除主键
```sql
ALTER TABLE 表名 DROP PRIMARY KEY;
```
具体操作:
* 删除st5表的主键
```sql
ALTER TABLE st5 DROP PRIMARY KEY;
```
![](img/主键04.png)
#### 小结
1. 说出主键约束的作用?唯一区分一条记录
2. 主键的特点?唯一,不能为NULL
3. 添加和删除主键?
字段名 字段类型 PRIMARY KEY
ALTER TABLE 表名 ADD PRIMARY KEY(字段名);
删除主键
ALTER TABLE 表名 DROP PRIMARY KEY;
### 主键自增
#### 目标
能够设置主键为自动增长
#### 讲解
主键如果让我们自己添加很有可能重复,我们通常希望在每次插入新记录时,数据库自动生成主键字段的值
```sql
字段名 字段类型 PRIMARY KEY AUTO_INCREMENT
```
`AUTO_INCREMENT` 表示自动增长(**字段类型必须是整数类型**)
具体操作:
* 创建学生表st6, 包含字段(id, name, age)将id做为主键并自动增长
```sql
CREATE TABLE st6 (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT
);
```
* 插入数据
```sql
-- 主键默认从1开始自动增长
INSERT INTO st6 (NAME, age) VALUES ('唐僧', 22);
INSERT INTO st6 (NAME, age) VALUES ('孙悟空', 26);
INSERT INTO st6 (NAME, age) VALUES ('猪八戒', 25);
INSERT INTO st6 (NAME, age) VALUES ('沙僧', 20);
```
![](img/主键05.png)
扩展
默认地AUTO_INCREMENT 的开始值是1如果希望修改起始值,请使用下列SQL语法
```sql
ALTER TABLE 表名 AUTO_INCREMENT=起始值;
```
DELETE和TRUNCATE的区别
* DELETE 删除表中的数据但不重置AUTO_INCREMENT的值。
![](img/主键06.png)
* TRUNCATE 摧毁表重建表AUTO_INCREMENT重置为1
![](img/主键07.png)
#### 小结
1. 设置主键为自动增长格式?
字段名 数据类型 PRIMARY KEY AUTO_INCREMENT
### 唯一约束
#### 目标
1. 能够说出唯一约束的作用
2. 能够添加唯一约束
#### 讲解
##### 唯一约束的作用
在这张表中这个字段的值不能重复
##### 唯一约束的基本格式
```sql
字段名 字段类型 UNIQUE
```
具体步骤:
* 创建学生表st7, 包含字段(id, name),name这一列设置唯一约束,不能出现同名的学生
```sql
CREATE TABLE st7 (
id INT,
NAME VARCHAR(20) UNIQUE
);
```
* 添加一些学生
```sql
INSERT INTO st7 VALUES (1, '貂蝉');
INSERT INTO st7 VALUES (2, '西施');
INSERT INTO st7 VALUES (3, '王昭君');
INSERT INTO st7 VALUES (4, '杨玉环');
-- 插入相同的名字出现name重复: Duplicate entry '貂蝉' for key 'name'
INSERT INTO st7 VALUES (5, '貂蝉');
-- 出现多个null的时候会怎样因为null是没有值所以不存在重复的问题
INSERT INTO st3 VALUES (5, NULL);
INSERT INTO st3 VALUES (6, NULL);
```
#### 小结
1. 说出唯一约束的作用?让这个字段的值不能重复
2. 添加唯一约束格式?字段名 字段类型 UNIQUE
### 非空约束
#### 目标
1. 能够说出非空约束的作用
2. 能够添加非空约束
#### 讲解
##### 非空约束的作用
这个字段必须设置值,不能是NULL
##### 非空约束的基本语法格式
```sql
字段名 字段类型 NOT NULL
```
具体操作:
* 创建表学生表st8, 包含字段(id,name,gender)其中name不能为NULL
```sql
CREATE TABLE st8 (
id INT,
NAME VARCHAR(20) NOT NULL,
gender CHAR(2)
);
```
* 添加一些完整的记录
```sql
INSERT INTO st8 VALUES (1, '郭富城', '男');
INSERT INTO st8 VALUES (2, '黎明', '男');
INSERT INTO st8 VALUES (3, '张学友', '男');
INSERT INTO st8 VALUES (4, '刘德华', '男');
-- 姓名不赋值出现姓名不能为null: Column 'name' cannot be null
INSERT INTO st8 VALUES (5, NULL, '男');
```
#### 小结
非空约束的格式:
字段名 数据类型 NOT NULL
### 默认值
#### 目标
1. 能够说出默认值的作用
2. 能够给字段添加默认值
#### 讲解
##### 默认值的作用
往表中添加数据时,如果不指定这个字段的数据,就使用默认值
##### 默认值格式
```sql
字段名 字段类型 DEFAULT 默认值
```
具体步骤:
* 创建一个学生表 st9包含字段(id,name,address) 地址默认值是广州
```sql
CREATE TABLE st9 (
id INT,
NAME VARCHAR(20),
address VARCHAR(50) DEFAULT '广州'
);
```
* 添加一条记录,使用默认地址
```sql
INSERT INTO st9 (id, NAME) VALUES (1, '刘德华');
```
![](img/默认值.png)
* 添加一条记录,不使用默认地址
```sql
INSERT INTO st9 VALUES (2, '张学友', '香港');
```
**面试题:**
如果一个字段设置了非空与唯一约束,该字段与主键的区别?
1.一张表只有一个主键
2.一张表可以多个字段添加非空与唯一约束
3.主键可以自动增长,自己添加的非空与唯一约束字段无法自动增长
#### 小结
1. 说出默认值的作用?不添加这个字段,就会使用默认值
2. 给字段添加默认值格式? 字段名 字段类型 DEFAULT 默认值
### 外键约束
#### 目标
1. 能够说出外键约束的作用
2. 能够创建外键约束
#### 讲解
##### 单表的缺点
创建一个员工表包含如下列(id, name, age, dep_name, dep_location),id主键并自动增长,添加5条数据
```sql
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
dep_name VARCHAR(30),
dep_location VARCHAR(30)
);
-- 添加数据
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('张三', 20, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('李四', 21, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('王五', 20, '研发部', '广州');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('老王', 20, '销售部', '深圳');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('大王', 22, '销售部', '深圳');
INSERT INTO emp (NAME, age, dep_name, dep_location) VALUES ('小王', 18, '销售部', '深圳');
```
缺点:表中出现了很多重复的数据(数据冗余)如果要修改研发部的地址需要修改3个地方。
![](img/外键01.png)
解决方案:将一张表分成2张表(员工表和部门表)
![](img/外键02.png)
```sql
-- 创建部门表
CREATE TABLE department (
id INT PRIMARY KEY AUTO_INCREMENT,
dep_name VARCHAR(20),
dep_location VARCHAR(20)
);
-- 创建员工表
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT,
dep_id INT
);
-- 添加2个部门
INSERT INTO department (dep_name, dep_location) VALUES ('研发部', '广州'), ('销售部', '深圳');
-- 添加员工,dep_id表示员工所在的部门
INSERT INTO employee (NAME, age, dep_id) VALUES
('张三', 20, 1),
('李四', 21, 1),
('王五', 20, 1),
('老王', 20, 2),
('大王', 22, 2),
('小王', 18, 2);
```
**问题**
当我们在employee的dep_id里面输入不存在的部门,数据依然可以添加.但是并没有对应的部门不能出现这种情况。employee的dep_id中的内容只能是department表中存在的id
![](img/外键03.png)
**需要达到目的**:需要约束dep_id只能是department表中已经存在id
**解决方式**:使用外键约束
##### 什么是外键约束
一张中的某个字段引用另一张表的主键
主表: 约束别人的表,把数据给别人使用功能
副表/从表: 被别人约束的表,使用别人的数据
![](img/外键04.png)
##### 创建外键
1. **新建表时增加外键**
```sql
CONSTRAINT [外键约束名称] FOREIGN KEY(外键字段名) REFERENCES 主表名(主键字段名)
```
**关键字解释**
CONSTRAINT: 添加约束
FOREIGN KEY(外键字段名): 将某个字段作为外键
REFERENCES 主表名(主键字段名) : 外键引用主表的主键
2. **已有表增加外键**
```sql
ALTER TABLE 从表 ADD [CONSTRAINT] [外键约束名称] FOREIGN KEY (外键字段名) REFERENCES 主表(主键字段名);
```
具体操作:
* 副表/从表: 被别人约束,表结构添加外键约束
* 删除副表/从表 employee
* 创建从表 employee 并添加外键约束
```sql
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT,
dep_id INT,
-- 添加一个外键
-- 外键取名公司要求,一般fk结尾
CONSTRAINT emp_depid_ref_dep_id_fk FOREIGN KEY(dep_id) REFERENCES department(id)
);
```
* 正常添加数据
```sql
INSERT INTO employee (NAME, age, dep_id) VALUES
('张三', 20, 1),
('李四', 21, 1),
('王五', 20, 1),
('老王', 20, 2),
('大王', 22, 2),
('小王', 18, 2);
```
* 部门错误的数据添加失败
```sql
INSERT INTO employee (NAME, age, dep_id) VALUES ('二王', 20, 5);
```
##### 删除外键
```sql
ALTER TABLE 表名 DROP FROEIGN KEY 外键名称;
```
具体操作:
* 删除employee表的emp_depid_ref_dep_id_fk外键
```sql
ALTER TABLE employee DROP FOREIGN KEY emp_depid_ref_dep_id_fk;
```
* 在employee表情存在况下添加外键
```sql
ALTER TABLE employee ADD CONSTRAINT emp_depid_ref_dep_id_fk FOREIGN KEY(dep_id) REFERENCES department(id);
```
#### 小结
1. 创建外键约束格式? CONSTRAINT [外键约束名称] FOREIGN KEY(外键字段名) REFERENCES 主表名(主键字段名)
2. 删除外键格式ALTER TABLE 表名 DROP FROEIGN KEY 外键名称;
### 外键的级联
#### 目标
了解外键的级联操作
#### 讲解
要把部门表中的id值2改成5能不能直接修改呢
```sql
UPDATE department SET id=5 WHERE id=2;
```
不能直接修改:Cannot delete or update a parent row: a foreign key constraint fails 如果副表(员工表)中有引用的数据,不能直接修改主表(部门表)主键
要删除部门id等于1的部门, 能不能直接删除呢?
```sql
DELETE FROM department WHERE id = 1;
```
不能直接删除:Cannot delete or update a parent row: a foreign key constraint fails 如果副表(员工表)中有引用的数据,不能直接删除主表(部门表)数据
##### 什么是级联操作
在修改和删除主表的主键时,同时更新或删除副表的外键值,称为级联操作
`ON UPDATE CASCADE` -- 级联更新,主键发生更新时,外键也会更新
`ON DELETE CASCADE` -- 级联删除,主键发生删除时,外键也会删除
具体操作:
* 删除employee表
* 重新创建employee表添加级联更新和级联删除
```sql
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
dep_id INT,
-- 添加外键约束,并且添加级联更新和级联删除
CONSTRAINT employee_dep_fk FOREIGN KEY (dep_id) REFERENCES department(id) ON UPDATE CASCADE ON DELETE CASCADE
);
```
* 再次添加数据到员工表和部门表
```sql
INSERT INTO employee (NAME, age, dep_id) VALUES ('张三', 20, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('李四', 21, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('王五', 20, 1);
INSERT INTO employee (NAME, age, dep_id) VALUES ('老王', 20, 2);
INSERT INTO employee (NAME, age, dep_id) VALUES ('大王', 22, 2);
INSERT INTO employee (NAME, age, dep_id) VALUES ('小王', 18, 2);
```
* 把部门表中id等于1的部门改成id等于10
```sql
UPDATE department SET id=10 WHERE id=1;
```
![](img/级联操作01.png)
* 删除部门号是2的部门
```sql
DELETE FROM department WHERE id=2;
```
![](img/级联操作02.png)
#### 小结
级联更新ON UPDATE CASCADE 主键修改后,外键也会跟着修改
级联删除ON DELETE CASCADE 主键删除后,外键对应的数据也会删除
## 第三章 表关系的概念
#### 目标
能够理解表之间的3种关系
#### 讲解
现实生活中,实体与实体之间肯定是有关系的,比如:老公和老婆,部门和员工,老师和学生等。那么我们在设计表的时候,就应该体现出表与表之间的这种关系!分成三种:
1. 一对一
2. 一对多
3. 多对多
##### 一对多
例如:班级和学生,部门和员工,客户和订单,分类和商品
一对多建表原则: 在从表(多方)创建一个字段,字段作为外键指向主表(一方)的主键
![](img/一对多.png)
##### 多对多
例如:老师和学生,学生和课程,用户和角色
多对多关系建表原则: 需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键。
![](img/多对多.png)
##### 一对一
在实际的开发中应用不多.因为一对一可以创建成一张表。
两种建表原则:
* 外键唯一:主表的主键和从表的外键(唯一),形成主外键关系,外键唯一`UNIQUE`
* 外键是主键:主表的主键和从表的主键,形成主外键关系
![](img/1对1.png)
![](img/1对12.png)
### 一对多关系练习
#### 目标
以下案例是我们JavaWeb课程最后的小项目.我们拿出其中一部分需求,根据需求来设计数据库表之间的关系
一个旅游线路分类中有多个旅游线路
![](img/1对多练习01.png)
![](img/1对多练习02.png)
#### 讲解
具体操作:
* 创建旅游线路分类表
```sql
-- 创建旅游线路分类表:tab_category
-- cid旅游线路分类主键自动增长
-- cname旅游线路分类名称非空唯一字符串100
CREATE TABLE tab_category (
cid INT PRIMARY KEY AUTO_INCREMENT,
cname VARCHAR(100) NOT NULL UNIQUE
);
```
* 添加旅游线路分类数据
```sql
INSERT INTO tab_category (cname) VALUES ('周边游'), ('出境游'), ('国内游'), ('港澳游');
```
* 创建旅游线路表
```sql
-- 创建旅游线路表:tab_route
-- rid旅游线路主键自动增长
-- rname旅游线路名称非空唯一字符串100
-- price价格
-- rdate 上架时间,日期类型
-- cid 外键,所属分类
create table tab_route(
rid int primary key auto_increment,
rname varchar(100) not null unique,
price double,
rdate date,
cid int, -- 外键
foreign key (cid) references tab_category(cid)
);
```
* 添加旅游线路数据
```sql
INSERT INTO tab_route VALUES
(NULL, '【厦门+鼓浪屿+南普陀寺+曾厝垵 高铁3天 惠贵团】尝味友鸭面线 住1晚鼓浪屿', 1499, '2018-01-27', 3),
(NULL, '【浪漫桂林 阳朔西街高铁3天纯玩 高级团】城徽象鼻山 兴坪漓江 西山公园', 699, '2018-01-27', 3),
(NULL, '【爆款¥1699秒杀】泰国 曼谷 芭堤雅 金沙岛 杜拉拉水上市场 双飞六天【含送签费 泰风情 广州往返 特价团】', 1699, '2018-01-27', 2),
(NULL, '【经典·狮航 ¥2399秒杀】巴厘岛双飞五天 抵玩【广州往返 特价团】', 2399, '2018-01-27', 2),
(NULL, '香港迪士尼乐园自由行2天【永东跨境巴士广东至迪士尼去程交通+迪士尼一日门票+香港如心海景酒店暨会议中心标准房1晚住宿】', 799, '2018-01-27', 4);
```
#### 小结
一对多的关系,创建表的顺序:先建主表,再建从表。
### 多对多关系练习
#### 目标
一个用户收藏多个线路,一个线路被多个用户收藏
![](img/多对多练习01.png)
![](img/多对多练习02.png)
#### 讲解
对于多对多的关系我们需要增加一张中间表来维护他们之间的关系
![](img/多对多练习03.png)
具体操作:
* 创建用户表
```sql
-- 创建用户表tab_user
-- 用户id主键,int类型,自动增长
-- 用户名username,字符串,长度30
-- 密码PASSWORD,字符串,长度30
-- 用户姓名NAME,字符串,长度100
-- 性别sex,字符串,长度1
CREATE TABLE tab_user (
uid INT PRIMARY KEY AUTO_INCREMENT, -- 用户id
username VARCHAR(100) NOT NULL UNIQUE, -- 用户名
PASSWORD VARCHAR(30) NOT NULL, -- 密码
NAME VARCHAR(100), -- 真实姓名
sex CHAR(1) -- 性别
);
```
* 添加用户数据
```sql
INSERT INTO tab_user VALUES
(NULL, 'cz110', 123456, '老王', '男'),
(NULL, 'cz119', 654321, '小王', '男');
```
* 创建收藏表
```sql
-- 创建收藏表:tab_favorite
-- 收藏主键fid,int类型,主键,自动增长
-- 用户id int,不为null
-- 收藏时间date,不为null
-- 旅游线路id,int类型,不为null
CREATE TABLE tab_favorite (
fid INT PRIMARY KEY AUTO_INCREMENT, -- 收藏主键
uid INT NOT NULL, -- 用户id
DATE DATE NOT NULL, -- 收藏时间
rid INT NOT NULL -- 旅游线路id
);
```
* 增加收藏表数据
```sql
INSERT INTO tab_favorite VALUES
(NULL, 1, '2018-01-01', 1), -- 老王选择厦门
(NULL, 1, '2018-01-01', 2), -- 老王选择桂林
(NULL, 1, '2018-01-01', 3), -- 老王选择泰国
(NULL, 2, '2018-01-01', 2), -- 小王选择桂林
(NULL, 2, '2018-01-01', 3), -- 小王选择泰国
(NULL, 2, '2018-01-01', 5); -- 小王选择迪士尼
```
#### 小结
多对多:创建一张中间表,中间表与其它两张表是多对一的关系
### 总结
1. 能够使用SQL语句进行排序
SELECT 字段 FROM 表名 WHERE 条件 ORDER BY 字段 [ASC|DESC];
ASC:升序
DESC:降序
2. 能够使用聚合函数
count: 统计数量
sum:求和
max:获取最大值
min:获取最小值
avg:获取平均值
3. 能够使用SQL语句进行分组查询
SELECT 字段 FROM 表名 WHERE 条件 GROUP BY 字段;
分组是将相同数据作为一组,返回每组第一条数据(没有意义), 通常分组后使用聚合函数
4. 能够完成数据的备份和恢复
命令行的方式
备份:不要登录:mysqldump -uroot -proot 数据库 > 文件名
还原:要登录,选中数据库:source 文件名
图形界面的方式
5. 能够使用SQL语句添加主键、外键、唯一、非空约束
主键: 字段名 字段类型 PRIMARY KEY
外键: CONSTRAINT 外键名 FOREIGN KEY(字段) REFERENCES 主表(主键)
唯一: 字段名 字段类型 UNIQUE
非空约束: 字段名 字段类型 NOT NULL
默认约束: 字段名 字段类型 DEFAULT 默认值
6. 能够说出多表之间的关系及其建表原则
1对1(1:1)
1对多(1:n)
多对多(m:n)
1对多:多方这张表建立外键引用一方的主键(先建立一方主表,后建立多方从表)
多对多:建立中间表,中间表的字段引用某张表的主键(外键)