MySQL--约束

约束

1. 概述

为了防止不符合规范的数据进入数据库,在用户对数据进行插入、修改、删除等操作时,DBMS自动按照一定的约束条件对数据进行监测,使不符合规范的数据不能进入数据库,以确保数据库中存储的数据正确、有效、相容。

约束条件与数据类型的宽度一样,都是可选参数,主要分为以下几种:

约束条件 解释
NOT NULL 非空约束,指定某列不能为空;
UNIQUE 唯一约束,指定某列或者几列组合不能重复
PRIMARY KEY 主键,指定该列的值可以唯一地标识该列记录
FOREIGN KEY 外键,指定该行记录从属于主表中的一条记录,主要用于参照完整性

2. NOT NULL

是否可空,null表示空,非字符串

not null - 不可空

null - 可空

2.1 实例

mysql> create table t12 (id int not null);
Query OK, 0 rows affected (0.02 sec)

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

mysql> desc t12;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | NO   |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
row in set (0.00 sec)

#不能向id列插入空元素。 
mysql> insert into t12 values (null);
ERROR 1048 (23000): Column 'id' cannot be null

mysql> insert into t12 values (1);
Query OK, 1 row affected (0.01 sec)

2.2 DEFAULT

我们约束某一列不为空,如果这一列中经常有重复的内容,就需要我们频繁的插入,这样会给我们的操作带来新的负担,于是就出现了默认值的概念。

默认值,创建列时可以指定默认值,当插入数据时如果未主动设置,则自动添加默认值。

not null + default

mysql> create table t13 (id1 int not null,id2 int not null default 222);
Query OK, 0 rows affected (0.01 sec)

mysql> desc t13;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id1   | int(11) | NO   |     | NULL    |       |
| id2   | int(11) | NO   |     | 222     |       |
+-------+---------+------+-----+---------+-------+
rows in set (0.01 sec)

# 只向id1字段添加值,会发现id2字段会使用默认值填充
mysql> insert into t13 (id1) values (111);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t13;
+-----+-----+
| id1 | id2 |
+-----+-----+
| 111 | 222 |
+-----+-----+
row in set (0.00 sec)

# id1字段不能为空,所以不能单独向id2字段填充值;
mysql> insert into t13 (id2) values (223);
ERROR 1364 (HY000): Field 'id1' doesn't have a default value

# 向id1,id2中分别填充数据,id2的填充数据会覆盖默认值
mysql> insert into t13 (id1,id2) values (112,223);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t13;
+-----+-----+
| id1 | id2 |
+-----+-----+
| 111 | 222 |
| 112 | 223 |
+-----+-----+
rows in set (0.00 sec)

3. UNIQUE

唯一约束,指定某列或者几列组合不能重复。

3.1 示例:

方法一:
create table department1(
id int,
name varchar(20) unique,
comment varchar(100)
);


方法二:
create table department2(
id int,
name varchar(20),
comment varchar(100),
unique(name)
);


mysql> insert into department1 values(1,'IT','技术');
Query OK, 1 row affected (0.00 sec)
mysql> insert into department1 values(1,'IT','技术');
ERROR 1062 (23000): Duplicate entry 'IT' for key 'name'

3.2 not null + unique

mysql> create table t1(id int not null unique);
Query OK, 0 rows affected (0.02 sec)

mysql> desc t1;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | NO   | PRI | NULL    |       |
+-------+---------+------+-----+---------+-------+
row in set (0.00 sec)

3.3 联合唯一

create table service(
id int primary key auto_increment,
name varchar(20),
host varchar(15) not null,
port int not null,
unique(host,port) #联合唯一
);

mysql> insert into service values
    -> (1,'nginx','192.168.0.10',80),
    -> (2,'haproxy','192.168.0.20',80),
    -> (3,'mysql','192.168.0.30',3306)
    -> ;
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> insert into service(name,host,port) values('nginx','192.168.0.10',80);
ERROR 1062 (23000): Duplicate entry '192.168.0.10-80' for key 'host'

