数据库(五):约束关系
进击のpython
数据库——约束条件
+-------+---------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | char(6) | YES | | NULL | |
| sex | enum('male','female') | YES | | NULL | |
| hobby | set('sing','dance','Rap') | YES | | NULL | |
+-------+---------------------------+------+-----+---------+-------+
这个表你很熟悉吧,这其中的Null和Default就是约束条件
那其实不光有这些约束条件,在规定int的时候的unsigned也是约束条件
那本节就是针对约束条件来进行展开的
not null与default
Null是指该数据可以为空,Default是该参数的默认值,就好像python中的默认参数一样
那我们现在就针对这个进行操作
create table t8(id int,name char(6),sex enum('male','female') not null default "male");
mysql> create table t8(
->
-> id int,
->
-> name char(6),
->
-> sex enum('male','female') not null default "male"
->
-> );
Query OK, 0 rows affected (0.57 sec)
mysql> desc t8;
+-------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | char(6) | YES | | NULL | |
| sex | enum('male','female') | NO | | male | |
+-------+-----------------------+------+-----+---------+-------+
3 rows in set (0.06 sec)
那现在开始进行传值:
mysql> insert into t8(id,name) values(1,'ponny');
Query OK, 1 row affected (0.37 sec)
mysql> select * from t8;
+------+-------+------+
| id | name | sex |
+------+-------+------+
| 1 | ponny | male |
+------+-------+------+
1 row in set (0.00 sec)
那你就可以看到,这个sex这个属性,在我没有进行传值的时候,库中使用的就是默认值
unique
那null和default我们都了解完毕之后,接下来我们就开始对key进行研究
key有一个约束条件叫做unique,叫做唯一性约束,本意就是这个键不能重复
我们可能都默认的传数据使得每一个值都不唯一,但其实不限制,是可以重复的
那为了不让数据重复,我们就有需要加上约束(对原表进行操作,练习命令):
mysql> alter table t8 change id id int unique;
Query OK, 0 rows affected (0.89 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc t8;
+-------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| id | int(11) | YES | UNI | NULL | |
| name | char(6) | YES | | NULL | |
| sex | enum('male','female') | NO | | male | |
+-------+-----------------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
这样id的值就不会重复了,院线数据中有个id=1的数据,我们看看再存1可不可以
mysql> insert into t8(id,name) values(1,'tom');
ERROR 1062 (23000): Duplicate entry '1' for key 'id'
如果有还添加就会出现这样的报错
那除了这样的唯一,还有一种叫做联合唯一的东西
什么叫做联合唯一呢?举个例子:
A:name:ponny id:2
B:name:tom id:2
这两个数据中某一部分相等,但是整体不同,这就是整体唯一所带来的效果
多数据中只有一个唯一限制,其实就是联合限制
primary key
primary key 主键
他的约束就比较干净利落,相当于 not null unique 还记得表的默认引擎是什么吗?innodb对吧
在innodb引擎对主键是有规定的:一张表必须有一个主键 不对啊!前面的表都没有主键啊!不是也能创建成功吗?
是这样的,MySQL数据库是有一种机制,在创建表的时候,他会先看看有没有规定主键
没有规定?ok,再去找有没有符合 not null unique 这种约束的,把第一个出现这种约束的当做主键
也没有?ok,那这样的话,MySQL就会自己偷偷地再创建一个字段,当做主键
但是最后一种情况很明显,扩大了内存,毕竟自己偷偷地又创建了一个字段
同时,倒数第二种情况意味着你要同样的表查两遍,就会降低速度(一点速度也是速度啊!!!!)
所以说,就可以看出来,还是要规定主键的!!!
那主键其实也是有两种的:
单列主键
一般情况下都是id设为主键,先记着,后面再说为什么
create table t9(id int primary key,name char(5));
mysql> create table t9(id int primary key,name char(5));
Query OK, 0 rows affected (1.00 sec)
mysql> desc t9;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | char(5) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.08 sec)
就可以看到,这个key的类型是PRI,也就是主键的意思,同时也看到null是NO
至于为空和唯一,就自己试试好吧~~
复合主键
复合主键就像联合唯一那样,单个数据可以相同但是总体数据不能相同,改造一下t9表:
mysql> alter table t9 add sex enum('famale','male');
Query OK, 0 rows affected (1.44 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table t9 add tel int;
Query OK, 0 rows affected (0.88 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc t9;
+-------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | char(5) | YES | | NULL | |
| sex | enum('famale','male') | YES | | NULL | |
| tel | int(11) | YES | | NULL | |
+-------+-----------------------+------+-----+---------+-------+
4 rows in set (0.01 sec)
现在我想id和tel设为复合主键,肯定有同学要这么写:
alter table t9 add primary key (id,tel);
执行之后会出现什么情况呢???
mysql> alter table t9 add primary key (id,tel);
ERROR 1068 (42000): Multiple primary key defined
这个错误就大概是你有多个主键,所以不行这么操作
复合主键讲究的是,同时创建,对于这种情况,操作方法就是先把原先的主键删掉,再创建复合主键
alter table t9 drop primary key;
mysql> alter table t9 drop primary key;
Query OK, 0 rows affected (1.69 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc t9;
+-------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
| name | char(5) | YES | | NULL | |
| sex | enum('famale','male') | YES | | NULL | |
| tel | int(11) | YES | | NULL | |
+-------+-----------------------+------+-----+---------+-------+
4 rows in set (0.01 sec)
然后再创建复合主键:
alter table t9 add primary key(id,tel);
mysql> alter table t9 add primary key(id,tel);
Query OK, 0 rows affected (1.49 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc t9;
+-------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | char(5) | YES | | NULL | |
| sex | enum('famale','male') | YES | | NULL | |
| tel | int(11) | NO | PRI | 0 | |
+-------+-----------------------+------+-----+---------+-------+
4 rows in set (0.01 sec)
这是有表修改的方法,也可以在创建表的时候直接定义复合主键:
create table t10(
id int,
name char(6),
tel int,
primary key(id,tel)
);
# 横着写会,竖着写就不会了????
mysql> create table t10(
->
-> id int,
->
-> name char(6),
->
-> tel int,
->
-> primary key(id,tel)
->
-> );
Query OK, 0 rows affected (1.11 sec)
mysql> desc t10;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | 0 | |
| name | char(6) | YES | | NULL | |
| tel | int(11) | NO | PRI | 0 | |
+-------+---------+------+-----+---------+-------+
3 rows in set (0.01 sec)
正好借着新建的这个表来验证一下复合主键的特性:
insert into t10(id,tel) values(1,1);
insert into t10(id,tel) values(2,1);
insert into t10(id,tel) values(1,2);
insert into t10(id,tel) values(2,1);
mysql> insert into t10(id,tel) values(1,1);
Query OK, 1 row affected (0.54 sec)
mysql>
mysql> insert into t10(id,tel) values(2,1);
Query OK, 1 row affected (0.05 sec)
mysql>
mysql> insert into t10(id,tel) values(1,2);
Query OK, 1 row affected (0.12 sec)
mysql>
mysql> insert into t10(id,tel) values(2,1);
ERROR 1062 (23000): Duplicate entry '2-1' for key 'PRIMARY'
mysql> select * from t10;
+----+------+-----+
| id | name | tel |
+----+------+-----+
| 1 | NULL | 1 |
| 1 | NULL | 2 |
| 2 | NULL | 1 |
+----+------+-----+
3 rows in set (0.00 sec)
前面的都添加成功了,而传入(2,1)的值提示的是,已经有了对吧~所以没有添加进去
auto_increment
自增约束
创建表格的时候,其实都默认的写了一个字段 id
那你有没有想过这个id是干什么用的?其实这个id是用来标志这是第几条记录的
那既然是标识是第几条记录的,那我们有没有一种方法,让它自动生成而不用我们自己写?
我们想要的效果是每插入一条记录,他就自动的加一,这是最好的对吧,所以自增就出现了
那一般约束条件都是直接在类型后面直接加对吧
create table t11(id int auto_increment);
mysql> create table t11(id int auto_increment);
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
他给我报了一个错误,什么错误呢?表类型错误,说一个表只能有一个自增,而且他还必须是键
我们现在学过哪几种建?一个是unique,一个是primary key
前面也提过,id最好设置为主键,所以上面的命令应该再优化一下:
create table t11(id int primary key auto_increment);
mysql> create table t11(id int primary key auto_increment);
Query OK, 0 rows affected (0.99 sec)
mysql> desc t11;
+-------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
+-------+---------+------+-----+---------+----------------+
1 row in set (0.01 sec)
那我再添加一个名字的字段(自己用指令添加)
那既然是自增长的 也就意味着我可以不用传id值,他会自动帮我处理
mysql> insert into t11(name) values('ponny'),('Tom');
Query OK, 2 rows affected (0.39 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from t11;
+----+-------+
| id | name |
+----+-------+
| 1 | ponny |
| 2 | Tom |
+----+-------+
2 rows in set (0.00 sec)
和我们想要的效果是一样的~达到了自己添加,id自动增长,从1开始,每次增长1个
id可以不写,那我要是非要写呢?可不可以添加成功呢?
mysql> insert into t11(id,name) values(5,"Jerry");
Query OK, 1 row affected (0.40 sec)
mysql> select * from t11;
+----+-------+
| id | name |
+----+-------+
| 1 | ponny |
| 2 | Tom |
| 5 | Jerry |
+----+-------+
3 rows in set (0.00 sec)
是可以添加成功的,那么我接下来再自增会出现什么情况呢?
mysql> insert into t11(name) values('ponny'),('Tom');
Query OK, 2 rows affected (0.38 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from t11;
+----+-------+
| id | name |
+----+-------+
| 1 | ponny |
| 2 | Tom |
| 5 | Jerry |
| 6 | ponny |
| 7 | Tom |
+----+-------+
5 rows in set (0.00 sec)
你会发现他不是在断的地方进行自增,而是以最后一组记录的id来进行的自增
还有个问题,我能不能指定他的起始,指定他一次增多少呢?
你别说,MySQL还真的提供了这个方法,我们可以先看一下他的默认设置
show variables like 'auto_inc%'
其中like就是模糊匹配,现在不说,这个%的意思是我要的是auto_inc开头的
当然,如果你能把increment全部拼出来,就不需要这个%了
mysql> show variables like 'auto_inc%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 1 |
| auto_increment_offset | 1 |
+--------------------------+-------+
2 rows in set (0.00 sec)
可以看到两条设置 increment:步长;offset:起始位置
可以看到后面都是1,代表着,从1增加,每次增加1
那既然能拿到,就能够设置,在设置上也有两种操作
分别是:set session 和 set global
set session 只对当次有效
set global 一直有效
设置步长:set global auto_increment_increment=5;
设置起始位置这就有说法了!
起始位置其实叫做起始偏移量,就相当于我已经走出去一步了,所以不应该超过步长才对
但是可以小于等于步长,也就意味着,如果设置超过了,起始位置就无效了
设置起始:set global auto_increment_offset=3;
mysql> set global auto_increment_increment=5;
Query OK, 0 rows affected (0.00 sec)
mysql> set global auto_increment_offset=3;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'auto_inc%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 1 |
| auto_increment_offset | 1 |
+--------------------------+-------+
2 rows in set (0.00 sec)
设置之后你发现,诶?怎么没变呢??原因是我们应该更新一下,就是退了重进
当我重进一下就变成了:
mysql> show variables like 'auto_inc%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 5 |
| auto_increment_offset | 3 |
+--------------------------+-------+
2 rows in set (0.00 sec)
那我们试一下:
mysql> insert into t11(name) values("da"),("daw");
Query OK, 2 rows affected (0.07 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from t11;
+----+-------+
| id | name |
+----+-------+
| 1 | ponny |
| 2 | Tom |
| 5 | Jerry |
| 6 | ponny |
| 7 | Tom |
| 8 | da |
| 13 | daw |
+----+-------+
7 rows in set (0.00 sec)
看,是不是从8直接到13了~
这里有一个现象:
mysql> delete from t11;
Query OK, 7 rows affected (0.48 sec)
mysql> select * from t11;
Empty set (0.00 sec)
mysql> insert into t11(name) values('sa'),('de');
Query OK, 2 rows affected (0.38 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from t11;
+----+------+
| id | name |
+----+------+
| 18 | sa |
| 23 | de |
+----+------+
2 rows in set (0.00 sec)
你会发现,即使删掉了,他还是会继续自增,这不是我们想要的效果
这个时候你说啊~我自己个给id也行,是也行,但是我们还是没有解决删干净这个问题
所以新的命令就出现了truncate t11;
mysql> truncate t11;
Query OK, 0 rows affected (0.54 sec)
mysql> insert into t11(name) values('sa'),('de');
Query OK, 2 rows affected (0.07 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from t11;
+----+------+
| id | name |
+----+------+
| 3 | sa |
| 8 | de |
+----+------+
2 rows in set (0.00 sec)
这是不是才是真正的达到了我们的要求啊~
通过这个例子我们可以看出,删表不建议delete,而应该使用truncate
delete的使用往往是跟着where连用的~
foreign key
在实际操作中,并不是只有一个表的,比如一个公司
员工要在各个部门上班,各个部门又有各个部门的信息
那讲道理你不能把员工信息和部门信息放在一张表里吧
姓名 | 部门 | 部门负责人 | 部门电话 | ... |
---|---|---|---|---|
Tom | 研发 | Alisa | 135XXXXXXXX | ... |
Jerry | 产品 | John | 173XXXXXXXX | ... |
Jevious | 产品 | John | 173XXXXXXXX | ... |
Ponny | 研发 | Alisa | 135XXXXXXXX | ... |
Daddy | 事务 | Kin | 186XXXXXXXX | ... |
就可以看出来,同一张表格里相同信息多不多?多!
而且还有问题,部门负责人永远都是XXX?不会换人?要是换人,这些数据都要改
而且 XXX一直都在XXX部门?也不一定吧,要是部门一改,那后面所有都要改
要是照这种表结构,这就十分的麻烦~我们可以怎么做呢?我们可以把表拆开
姓名 | ... |
---|---|
Tom | ... |
Jerry | ... |
Jevious | ... |
Ponny | ... |
Daddy | ... |
部门 | 部门负责人 | 部门电话 | ... |
---|---|---|---|
研发 | Alisa | 135XXXXXXXX | ... |
产品 | John | 173XXXXXXXX | ... |
事务 | Kin | 186XXXXXXXX | ... |
上面提出的问题就已经完美解决了,但是这两个表格联系上了吗?很明显没有,而且员工确实应该有部门
那我们表格就可以这么设置:
姓名 | 部门ID | ... |
---|---|---|
Tom | 1 | ... |
Jerry | 2 | ... |
Jevious | 2 | ... |
Ponny | 1 | ... |
Daddy | 3 | ... |
部门ID | 部门 | 部门负责人 | 部门电话 | ... |
---|---|---|---|---|
1 | 研发 | Alisa | 135XXXXXXXX | ... |
2 | 产品 | John | 173XXXXXXXX | ... |
3 | 事务 | Kin | 186XXXXXXXX | ... |
这样是不是就通过部门id 让两个表有了联系
但是同理也出来了,如果还有个扩展表格,比如部门负责人信息,也像部门这样分离出来
id取得也是1 2 3 ,那员工表里面的部门id的1 2 3指的是部门表还是部门信息表?
是不是没有硬性规定说我的这个部门id就是来自于部门这个表
所以我们需要给他个硬性规定,foreign key 外键就出现了
create table staff(
id int primary key,
name char(10),
department_id int,
foreign key (department_id) references department(id)
);
但是你要是绑定的前提是不是得有这张表啊:
create table department(
id int primary key,
name char(5),
leader char(10),
tel char(11)
);
mysql> create table department(
-> id int primary key,
-> name char(5),
-> leader char(10),
-> tel char(11)
-> );
Query OK, 0 rows affected (0.74 sec)
mysql> create table staff(
-> id int primary key,
-> name char(10),
-> department_id int,
-> foreign key (department_id) references department(id)
-> );
Query OK, 0 rows affected (0.52 sec)
mysql> show tables;
+-------------+
| Tables_in_t |
+-------------+
| department |
| staff |
+-------------+
2 rows in set (0.05 sec)
表建好了,就是写入记录,其实还是一样,要先写被关联的表
你想啊,你不先写被关联的表,你关联谁啊?,所以我们先对被关联的表进行赋值
insert into department values
(1,'研发','Alisa','135XXXXXXXX'),
(2,'产品','John','173XXXXXXXX'),
(3,'事务','Kin','186XXXXXXXX');
mysql> insert into department values
->
-> (1,'研发','Alisa','135XXXXXXXX'),
->
-> (2,'产品','John','173XXXXXXXX'),
->
-> (3,'事务','Kin','186XXXXXXXX')
-> ;
Query OK, 3 rows affected (0.23 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from department;
+----+--------+--------+-------------+
| id | name | leader | tel |
+----+--------+--------+-------------+
| 1 | 研发 | Alisa | 135XXXXXXXX |
| 2 | 产品 | John | 173XXXXXXXX |
| 3 | 事务 | Kin | 186XXXXXXXX |
+----+--------+--------+-------------+
3 rows in set (0.01 sec)
建立主表
insert into staff values
(1,'Tom',1),
(2,'Jerry',2),
(3,'Jevious',2),
(4,'Ponny',1),
(5,'Daddy',3);
mysql> insert into staff values
->
-> (1,'Tom',1),
->
-> (2,'Jerry',2),
->
-> (3,'Jevious',2),
->
-> (4,'Ponny',1),
->
-> (5,'Daddy',3);
Query OK, 5 rows affected (0.41 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from staff;
+----+---------+---------------+
| id | name | department_id |
+----+---------+---------------+
| 1 | Tom | 1 |
| 2 | Jerry | 2 |
| 3 | Jevious | 2 |
| 4 | Ponny | 1 |
| 5 | Daddy | 3 |
+----+---------+---------------+
5 rows in set (0.00 sec)
代码都是根据需求来的,假设有一天,事务部解散了,数据要怎么办呢?
很明显要先删掉员工表里department_id=3的,而不应该先去删department这个表
因为先删department,就会让staff表里的关联到事务的记录的最后一项变得没有意义
(这部分的描述要是不理解可以自己实际操作,删掉 事务 部门)
那,我这要想解散一个部门还要多表联动?这是两个表,这表要是多点,也就牵一发动全身
所以有没有什么好办法,能让解散变得简单点呢?我删掉部门,和这个关联的都删掉
实际上,在外键的设定上还有几个参数没介绍:
on delete cascade
删除同步
on update cascade
更新同步
那我们重新建立:
create table staff(
id int primary key,
name char(10),
department_id int,
foreign key (department_id) references department(id) on delete cascade on update cascade
);
其他的都跟上面一样,那我们来试试可不可以直接删部门!
mysql> delete from department where id = 3;
Query OK, 1 row affected (0.41 sec)
mysql> select * from staff;
+----+---------+---------------+
| id | name | department_id |
+----+---------+---------------+
| 1 | Tom | 1 |
| 2 | Jerry | 2 |
| 3 | Jevious | 2 |
| 4 | Ponny | 1 |
+----+---------+---------------+
4 rows in set (0.00 sec)
mysql> select * from department;
+----+--------+--------+-------------+
| id | name | leader | tel |
+----+--------+--------+-------------+
| 1 | 研发 | Alisa | 135XXXXXXXX |
| 2 | 产品 | John | 173XXXXXXXX |
+----+--------+--------+-------------+
2 rows in set (0.00 sec)
这是不是我们想要的效果完美
那我们再试试修改:
mysql> update department set id=3 where id = 2;
Query OK, 1 row affected (0.43 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from department;
+----+--------+--------+-------------+
| id | name | leader | tel |
+----+--------+--------+-------------+
| 1 | 研发 | Alisa | 135XXXXXXXX |
| 3 | 产品 | John | 173XXXXXXXX |
+----+--------+--------+-------------+
2 rows in set (0.00 sec)
mysql> select * from staff;
+----+---------+---------------+
| id | name | department_id |
+----+---------+---------------+
| 1 | Tom | 1 |
| 2 | Jerry | 3 |
| 3 | Jevious | 3 |
| 4 | Ponny | 1 |
+----+---------+---------------+
4 rows in set (0.00 sec)
当我将部门的编号修改之后,看,原关联表也进行了修改!这就是最完美的关联!
但是!!!!
!!!!!!
不推荐使用外键!
为什么?因为这么写无异于将两个表耦合在一起了,对于扩展来说是十分不友好的
我们应该做的,是让两个表的逻辑相关联,通过代码逻辑让其产生联系,而让每个表都是独立的个体
所以不建议这样做出硬性关联!