一、约束
1、概念:对表中的数据进行限定,保证数据的正确性、有效性和完整性。
2、分类:
- 主键约束:primary key
- 含义:非空且唯一
- 一张表只能有一个字段为主键
- 主键就是表中记录的唯一标识
- 非空约束:not null
- 值不能为null
- 唯一约束:unique
- 值不能重复
- MySql中唯一约束限定的列的值可以有多个null
- 外键约束:foreign key
- 让表于表产生关系,从而保证数据的正确性。(阿里规范:不得使用外键与级联,一切外键概念必须在应用层解决)
3、语法
1、主键约束语法
1、创建表时添加
在创建表时,添加主键约束
create table 表名(
id int primary key,-- 给id添加主键约束
name varchar(20)
);
2、创建表后添加
语法:
ALTER TABLE 表名 add primary key(字段名);
举例:
ALTER TABLE stu add primary key(id);
3、删除主键
ALTER TABLE 表名 DROP PRIMARY KEY;
4、自动增长
概念:如果某一列是数值类型的,使用 auto_increment 可以来完成值得自动增长
1. 在创建表时,添加主键约束,并且完成主键自增长
create table stu(
id int primary key auto_increment,-- 给id添加主键约束
name varchar(20)
);
2.创建表后添加
ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;
3. 删除自动增长
ALTER TABLE stu MODIFY id INT;
2、唯一约束
1、创建表时添加约束
CREATE TABLE stu(
id INT,
phone_number VARCHAR(20) UNIQUE -- 添加了唯一约束
);
* 注意mysql中,唯一约束限定的列的值可以有多个null
2、创建表后添加约束
ALTER TABLE 表名 add unique(字段名);
3、删除唯一约束
1.先查看约束名
show create table 表名;
2.找到唯一约束名,然后删除
ALTER TABLE 表名 DROP INDEX 约束名;
3、外键约束
1、创建表时添加约束
create table 表名(
....
外键列
constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称)
);
2、创建表后添加约束
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称);
3、删除非空约束
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;
2、非空约束语法
1、创建表时添加约束
CREATE TABLE stu(
id INT,
NAME VARCHAR(20) NOT NULL -- name为非空
);
2、创建表后添加约束
ALTER TABLE 表名 MODIFY 字段名 字段类型 NOT NULL;
3、删除非空约束
ALTER TABLE 表名 MODIFY 字段名 字段类型;
5、级联操作
用于操作具有外键关系的表,使其级联更新或级联删除。(阿里规范:不得使用外键与级联,一切外键概念必须在应用层解决)
1、添加级联
LTER TABLE 表名 ADD CONSTRAINT 外键名称
FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称) ON UPDATE CASCADE ON DELETE CASCADE ;
2、分类
- 级联更新:ON UPDATE CASCADE
- 级联删除:ON DELETE CASCADE
二、数据库的设计
2.1 多表之间的关系
1、多表之间的关系分类
-
一对一(了解)
如:人和身份证
分析:一个人只有一个身份证,一个身份证只能对应一个人
-
一对多(多对一):
如:部门和员工
分析:一个部门有多个员工,一个员工只能对应一个部门
-
多对多:
如:学生和课程
分析:一个学生可以选择很多门课程,一个课程也可以被很多学生选择
2、实现关系
- 一对一实现方式:一对一关系实现,可以在任意一方添加唯一外键指向另一方的主键。
- 一对多实现方式:在多的一方建立外键,指向一的一方的主键
- 多对多实现方式:多对多关系实现需要借助第三张中间表。中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键
3、小练习
-- 创建旅游线路分类表 tab_category
-- cid 旅游线路分类主键,自动增长
-- cname 旅游线路分类名称非空,唯一,字符串 100
CREATE TABLE tab_category (
cid INT PRIMARY KEY AUTO_INCREMENT,
cname VARCHAR(100) NOT NULL UNIQUE
);
-- 创建旅游线路表 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)
);
/*创建用户表 tab_user
uid 用户主键,自增长
username 用户名长度 100,唯一,非空
password 密码长度 30,非空
name 真实姓名长度 100
birthday 生日
sex 性别,定长字符串 1
telephone 手机号,字符串 11
email 邮箱,字符串长度 100
*/
CREATE TABLE tab_user (
uid INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(100) UNIQUE NOT NULL,
PASSWORD VARCHAR(30) NOT NULL,
NAME VARCHAR(100),
birthday DATE,
sex CHAR(1) DEFAULT '男',
telephone VARCHAR(11),
email VARCHAR(100)
);
/*
创建收藏表 tab_favorite
rid 旅游线路 id,外键
date 收藏时间
uid 用户 id,外键
rid 和 uid 不能重复,设置复合主键,同一个用户不能收藏同一个线路两次
*/
CREATE TABLE tab_favorite (
rid INT, -- 线路id
DATE DATETIME,
uid INT, -- 用户id
-- 创建复合主键
PRIMARY KEY(rid,uid), -- 联合主键
FOREIGN KEY (rid) REFERENCES tab_route(rid),
FOREIGN KEY(uid) REFERENCES tab_user(uid)
);
2.2 数据库设计的范式
1、概念:设计数据库时,需要遵循的一些规范。要遵循后边的范式要求,必须先遵循前边的所有范式要求
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。
2、范式分类
-
第一范式(1NF):每一列都是不可分割的原子数据项。
-
每一列属性都是不可再分的属性值,确保每一列的原子性
-
两列的属性相近或相似或一样,尽量合并属性一样的列,确保不产生冗余数据。
-
-
第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖)。
- 每一行的数据只能与其中一列相关,即一行数据只做一件事。只要数据列中出现数据重复,就要把表拆分开来。
如上图,一个人同时订几个房间,就会出来一个订单号多条数据,这样子联系人都是重复的,就会造成数据冗余。我们应该把他拆开来。
-
第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)。
- 数据不能存在传递关系,即没个属性都跟主键有直接关系而不是间接关系。像:a-->b-->c 属性之间含有这样的关系,是不符合第三范式的。
3、扩充
* 几个概念:
1. 函数依赖:A-->B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A
例如:学号-->姓名。 (学号,课程名称) --> 分数
2. 完全函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定需要依赖于A属性组中所有的属性值。
例如:(学号,课程名称) --> 分数
3. 部分函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定只需要依赖于A属性组中某一些值即可。
例如:(学号,课程名称) -- > 姓名
4. 传递函数依赖:A-->B, B -- >C . 如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称 C 传递函数依赖于A
例如:学号-->系名,系名-->系主任
5. 码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码
例如:该表中码为:(学号,课程名称)
* 主属性:码属性组中的所有属性
* 非主属性:除过码属性组的属性
4、总结
三大范式的作用:优化数据库的结构,减少数据库的冗余。
一范式:确定表的原子性,表中的每一列都不可再分。
二范式:在1范式的基础上,消除表中的部分函数依赖--》需要确保表中的每一列都和主键相关,而不能只于主键中的某一部分相关。(主要针对联合主键)
如:假设存在联合主键(a,b),其他列c,d。则该表需要满足:(a,b)-->c ,(a,b)-->d。
如果出现:a-->c则该表不符合规范
三范式:在2范式的基础上,消除表中的传递依赖--》需要确保表中的每一列都和主键直接相关,而不能间接相关。
如:假设存在主键m,非主键x,y。则需要满足,m-->x m-->y。
如果出现:m-->x , x-->y 然后得出 m-->y 则该表不符合规范。
如何使用三大范式去验证表设计的是否规范?
表中的每一列都与主键或联合主键直接相关。
其他列只与主键的某一部分相关 ×
通过主键和非主键列确定其他列 ×
三、事务
事务的基本介绍
1、什么是事务?
事务是由单独单元的一个或多个sql组成,这个单元中的sql之间是相互依赖的。当单独单元中的一个sql发生错误,则事务失败,会将整个单元回滚到事务开始之前。当单独单元中的所有sql都执行成功,则事务会顺利进行。
举一个不太恰当的例子,在炎热的夏天你想从冰箱拿一瓶可乐,你可以将从冰箱拿可乐看作一个事务,这个事务包括这几个操作:打开冰箱、选择可乐、关闭冰箱。如果这三个中的任何一个操作失败,你都会回到打开冰箱前的状态,如果这三个操作都成功了,你才可以喝可乐。
2、操作
- 开启事务:start transaction;
- 提交事务:commit;
- 回滚事务:rollback;
3、例子
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME, balance) VALUES ('zhangsan', 1000), ('lisi', 1000);
SELECT * FROM account;
UPDATE account SET balance = 1000;
-- 张三给李四转账 500 元
-- 0. 开启事务
START TRANSACTION;
-- 1. 张三账户 -500
UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan';
-- 2. 李四账户 +500
-- 出错了...
UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';
-- 发现执行没有问题,提交事务
COMMIT;
-- 发现出问题了,回滚事务
ROLLBACK;
4、MySQL数据库中事务默认自动提交
5、事务提交的两种方式
-
自动提交
- mysql就是自动提交的
- 一条DML(增删改)语句会自动提交一次事务。
-
手动提交:
- Oracle 数据库默认是手动提交事务
- 需要先开启事务,再提交
6、修改事务的默认提交方式
- 查看事务的默认提交方式:SELECT @@autocommit; -- 1 代表自动提交 0 代表手动提交
- 修改默认提交方式: set @@autocommit = 0;
事务的ACID原则与隔离级别
1、事务的四大特征
- 原子性:每一个事务都是不可分割的最小操作单位,要么同时成功,要么同时失败。
- 一致性:事务操作前后,数据总量不变。如银行转账,事务执行前后,数据的总量是一致的,不会凭空增加,也不会凭空减少。
- 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
- 隔离性:多个事务之间,相互独立,互不干扰。
2、事务的隔离级别
-
概念:多个事务之间隔离的,相互独立的。但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
-
存在问题
- 脏读:一个事务读取到另一个事务未提交的数据。
- 不可重复读:一个事务读取表中的某一行数据,多次读取的结果不一致。
- 幻读:在一个事务内读取到别的事务插入的数据,导致前后读取不一致。
tip:
- 脏读和不可重复读的区别:脏读是读取前一事务未提交的数据,而不可重复读是重新读取了前一事务已提交的数据。
- 幻读与不可重复读的区别:不可重复读读取到的是更新(update)数据,而幻读读取到的是插入(insert)数据。
在某些情况下,不可重复读与幻读是允许存在的,但是脏读是不允许存在的。
-
隔离级别
1、read uncommitted:读未提交
- 产生的问题:脏读、不可重复读、幻读
2、read committed:读已提交 (Oracle)
- 产生的问题:不可重复读、幻读
3、repeatable read:可重复读 (MySQL默认)
- 产生的问题:幻读
4、serializable:串行化
- 可以解决所有的问题
- 注意:隔离级别从小到大安全性越来越高,但是效率越来越低
- 数据库查询隔离级别:
- select @@tx_isolation;
- 数据库设置隔离级别:
- set global transaction isolation level 级别字符串;
* 演示:
set global transaction isolation level read uncommitted;
start transaction;
-- 转账操作
update account set balance = balance - 500 where id = 1;
update account set balance = balance + 500 where id = 2;