4. PRIMARY KEY

主键为了保证表中的每一条数据的该字段都是表格中的唯一值。换言之,它是用来独一无二地确认一个表格中的每一行数据。
主键可以包含一个字段或多个字段。当主键包含多个栏位时,称为组合键 (Composite Key),也可以叫联合主键。
主键可以在建置新表格时设定 (运用 CREATE TABLE 语句),或是以改变现有的表格架构方式设定 (运用 ALTER TABLE)。
主键必须唯一,主键值非空;可以是单一字段,也可以是多字段组合。

4.1 单字段主键

============单列做主键===============
#方法一:not null+unique
create table department1(
id int not null unique, #主键
name varchar(20) not null unique,
comment varchar(100)
);

mysql> desc department1;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id      | int(11)      | NO   | PRI | NULL    |       |
| name    | varchar(20)  | NO   | UNI | NULL    |       |
| comment | varchar(100) | YES  |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+
rows in set (0.01 sec)

#方法二:在某一个字段后用primary key
create table department2(
id int primary key, #主键
name varchar(20),
comment varchar(100)
);

mysql> desc department2;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id      | int(11)      | NO   | PRI | NULL    |       |
| name    | varchar(20)  | YES  |     | NULL    |       |
| comment | varchar(100) | YES  |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+
rows in set (0.00 sec)

#方法三:在所有字段后单独定义primary key
create table department3(
id int,
name varchar(20),
comment varchar(100),
primary key(id); #创建主键并为其命名pk_name

mysql> desc department3;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id      | int(11)      | NO   | PRI | NULL    |       |
| name    | varchar(20)  | YES  |     | NULL    |       |
| comment | varchar(100) | YES  |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+
rows in set (0.01 sec)

# 方法四:给已经建成的表添加主键约束
mysql> create table department4(
    -> id int,
    -> name varchar(20),
    -> comment varchar(100));
Query OK, 0 rows affected (0.01 sec)

mysql> desc department4;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id      | int(11)      | YES  |     | NULL    |       |
| name    | varchar(20)  | YES  |     | NULL    |       |
| comment | varchar(100) | YES  |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+
rows in set (0.01 sec)

mysql> alter table department4 modify id int primary key;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc department4;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id      | int(11)      | NO   | PRI | NULL    |       |
| name    | varchar(20)  | YES  |     | NULL    |       |
| comment | varchar(100) | YES  |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+
rows in set (0.01 sec)

4.2 多字段主键

==================多列做主键================
create table service(
ip varchar(15),
port char(5),
service_name varchar(10) not null,
primary key(ip,port)
);


mysql> desc service;
+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| ip           | varchar(15) | NO   | PRI | NULL    |       |
| port         | char(5)     | NO   | PRI | NULL    |       |
| service_name | varchar(10) | NO   |     | NULL    |       |
+--------------+-------------+------+-----+---------+-------+
rows in set (0.00 sec)

mysql> insert into service values
    -> ('172.16.45.10','3306','mysqld'),
    -> ('172.16.45.11','3306','mariadb')
    -> ;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into service values ('172.16.45.10','3306','nginx');
ERROR 1062 (23000): Duplicate entry '172.16.45.10-3306' for key 'PRIMARY'

4.3 自动增长 auto_increment

约束字段为自动增长,被约束的字段必须同时被key约束

4.3.1 设置自动增长

#不指定id,则自动增长
create table student(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') default 'male'
);

mysql> desc student;
+-------+-----------------------+------+-----+---------+----------------+
| Field | Type                  | Null | Key | Default | Extra          |
+-------+-----------------------+------+-----+---------+----------------+
| id    | int(11)               | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20)           | YES  |     | NULL    |                |
| sex   | enum('male','female') | YES  |     | male    |                |
+-------+-----------------------+------+-----+---------+----------------+
mysql> insert into student(name) values
    -> ('nick'),
    -> ('tank')
    -> ;

