MySQL之约束与多表关系

约束

什么是约束 constraint

约束就是一种限制,例如宪法,让你不能做某些事情

数据库的约束,是对数据的安全性,完整性的保证;

mysql中的约束

unique

唯一性约束,表示这个字段不能出现重复的值, 用于唯一标识一条记录

例如身份证号码,学号等

not null

非空约束,表示这个字段的值不能为空

例如,账户名,密码等,

null

一些数据类型默认就是可以为空的

default

默认值,用于给某一个字段设置默认值

普通约束测试:
#完整的建表语句
create table table_name(字段名称 字段类型[(宽度) 约束]) charset utf8;

# 学生表 具备 姓名 性别 学号
create table student(
   # 非空
name char(20) not null,
   # 默认值
   gender enum("g","b") default "b",
   # 唯一
   id int  unique
)
#测试:
insert into student values(null,null,null);   # 错误原因是 name 不能为空
insert into student values("jack",null,null);  # 可以插入 null也是一个特殊的值 并且id的唯一约束 也可以为null
insert into student(name,id) values("jack",null); #可以插入 当没有给gender指定参数时,将使用默认值
alter table student modify id int unique not null; # 为已经存在的字段添加约束

primary key

主键约束,从约束角度来看就等同于,非空+唯一

主键与普通的约束的区别

create table person(
  id char(19) primary key,
name char(20)
);

insert into person values("1","rose");# ok
insert into person values("1","jack");# ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'主键冲突;
insert into person values("2","jack"); # ok
insert into person values(null,"tom"); # Column 'id' cannot be null #主键不能为空  


#从约束角度来看就等同于,非空+唯一  
create table person2(
  id char(19),
name char(20)
);

# 在innodb存储引擎中 ,主键用于组织数据 (树形结构)  
# 也就说主键对于innodb引擎来说是必须要的,没有不行!
# 如果没有手动指定主键  
# mysql 会自动查找一个具备非空且唯一的字段作为主键
# 如果也没有也没有这样的字段,mysql会创建一个隐藏字段 作为主键
# 首先明确主键是一种索引 unique也是,索引的作用是加速查询  
# 如果我们在查询语句中没有使用索引字段 ,mysql将无法为我们的查询加速 意味着如果没有主键,将无法加速查询

总的来说:主键 具备约束的作用,还能加快我们的查询速度,所以今后在创建表的时候都应该创建索引字段
应该将什么样的字段设置为主键????
如果本来的业务中就存在非空且唯一的字段 那就把它设为主键   如果没有就自己添加一个字段专门作为主键  
通常我们会将主键设置为类int 类型,是为了方便保证其唯一;

案例:
mysql> create table PC(
  id int primary key,
  pp char(20),
  model char(10),
  price float
);

mysql> insert into PC values(1,"IBM","1214SB",40000);
mysql> insert into PC values(2,"DELL","1200DSB",4000);
mysql> select *from PC;
mysql> select *from PC where id = 1;
mysql> select *from PC where pp = "DELL";
为主键设置自动增长

当我们创建了主键字段时,插入数据必须保证主键是唯一的不能重复,这就需要我们自己管理主键值,这是很麻烦的,所以mysql有一个自动增长的属性,可以添加在整形字段上,每次插入数据时,都可以自动的插入值,并且每次加1不会冲突;

create table teacher(
  id int primary key auto_increment,
  name char(10)
);
insert into teacher values(null,"jack"); # 对于自动增长的字段可以给null 也会自动生成值
insert into teacher(name) values("jack");# 也可以跳过这个字段



create table teacher3(
  id char unique auto_increment ,
  name char(10)
);

自动增长可以用在 具备索引,并且是数字类型的字段上,但是通常与主键一起使用!

表之间的关系

foreign key

外键约束,用于指向另一个表的主键字段

# 创建表的时候添加外键
create table teacher(
      id int primary key auto_increment,
      name char(20),
      gender char(1),
      dept_id int,
  foreign key(dept_id) references dept(id)
);
解释:
foreign key(dept_id) references dept(id)
dept_id 表示当前表的外键字段  
dept 表示要关联哪个表
dept(id) id表示关联的dept表的id字段


主表与从表  
先有主表 再有从表  
先有dept 再有teacher

foreign key 带来的约束作用:

1.在从表中插入一条记录,关联了一个主表中不存在的id 导致插入失败

必须保证部门id (外键的值) 必须是在主表中存在的

2.插入数据的顺序

先插入主表记录 在插入从表记录

3.从表更新外键时也必须保证 外键的值在主表中是存在的

4.删除主表记录前 要保证从表中没有外键关联被删除的id

delete from dept where id = 1;# 报错

必须先删除从表 再删除主表

delete from teacher where dept_id = 1;

delete from dept where id = 1;

5.更新主表记录的主键时 要保证从表中没有外键关联被删除的id

6.必须先创建主表

7.删除表 必须先删除从表

强调:foreign key就是用来保证两张表之间的关联关系是正确的!

 

练习: 班级表 和 学员表

主表是班级

从表是学员 外键加给学员

create table class(
      id int primary key auto_increment,
      name char(20)
);

create table student(
      id int primary key auto_increment,
      name char(20),
      gender char(1),
      c_id int,
  foreign key(c_id) references class(id)
);

 

级联操作 cascade

当我们需要删除部门(主表)信息 时,必须先删除从表中关联的数据,很麻烦

级联操作指的就是,当你操作主表时,自动的操作从表

两种级联操作

1.级联的删除

当删除主表时自动删除从表中相关数据

2.级联更新

