【MySQL】完整性约束 -- 2019-08-17 03:26:20

烂漫一生 提交于 2019-11-27 14:26:34

原文: http://blog.gqylpy.com/gqy/249

"


目录

not null

default

unique

单列唯一

联合唯一

primary key

单列主键

复合主键

auto_increment

步长与偏移量

foreign key


约束条件与数据类型的宽度一样,都是可选参数
作用:用于保证数据的完整性和一致性

主要分为:

  • primary key (pk)    # 标识该字段为该表的主键,能够唯一的标识记录
  • foreign key (pk)    # 标识该字段为该表的外键
  • not null    # 标识该字段不能为空
  • unique key (uk)    # 标识该字段的值是唯一的
  • auto_increment    # 标识该字段的值自动增长(整数类型,而且为主键)
  • default    # 为该字段设置默认值
  • unsigned    # 无符号
  • zerofill    # 使用0填充

说明:

  1. 是否允许为空,默认为null,设置not null,使字段不允许为空,必须赋值.
  2. 字段是否有默认值,缺省的默认值是null,如果插入记录时不给字段赋值,则此字段使用默认值null.
    (设置枚举的默认值,且不为空:sex enum('boy', 'girl') not null default 'boy'
    (设置年龄必须为正值(无符号)且不为空,默认为20:age int unsigned not null default 20

 



not null

设置not null,插入值时不能为空.

# 创建测试表mysql> create table test(id int not null);Query OK, 0 rows affected (0.08 sec) # 此时查看表结构,Null的标识为NO,即不允许为空 mysql> desc test;+-------+---------+------+-----+---------+-------+| Field | Type    | Null | Key | Default | Extra |+-------+---------+------+-----+---------+-------+| id    | int(11) | NO   |     | NULL    |       |+-------+---------+------+-----+---------+-------+1 row in set (0.00 sec) # 插入一个空字符串是可以的(本人是在Mac系统上实测,这一步骤可能与Windows或Linux有差异)mysql> insert into test values();Query OK, 1 row affected, 1 warning (0.03 sec) # 插入null时,直接报错mysql> insert into test values(null);ERROR 1048 (23000): Column 'id' cannot be null # 此时查询记录# 可见:int类型设置not null后,插入空字符串会替换成0mysql> select * from test;+----+| id |+----+|  0 |+----+1 row in set (0.00 sec)

default

设置一个字段有默认值后,则无论这个字段是null还是not null,都可以插入空,插入空默认填入default指定的默认值.

# 第一种情况:defaultmysql> create table test(id int default 1);Query OK, 0 rows affected (0.07 sec) mysql> desc test;+-------+---------+------+-----+---------+-------+| Field | Type    | Null | Key | Default | Extra |+-------+---------+------+-----+---------+-------+| id    | int(11) | YES  |     | 1       |       |+-------+---------+------+-----+---------+-------+1 row in set (0.00 sec) mysql> insert into test values();Query OK, 1 row affected (0.04 sec) mysql> select * from test;+------+| id   |+------+|    1 |+------+1 row in set (0.00 sec)  # 第二种情况:not null + defaultmysql> create table test(id int not null default 2);Query OK, 0 rows affected (0.05 sec) mysql> desc test;+-------+---------+------+-----+---------+-------+| Field | Type    | Null | Key | Default | Extra |+-------+---------+------+-----+---------+-------+| id    | int(11) | NO   |     | 2       |       |+-------+---------+------+-----+---------+-------+1 row in set (0.00 sec) mysql> insert into test values();Query OK, 1 row affected (0.04 sec) mysql> select * from test;+----+| id |+----+|  2 |+----+1 row in set (0.00 sec)

unique

中文翻译:不同的。在MySQL中称为单列唯一

单列唯一

# 创建unique方式1:# 指定name唯一mysql> create table test(    -> id int,    -> name char(3) unique    -> );Query OK, 0 rows affected (0.17 sec) # 插入相同的name,直接报错mysql> insert into test values(1, 'zyk'), (2, 'zyk');ERROR 1062 (23000): Duplicate entry 'zyk' for key 'name' # 此时正常mysql> insert into test values(1, 'zyk'), (2, 'xhh');Query OK, 2 rows affected (0.01 sec)Records: 2  Duplicates: 0  Warnings: 0  # 创建unique方式2:mysql> create table test(    -> id int,    -> name char(3),    -> unique(id),    -> unique(name)    -> );Query OK, 0 rows affected (0.09 sec) # 可见:Key标识为UNImysql> desc test;+-------+---------+------+-----+---------+-------+| Field | Type    | Null | Key | Default | Extra |+-------+---------+------+-----+---------+-------+| id    | int(11) | YES  | UNI | NULL    |       || name  | char(3) | YES  | UNI | NULL    |       |+-------+---------+------+-----+---------+-------+2 rows in set (0.00 sec)

联合唯一

即指定表中两条或以上的记录,只有当这些被指定的记录都相同时才符合联合唯一,否则不会被限制.

mysql> create table test(    -> ip char(15),    -> port int,    -> unique(ip, port)    # 联合唯一    -> );Query OK, 0 rows affected (0.08 sec) # 此时查看表结构,Key的标识为MULmysql> desc test;+-------+----------+------+-----+---------+-------+| Field | Type     | Null | Key | Default | Extra |+-------+----------+------+-----+---------+-------+| ip    | char(15) | YES  | MUL | NULL    |       || port  | int(11)  | YES  |     | NULL    |       |+-------+----------+------+-----+---------+-------+2 rows in set (0.00 sec) # 插入两条不完全相同的数据mysql> insert into test values    -> ('192.168.1.1', 3306),    -> ('192.168.1.2', 3306);Query OK, 2 rows affected (0.03 sec)    # 成功插入Records: 2  Duplicates: 0  Warnings: 0 # 再插入一条存在的数据,被阻止mysql> insert into test values('192.168.1.1', 3306);ERROR 1062 (23000): Duplicate entry '192.168.1.1    -3306' for key 'ip'

primary key

一张表中可以:单列做主键,多列做主键(复合主键)
约束:字段的值不为空且值唯一,等价于not null unique
MySQL的存储引擎默认是InnoDB,对于InnoDB存储引擎来说,一张表必须有一个主键.

单列主键

mysql> create table test(    -> id int primary key,    # 单列主键    -> name char(3)    -> );Query OK, 0 rows affected (0.05 sec) # 此时查看表结构,Key的标识为PRImysql> desc test;+-------+---------+------+-----+---------+-------+| Field | Type    | Null | Key | Default | Extra |+-------+---------+------+-----+---------+-------+| id    | int(11) | NO   | PRI | NULL    |       || name  | char(3) | YES  |     | NULL    |       |+-------+---------+------+-----+---------+-------+2 rows in set (0.00 sec) # 插入两条id不同的数据mysql> insert into test values    -> (1, 'zyk'),    -> (2, 'xhh');Query OK, 2 rows affected (0.00 sec)Records: 2  Duplicates: 0  Warnings: 0 # id已存在,阻止插入mysql> insert into test value(2, 'jein');ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'

复合主键

复合主键与联合唯一同理:
指定表中两条或以上的记录,只有当这些被指定的记录都相同时才符合复合主键的限制,否则不会被限制.

mysql> create table test(    -> ip char(15),    -> port int,    -> primary key(ip, port)    # 复合主键    -> );Query OK, 0 rows affected (0.06 sec) mysql> insert into test values    -> ('192.168.1.1', 3306),    -> ('192.168.1.2', 3306);Query OK, 2 rows affected (0.00 sec)Records: 2  Duplicates: 0  Warnings: 0 mysql> insert into test values('192.168.1.1', 3306);ERROR 1062 (23000): Duplicate entry '192.168.1.1    -3306' for key 'PRIMARY' mysql> desc test;+-------+----------+------+-----+---------+-------+| Field | Type     | Null | Key | Default | Extra |+-------+----------+------+-----+---------+-------+| ip    | char(15) | NO   | PRI | NULL    |       || port  | int(11)  | NO   | PRI | NULL    |       |+-------+----------+------+-----+---------+-------+2 rows in set (0.01 sec)

auto_increment

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

不指定id,则自动增长:

mysql> create table test(    -> id int primary key auto_increment,    # 约束字段自动增长    -> name varchar(20),    -> sex enum('boy', 'girl') default 'boy'    -> );Query OK, 0 rows affected (0.05 sec) # 表结构显示Extra的标识为auto_increment,即自动增长mysql> desc test;+-------+--------------------+------+-----+---------+----------------+| Field | Type               | Null | Key | Default | Extra          |+-------+--------------------+------+-----+---------+----------------+| id    | int(11)            | NO   | PRI | NULL    | auto_increment || name  | varchar(20)        | YES  |     | NULL    |                || sex   | enum('boy','girl') | YES  |     | boy     |                |+-------+--------------------+------+-----+---------+----------------+3 rows in set (0.00 sec) # 插入两条记录mysql> insert into test(name) values ('zyk'), ('xhh');Query OK, 2 rows affected (0.00 sec)Records: 2  Duplicates: 0  Warnings: 0 # 可见,id确为自动增长mysql> select * from test;+----+------+------+| id | name | sex  |+----+------+------+|  1 | zyk  | boy  ||  2 | xhh  | boy  |+----+------+------+2 rows in set (0.00 sec)

指定id:

# 指定id插入两条数据mysql> insert into test values    -> (4, 'join', 'girl'),    -> (7, 'lay', 'girl');Query OK, 2 rows affected (0.01 sec)Records: 2  Duplicates: 0  Warnings: 0 mysql> select * from test;+----+------+------+| id | name | sex  |+----+------+------+|  1 | zyk  | boy  ||  2 | xhh  | boy  ||  4 | join | girl ||  7 | lay  | girl |+----+------+------+4 rows in set (0.00 sec) # 再次插入一条不指定id的记录,会随着值最大的id+1mysql> insert into test(name) values ('great white');Query OK, 1 row affected (0.00 sec) mysql> select * from test;+----+-------------+------+| id | name        | sex  |+----+-------------+------+|  1 | zyk         | boy  ||  2 | xhh         | boy  ||  4 | join        | girl ||  7 | lay         | girl ||  8 | great white | boy  |+----+-------------+------+5 rows in set (0.00 sec) # mysql> insert into test(id, name) values (3, 'aaa');Query OK, 1 row affected (0.04 sec) mysql> select * from test;+----+-------------+------+| id | name        | sex  |+----+-------------+------+|  1 | zyk         | boy  ||  2 | xhh         | boy  ||  3 | aaa         | boy  ||  4 | join        | girl ||  7 | lay         | girl ||  8 | great white | boy  |+----+-------------+------+6 rows in set (0.00 sec)

对于自增的字段,在用delete删除后,再插入值,改字段仍按照删除前的位置继续增长.
应使用truncate清空表,trunacte是直接清空表,再删除数据量庞大的表时非常有用.

# delete删除表记录mysql> delete from test;Query OK, 8 rows affected (0.00 sec) mysql> insert into test(name) values('zyk');Query OK, 1 row affected (0.04 sec) # 可见:delete删除表记录后,再插入值,仍然是按照删除前的位置继续增长mysql> select * from test;+----+------+------+| id | name | sex  |+----+------+------+|  9 | zyk  | boy  |+----+------+------+1 row in set (0.00 sec) # truncate清空表mysql> truncate test;Query OK, 0 rows affected (0.00 sec) mysql> insert into test(name) values('zyk');Query OK, 1 row affected (0.00 sec) # 此时id重新开始自增mysql> select * from test;+----+------+------+| id | name | sex  |+----+------+------+|  1 | zyk  | boy  |+----+------+------+1 row in set (0.00 sec)

清空表分区 delete 与 truncate 的区别:

  • delete from tb    # 有自增id时,删除后新增的数据的id仍然是按照未删除之前最大的id+1计算的
  • truncate table tb    # 直接清空表,数据量大时非常适用,删除速度比delete快,且id直接从0开始

步长与偏移量

auto_increment_increment:步长,默认为1
auto_increment_offset:起始的偏移量,默认为1

注意:如果偏移量大于步长,则偏移量的会被重设为1

# 查看步长与偏移量mysql> show variables like 'auto_i%';+--------------------------+-------+| Variable_name            | Value |+--------------------------+-------+| auto_increment_increment | 1     |    # 步长| auto_increment_offset    | 1     |    # 偏移量+--------------------------+-------+2 rows in set (0.00 sec) # session为会话设置,只在本次链接中有效mysql> set session auto_increment_increment = 5;Query OK, 0 rows affected (0.00 sec) # global全局(设置后需重新连接,才会生效)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_i%';+--------------------------+-------+| Variable_name            | Value |+--------------------------+-------+| auto_increment_increment | 5     || auto_increment_offset    | 3     |+--------------------------+-------+2 rows in set (0.00 sec) # 先清空表mysql> truncate table test;Query OK, 0 rows affected (0.04 sec) # 插入一条记录,可见:id是从3开始计算的mysql> insert into test(name) values ('zyk');Query OK, 1 row affected (0.04 sec) mysql> select * from test;+----+------+------+| id | name | sex  |+----+------+------+|  3 | zyk  | boy  |+----+------+------+1 row in set (0.01 sec) # 再插入几条数据,可见:id每次自增5mysql> insert into test(name) values ('zyk1'), ('zyk2'), ('zyk3');Query OK, 3 rows affected (0.00 sec)Records: 3  Duplicates: 0  Warnings: 0 mysql> select * from test;+----+------+------+| id | name | sex  |+----+------+------+|  3 | zyk  | boy  ||  8 | zyk1 | boy  || 13 | zyk2 | boy  || 18 | zyk3 | boy  |+----+------+------+4 rows in set (0.00 sec)

foreign key

一 快速理解foreign key

之前创建表的时候都是在一张表中添加记录,比如如下表:
![在这里插入图片描述](http://blog.gqylpy.com/media/ai/2019-03/c5c57780-f4f5-4916-8031-59471269e7d6.png)

公司有3个部门,但是有1个亿的员工,那意味着部门这个字段需要重复存储,部门名字越长,越浪费。
这个时候,
解决方法:
我们完全可以定义一个部门表
然后让员工信息表关联该表,如何关联,即foreign key
我们可以将上表改为如下结构:
![在这里插入图片描述](http://blog.gqylpy.com/media/ai/2019-03/fdc13268-a918-46ed-9458-6cd43d915c0e.png)

此时有两张表:
一张是employee表,简称emp表(关联表,也从从表)
一张是department表,简称dep表(被关联表,也叫主表)

注意:被关联表称为主表,关联表(也就是设置外键的表)称为从表.

# 先建立主表mysql> create table dep(    -> id int primary key,    -> name varchar(20) not null,    -> descripe varchar(20) not null    -> );Query OK, 0 rows affected (0.07 sec) # 再建立从表mysql> create table emp(    -> id int primary key,    -> name varchar(20) not null,    -> age int not null,    -> dep_id int,    # 与主表建立关联, fk_dep为自定义的外键名,不可重复:    -> constraint fk_dep foreign key(dep_id) references dep(id)        -> );Query OK, 0 rows affected (0.05 sec) mysql> insert into dep values    -> (1, "技术部", "各个大牛部们"),    -> (2, "销售部", "一本正经胡说八道部门"),    -> (3, "财务部", "花钱太多部门");Query OK, 3 rows affected (0.00 sec)Records: 3  Duplicates: 0  Warnings: 0 mysql> insert into emp values    -> (1, 'zyk', 19, 1),    -> (2, 'xhh', 18, 2),    -> (3, 'jein', 18, 3),    -> (4, 'egon', 18, 2),    -> (5, 'lisi', 18, 3);Query OK, 5 rows affected (0.04 sec)Records: 5  Duplicates: 0  Warnings: 0 # 如果从表中有与主表中关联的行,则不允许删除主表中的被关联的行mysql> delete from dep where id=1;ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`db`.`emp`, CONSTRAINT `fk_dep` FOREIGN KEY (`dep_id`) REFERENCES `dep` (`id`)) # 删除从表中与主表中关联的的行(dep_id=1)mysql> delete from emp where dep_id = 1;Query OK, 1 row affected (0.03 sec) # 此时可删除主表中的数据mysql> delete from dep where id = 1;Query OK, 1 row affected (0.01 sec) mysql> select * from dep;+----+-----------+--------------------------------+| id | name      | descripe                       |+----+-----------+--------------------------------+|  2 | 销售部    | 一本正经胡说八道部门               ||  3 | 财务部    | 花钱太多部门                      |+----+-----------+--------------------------------+2 rows in set (0.00 sec)

同步表:
on delete cascade    # 同步删除
on update cascade    # 同步更新

# 主表内容如下mysql> select * from dep;+----+-----------+--------------------------------+| id | name      | descripe                       |+----+-----------+--------------------------------+|  1 | 技术部    | 各个大牛部们                      ||  2 | 销售部    | 一本正经胡说八道部门               ||  3 | 财务部    | 花钱太多部门                      |+----+-----------+--------------------------------+3 rows in set (0.00 sec) # 建立从表mysql> create table emp(    -> id int primary key,    -> name varchar(20) not null,    -> age int not null,    -> dep_id int,    -> constraint fk_dep foreign key(dep_id) references dep(id)    -> on delete cascade    # 同步删除    -> on update cascade    # 同步更新    -> );Query OK, 0 rows affected (0.08 sec) # 从表插入以下数据mysql> select * from emp;+----+------+-----+--------+| id | name | age | dep_id |+----+------+-----+--------+|  1 | zyk  |  19 |      1 ||  2 | xhh  |  18 |      2 ||  3 | jein |  18 |      3 ||  4 | egon |  18 |      2 ||  5 | lisi |  18 |      3 |+----+------+-----+--------+5 rows in set (0.00 sec)  # 同步更新mysql> update dep set id=10 where id=1;Query OK, 1 row affected (0.04 sec)Rows matched: 1  Changed: 1  Warnings: 0 mysql> select * from dep;+----+-----------+--------------------------------+| id | name      | descripe                       |+----+-----------+--------------------------------+|  2 | 销售部    | 一本正经胡说八道部门               ||  3 | 财务部    | 花钱太多部门                      || 10 | 技术部    | 各个大牛部们                      |+----+-----------+--------------------------------+3 rows in set (0.00 sec) # 此时可见:与主表关联的dep_id也跟着变化了mysql> select * from emp;+----+------+-----+--------+| id | name | age | dep_id |+----+------+-----+--------+|  1 | zyk  |  19 |     10 ||  2 | xhh  |  18 |      2 ||  3 | jein |  18 |      3 ||  4 | egon |  18 |      2 ||  5 | lisi |  18 |      3 |+----+------+-----+--------+5 rows in set (0.00 sec)  # 同步删除mysql> delete from dep where id=3;Query OK, 1 row affected (0.00 sec) mysql> select * from dep;+----+-----------+--------------------------------+| id | name      | descripe                       |+----+-----------+--------------------------------+|  2 | 销售部    | 一本正经胡说八道部门           || 10 | 技术部    | 各个大牛部们                   |+----+-----------+--------------------------------+2 rows in set (0.00 sec) # 此时可见:与主表关联的dep_id也被删除了mysql> select * from emp;+----+------+-----+--------+| id | name | age | dep_id |+----+------+-----+--------+|  1 | zyk  |  19 |     10 ||  2 | xhh  |  18 |      2 ||  4 | egon |  18 |      2 |+----+------+-----+--------+3 rows in set (0.00 sec)

 

 



 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!