mysql> select * from student;
+----+------+------+
| id | name | sex  |
+----+------+------+
|  1 | nick | male |
|  2 | tank | male |
+----+------+------+


#也可以指定id
mysql> insert into student values(4,'asb','female');
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values(7,'wsb','female');
Query OK, 1 row affected (0.00 sec)

mysql> select * from student;
+----+------+--------+
| id | name | sex    |
+----+------+--------+
|  1 | nick | male   |
|  2 | tank | male   |
|  4 | asb  | female |
|  7 | wsb  | female |
+----+------+--------+


#对于自增的字段,在用delete删除后,再插入值,该字段仍按照删除前的位置继续增长
mysql> delete from student;
Query OK, 4 rows affected (0.00 sec)

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

mysql> insert into student(name) values('ysb');
mysql> select * from student;
+----+------+------+
| id | name | sex  |
+----+------+------+
|  8 | ysb  | male |
+----+------+------+

#应该用truncate清空表,比起delete一条一条地删除记录,truncate是直接清空表,在删除大表时用它
mysql> truncate student;
Query OK, 0 rows affected (0.01 sec)

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

mysql> select * from student;
+----+------+------+
| id | name | sex  |
+----+------+------+
|  1 | nick | male |
+----+------+------+
row in set (0.00 sec)

5. FOREIGN KEY

多表 :

假设我们要描述所有公司的员工,需要描述的属性有这些 : 工号 姓名 部门

公司有3个部门,但是有1个亿的员工,那意味着部门这个字段需要重复存储,部门名字越长,越浪费

解决方法: 我们完全可以定义一个部门表 然后让员工信息表关联该表,如何关联,即foreign key

5.1 foreign key 带来的约束作用

  1. 必须先创建主表, 再创建从表
  2. 在从表中插入一条记录,关联了一个主表中不存在的id , 导致插入失败, 必须保证外键的值必须是在主表中存在的
  3. 插入数据的顺序, 先插入主表记录, 在插入从表记录
  4. 从表更新外键时也必须保证, 外键的值在主表中是存在的
  5. 删除主表记录前 要保证从表中没有外键关联被删除的id
  6. 更新删除主表记录的主键时, 要保证从表中没有外键关联主表主键

5.2 示例

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)
	);

5.3 级联操作

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

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

5.3.1 两种级联操作

  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);

多表关系

1. 为什么要分表操作

有人说, 一张表不是很好吗? 分成多个表进行操作又乱又麻烦

其实不然, 就以下表为例:

看起来是不是很乱, 而且如果生产部的主管发生变更, 那所有生产部人员的Manager都需要进行相应的变更, 这样极大地增加了工作的难度和复杂度

所以我们就需要进行分表操作, 如果部门主管发生变更, 只需要改右边的表就可以了, 左边的表不需要变动

多表操作的优点:

  1. 节省内存空间
  2. 表与表之间分开, 除了外键约束之外, 没有任何关系, 修改一个表不会影响另外的表

2. 多对一

指的是从表的多条数据指向主表的同一个记录,比如员工和部门的关系, 一个部门可以有好多员工, 这就是多对一

处理方式

在多的一方保存相应的一的一方的编号

#部门:
	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),
	);

3. 多对多

例如老师和学生, 一个学生有不同的老师来教不同的课程, 二一个老师又有很多学生, 这就是多对多关系

处理方式

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

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));
 

案例

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 = "老李"
    )
);

4. 一对一

如一个客户对应一个学生, 站在两边看都是一对一的关系,

处理方式,

​ 确定先后顺序, 将先存在的数据作为主表 ,后存在的作为从表

​ 使两个表的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-13 16:15  蔚蓝的爱  阅读(183)  评论(0编辑  收藏  举报