当主表的主键更新时自动的更新关联的从表数据

案例:以上面的班级个学员为例:

drop table if exists class;
# 如果这表存在 才执行删除 可以避免报错 if exists
# if not exists 如果不存在才执行
create table class(
      id int primary key auto_increment,
      name char(20)
);
insert into class values(null,"py9");
insert into class values(null,"py10");

#创建表的时候指定级联操作
drop table if exists student;
create table student(
      id int primary key auto_increment,
      name char(20),
      gender char(1),
      c_id int,
  foreign key(c_id) references class(id)
        on update cascade
  on delete cascade
);
# 级联操作可以单独使用 也可以一起使用   空格隔开即可
insert into student values(null,"jack","m",1);
insert into student values(null,"rose","m",1);
insert into student values(null,"tom","m",2);

 

外键的使用

什么时候使用外键:

表之间存在关联关系 ,

首先要确定表之间的关系:

多对一,

一对多(多对一)

 

或者

 

老师和部门的关系 
老师的角度看
一个老师应该对应有一个部门
一个老师可以对应对多个部门?  不行 一个老师只能属于一个部门 (要看具体业务要求)!
多个老师可以对应一个部门
多对一
部门的角度看
一个部门可以对应多个老师
一个部门可以对应一个老师
多个部门可以对应一个老师? 不行
一对多
如何处理一对多(多对一)?
在老师表中存储 部门id
即多的一方存储 一的一方的id

处理方式

在一的一方即teacher表中保存相应部门(多的一方)的编号;

案例:

    #部门:
create table dept(
      id int primary key auto_increment,
      name char(20),
      job char(50),
      manager char(10)
);
#老师表:
create table teacher(
      id int primary key auto_increment,
      name char(20),
      gender char(1),
      dept_id int,
       foreign key(t_id) references teacher(id),
);

 

多对多

如何确定多对多关系

例如 老师表 和 学生表

套路一样 先站在老师表 在站在学生表

老师表角度: 一个老师可以对应多个学生 一对多

学生表角度: 一个学生可以对应多个老师 一对多

如果双方都是一对多的关系 那么 两者是多对多关系

处理方式

建立一个中间表,用于存储关系,至少具备两个字段分别指向老师和学生的主键,两个字段都是外键 如下:

create table t_s_r(
  id int primary key auto_increment,
  t_id int,
  s_id int,
   foreign key(t_id) references teacher(id),
   foreign key(s_id) references student(id),
   unique key(t_id,s_id)
  );
   
上表中id是可选的,问题是如何保证没有重复的关系 ?

方式1:
给两个字段设置为联合唯一 + 非空
# 假设表已经存在了
alter table t_s_r add unique key(t_id,s_id);
# 创建表时指定 多字段联合唯一
create table t_s_r2(t_id int,s_id int,unique key(t_id,s_id));
方式2:
# 推荐 将中间的关系表 两个id 作为联合主键 同时具备了 唯一且非空约束
create table t_s_r3(t_id int,s_id int,primary key(t_id,s_id));



处理多对多关系
1.创建两个主表 如学员 和 老师
2.创建关系表 包含两个字段 分别设置外键 指向对应的表
3.将两个字段 作为联合主键  

案例:

 

create table student(id int primary key auto_increment,name char(10));
create table teacher(id int primary key auto_increment,name char(10));
create table t_s_r(
  t_id int,
  s_id int,
   foreign key(t_id) references teacher(id),
   foreign key(s_id) references student(id),
   primary key(t_id,s_id)
  );
# 先插入 学生或是老师都可以 但是关系表一定是最后添加的
insert into teacher values(null,"bgon"),(null,"nike");

 insert into student values(null,"老王"),(null,"老李");
 
 # 老王被bgon教过
 insert into t_s_r values(1,1);
  # nike教过老李
 insert into t_s_r values(2,2);
  # nike教过老王
 insert into t_s_r values(2,1);



已知老师名称为bgon 请找出他教过那些学生
1.通过名字获取 bgon的id
2.拿着id取关系表中拿到一堆学生的id
3.通过学生的id取出学生的信息
select id from teacher where name = "bgon";
select s_id from t_s_r where t_id = 1;
select * from student where id = 1;

# 子查询方式   把一条语句的结果作为另一条语句的条件!
select * from student where id = (select s_id from t_s_r where t_id = (select id from teacher where name = "bgon"));
 
 
 
已知学生名为老李 请查询出 哪些老师教过他
1.通过名字获取老李的id
select id from student where name = "老李";
2.拿着id去关系表 找出老师的id
select t_id from t_s_r where s_id = 2;
3.通过老师的id取出老师的信息
select name from teacher where id = x;


# 子查询方式:
select name from teacher where id = (
   select t_id from t_s_r where s_id = (
       select id from student where name = "老李"
  )
);

一对一关系

如一个客户对应一个学生:

站在两边看都是一对一的关系,

处理方式,

确定先后顺序

将先存在的数据作为主表

后存在的作为从表

使两个表的id保持一一对应

方法1: 从表的id 即是主键又是外键

方法2:从表的id设置为外键 并保证唯一

案例:

# 人员表
create table person(
id int primary key auto_increment,
  name char(10),
  age int
);
# 详情表
create table person_info(
id int primary key,
  height float,
  weight float,
   foreign key(id) references person(id)
);
#再这样的关系中 必须先插入主表即person 拿到一个id 在添加详情表的数据  

#将一条完整数拆分到不同表中,可以提高查询的效率,上述方式称之为垂直分表!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-07-12 17:09  纵横捭阖行  阅读(479)  评论(0编辑  收藏  举报