【MySQL表的约束】

前言

剑指offer:一年又5天

表的约束

我们上一篇文章所讲的数据类型也是一种约束–不同类型有对应的数据范围;约束本质上是通过技术手段倒逼程序员输入正确的数据,反过来站在数据库角度:凡是数据库内的数据都是符合约束的;
约束保证了数据库中数据的完整性和可预期性。
但是仅仅有类型约束还是不够的,本篇文章我们会讲解表的其他约束:空值约束,默认值约束,列描述,零填充,主键,唯一键和外键约束。

1、空属性

两个值:null(默认的)和not null(不为空)
数据库默认字段基本都是字段为空,但是实际开发时,尽可能保证字段不为空,因为数据为空没办法参与运算。

mysql> use db2;
Database changed
mysql> create table tb1(
    -> c1 int,
    -> c2 int not null                   -- 设置c2列为非空
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> show create table tb1\G
*************************** 1. row ***************************
       Table: tb1
Create Table: CREATE TABLE `tb1` (
  `c1` int(11) DEFAULT NULL,                 -- c1列我们没有设置因此没有‘not null’的约束,并且默认值为null
  `c2` int(11) NOT NULL                      -- c2列设置不能为空,没有默认值
) ENGINE=InnoDB DEFAULT CHARSET=utf8
-- 再次声明一下:我们输入的sql语句会先提交到本地的mysqld服务器,经过服务器优化后再执行,
--   因此现在我们看到的是经过服务器优化后的且实际执行的sql语句,
--   这些默认值以及engine,charset都是mysqld自动为我们写入的
1 row in set (0.00 sec)

mysql> desc tb1;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |          -- 我们查看第三列属性:Null表示是否可以为null
+-------+---------+------+-----+---------+-------+          -- c1列为yes,c2列为no
| c1    | int(11) | YES  |     | NULL    |       |
| c2    | int(11) | NO   |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> insert into tb1(c1, c2) values(1,1);              -- 插入c1,c2
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb1(c1, c2) values(2,2);              -- 插入c1,c2
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb1(c2) values(3);                    -- 只插入c2
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb1(c1) values(4);                    -- 只插入c1
ERROR 1364 (HY000): Field 'c2' doesn't have a default value   -- 出错,提示没有默认值,我们先不看后面再说

mysql> insert into tb1(c1, c2) values(4,null);          -- c1插入4,c2插入null
ERROR 1048 (23000): Column 'c2' cannot be null          -- 出错,提示c2不能为空

mysql> insert into tb1(c1, c2) values(5,null);          -- c1插入5,c2插入null
ERROR 1048 (23000): Column 'c2' cannot be null          -- 出错,提示c2不能为空

mysql> select * from tb1;
+------+----+
| c1   | c2 |
+------+----+
|    1 |  1 |
|    2 |  2 |
| NULL |  3 |             -- 只插入c2,c1默认为空->插入成功
+------+----+             -- 插入c1为4,c2为null->插入失败,因为c2设置为not null也就是不可以为空
3 rows in set (0.00 sec)

结论:设置为not null的列,必须给他设置数据。
站在正常的业务逻辑中:
如果班级没有名字,你不知道你在哪个班级
如果教室名字可以为空,就不知道在哪上课
所以我们在设计数据库表的时候,一定要在表中进行限制,满足上面条件的数据就不能插入到表中。这就是“约束”。

什么叫null不会参与运算:这里的null和C语言的null不同,C语言的NULL与数字0和字符’\0’在数值上等价–都0,但是MySQL中null就是什么都没有,什么都没有当然就不能进行操作;
区分 null 与 ‘’(两个单引号), null是什么都没有, ''是一个字符串,但是这个字符串中什么都没写。
示例:

mysql> select 1 + 1;
+-------+
| 1 + 1 |
+-------+
|     2 |            -- 运算结果
+-------+
1 row in set (0.00 sec)

mysql> select 99 * 10;
+---------+
| 99 * 10 |
+---------+
|     990 |            -- 运算结果
+---------+
1 row in set (0.00 sec)

mysql> select 1 * null;
+----------+
| 1 + null |
+----------+
|     NULL |            -- null不参与运算
+----------+
1 row in set (0.00 sec)



2、默认值

所谓默认值就是用户自己设置了就用用户设置的,用户没有设置就这个提前设置好的。

mysql> use db1;                              
Database changed
mysql> create table person(                     -- 场景:注册一个社交网站账号
    -> name varchar(20) default 'momo',           -- 名字默认为 'momo'
    -> gander char(2) default '未知',              -- 性别默认为 '未知'
    -> age int                                    -- 年龄没有写默认值
    -> );
Query OK, 0 rows affected (0.04 sec)

mysql> show create table person\G
*************************** 1. row ***************************
       Table: person
Create Table: CREATE TABLE `person` (
  `name` varchar(20) DEFAULT 'momo',
  `gander` char(2) DEFAULT '未知',
  `age` int(11) DEFAULT NULL            -- mysql服务器自行配置:年龄默认为null
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> insert into person(name, gander, age) values('宁姚', '女', 20);  -- 用户写了就用用户的
Query OK, 1 row affected (0.01 sec)

mysql> insert into person(name, gander) values('陈平安', '男');        -- 用户没写就使用默认
Query OK, 1 row affected (0.01 sec)

mysql> insert into person(name) values('老道士');        -- 用户没写就使用默认
Query OK, 1 row affected (0.00 sec)

mysql> insert into person(age) values(1);        -- 用户没写就使用默认
Query OK, 1 row affected (0.00 sec)

mysql> select * from person;
+-----------+--------+------+
| name      | gander | age  |
+-----------+--------+------+
| 宁姚      ||   20 |
| 陈平安    || NULL |
| 老道士    | 未知   | NULL |
| momo      | 未知   |    1 |
+-----------+--------+------+
4 rows in set (0.00 sec)

空属性与默认值

mysql> create table tb1(
    -> c1 int,
    -> c2 int not null,
    -> c3 int not null default 0
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> show create table tb1\G
*************************** 1. row ***************************
       Table: tb1
Create Table: CREATE TABLE `tb1` (
  `c1` int(11) DEFAULT NULL,
  `c2` int(11) NOT NULL,
  `c3` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> desc tb1;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| c1    | int(11) | YES  |     | NULL    |       |
| c2    | int(11) | NO   |     | NULL    |       |
| c3    | int(11) | NO   |     | 0       |       |
+-------+---------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> insert into tb1(c1, c2, c3) values(1, 1, 1);         -- 插入成功
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb1(c1, c2, c3) values(2, 2, 2);         -- 插入成功
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb1(c2, c3) values(2, 2);         -- 插入成功,c1不给值,使用默认值:null
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb1(c1, c3) values(2, 2);        -- 插入失败,c2不给值,由于创建时声明为not null,服务器就没有给默认值null,因此c2是没有值的
ERROR 1364 (HY000): Field 'c2' doesn't have a default value

mysql> insert into tb1(c1, c2, c3) values(2, null, 2);  -- 插入失败,c2有not null约束,所以不能插入null
ERROR 1048 (23000): Column 'c2' cannot be null

mysql> insert into tb1(c1, c2) values(2, 2);           -- 插入成功,c3不给值,使用默认值 0
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb1(c1, c2, c3) values(2, 2, null);  -- 插入失败,c3有not null约束,不能插入null
ERROR 1048 (23000): Column 'c3' cannot be null
mysql> select * from tb1;
+------+----+----+
| c1   | c2 | c3 |
+------+----+----+
|    1 |  1 |  1 |
|    2 |  2 |  2 |
| NULL |  2 |  2 |
|    2 |  2 |  0 |
+------+----+----+
4 rows in set (0.00 sec)

总结:

  • 没有设置not null和default约束:mysql服务器会自行添加default为null;
  • 设置了not null约束:mysqld不会自行添加default为null,并且用户插入数据时不允许插入null;
  • 用户在插入数据时可以插入null;
  • default在用户没有插入该列属性时使用;
  • 既设置了not null又设置了default:可以不插入数据,但是插入数据时一定不能插入 null;
  • null和default并不冲突,而是相互补充的,当然一般不会同时设置。


3、列描述

comment是对该列数据的简单描述,类似于C语言的注释,是给程序员看的但并不起到实际约束作用。

mysql> create table tb2(
    -> name varchar(20) comment '这是用户姓名',              -- 列描述 “这是用户姓名”
    -> age tinyint unsigned comment '这是用户年龄,要在200以内'       -- 列描述 “这是用户年龄,要在200以内”
    -> );
Query OK, 0 rows affected (0.04 sec)

mysql> show create table tb2\G
*************************** 1. row ***************************
       Table: tb2
Create Table: CREATE TABLE `tb2` (
  `name` varchar(20) DEFAULT NULL COMMENT '这是用户姓名',              -- 列描述 “这是用户姓名”
  `age` tinyint(3) unsigned DEFAULT NULL COMMENT '这是用户年龄,要在200以内'       -- 列描述 “这是用户年龄,要在200以内”
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> insert into tb2(name, age) values('张三', 20);
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb2(name, age) values('李四', 0);
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb2(name, age) values('王五', 200);
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb2(name, age) values('赵六', 201);
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb2(name, age) values('赵六', 255);
Query OK, 1 row affected (0.00 sec)

mysql> select * from tb2;
+--------+------+
| name   | age  |
+--------+------+
| 张三   |   20 |
| 李四   |    0 |
| 王五   |  200 |
| 赵六   |  201 |        -- 超过200的数据照样可以插入成功
| 赵六   |  255 |
+--------+------+
5 rows in set (0.00 sec)

mysql> desc tb2;                -- 在表结构中没有comment字段
+-------+---------------------+------+-----+---------+-------+
| Field | Type                | Null | Key | Default | Extra |
+-------+---------------------+------+-----+---------+-------+
| name  | varchar(20)         | YES  |     | NULL    |       |
| age   | tinyint(3) unsigned | YES  |     | NULL    |       |
+-------+---------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)



4、zerofill

我们在前面讲了mysql的各种数据类型,以及使用各种数据类型的注意事项,比如float(4,2) 表示最多四位且小数占两位;char(2)表示最多两个字符,那就会有很多疑惑:int后面的数字表示什么意思?

mysql> create table tb3(
    -> c1 int,
    -> c2 int
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> show create table tb3\G
*************************** 1. row ***************************
       Table: tb3
Create Table: CREATE TABLE `tb3` (
  `c1` int(11) DEFAULT NULL,               -- 11的含义是什么?
  `c2` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

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

mysql> insert into tb3(c1, c2) values(10, 10);
Query OK, 1 row affected (0.01 sec)

mysql> select * from tb3;
+------+------+
| c1   | c2   |
+------+------+
|    1 |    1 |
|   10 |   10 |
+------+------+
2 rows in set (0.00 sec)

mysql> alter table tb3 modify c2 int zerofill; -- 修改c2列,添加zerofill约束
Query OK, 2 rows affected (0.09 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from tb3;
+------+------------+
| c1   | c2         |
+------+------------+
|    1 | 0000000001 |      -- 观察发现:1前面有9个0,带上1一共10位
|   10 | 0000000010 |      -- 观察发现:10前面有8个0,带上10一共10位
+------+------------+
2 rows in set (0.00 sec)

mysql> insert into tb3(c1, c2) values(100, 100);
Query OK, 1 row affected (0.01 sec)

mysql> select * from tb3;
+------+------------+
| c1   | c2         |
+------+------------+
|    1 | 0000000001 |
|   10 | 0000000010 |
|  100 | 0000000100 |   -- 现在我们发现:int(10)表示的是显示位数,那是不是和float的第一个数字一样限制最大显示位数?
+------+------------+
3 rows in set (0.00 sec)

mysql> alter table tb3 modify c2 int(3) zerofill;    -- 我们修改c2为int(3),继续测试
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> select * from tb3;
+------+------+
| c1   | c2   |
+------+------+
|    1 |  001 |     
|   10 |  010 |           -- 显示位数都为3为,不足3位的,高位补0
|  100 |  100 |
+------+------+
3 rows in set (0.00 sec)

mysql> insert into tb3(c1, c2) values(1000, 1000);       -- 我们发现插入成功了
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb3(c1, c2) values(10000, 10000);      -- 我们发现插入成功了
Query OK, 1 row affected (0.00 sec)

mysql> 
mysql> select * from tb3;
+-------+-------+
| c1    | c2    |
+-------+-------+
|     1 |   001 |
|    10 |   010 |
|   100 |   100 |
|  1000 |  1000 |               -- 查看也没有问题
| 10000 | 10000 | 
+-------+-------+
5 rows in set (0.00 sec)

结论:整形数据类型后面的数字表示的是最小显示位数,如果加上zerofill约束不足最小显示位数的情况下会在高位补0
思考:那么为什么默认是10,为什么不是9,不是11?
回答:int类型大小为-2^31 ~ 2^31 -1 ,及2*10^9 (21亿多),因此10位刚好足够显示int的最大值。

补充:001 与 1是否一样

mysql> select c2 ,hex(c2) from tb3 where c2 = 1;      -- 查找c2=1的记录, hex()使用10进制形式显示
+------+---------+
| c2   | hex(c2) |
+------+---------+
|  001 | 1       |
+------+---------+
1 row in set (0.00 sec)

结论:数字001还是1,只是显示形式不同而已。



5、主键约束

语法:

primary key(列名);

主键:primary key用来唯一的约束该字段里面的数据,不能重复,不能为空,一张表中最多只能有一个主键;主键所在的列通常是整数类型。

案例:

mysql> create table tb4(
    -> id int primary key,            
    -> name varchar(20) not null,
    -> phone varchar(11)
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> desc tb4;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | NULL    |       |  -- key属性为PRI就是主键的意思primary, 主键的Null属性自动设置为no
| name  | varchar(20) | NO   |     | NULL    |       |   
| phone | varchar(11) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

主键不能重复不能为空:

mysql> insert into tb4(id, name, phone) values(001, '张三', '12345678');      -- 插入成功
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb4(id, name, phone) values(002, '李四', '23456789');      -- 插入成功
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb4(id, name, phone) values(002, '李四', '23456789');      -- 插入失败, 主键重复
ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'

mysql> insert into tb4(id, name, phone) values(002, '王五', '23456789');      -- 插入失败, 主键重复
ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'

mysql> insert into tb4(id, name, phone) values(003, '李四', '23456789');      -- 插入成功,主键不重复即可,其他列没有限制
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb4(id, name, phone) values(null, '李四', '23456789');      -- 插入失败, 主键不能为空
ERROR 1048 (23000): Column 'id' cannot be null

mysql> select * from tb4;
+----+--------+----------+
| id | name   | phone    |
+----+--------+----------+
|  1 | 张三   | 12345678 |
|  2 | 李四   | 23456789 |
|  3 | 李四   | 23456789 |
+----+--------+----------+
4 rows in set (0.00 sec)


表已经创建好后再追加主键

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

mysql> alter table tb5 modify id int primary key;        -- 设置主键(前提:id列数据无重复)
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0

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


删除主键

mysql> alter table tb5 drop primary key;
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc tb5;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   |     | NULL    |       |   -- 主键约束去掉了,null约束还在
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

复合主键:一个表只能有一个主键,一个主键可以有多个列。

								-- 情景:学生选课
mysql> create table tb6(
    -> id int,
    -> name varchar(20),
    -> class_id int,     -- 课程id
    -> primary key(id, class_id)    -- id和class_id 共同构成主键
    -> );
Query OK, 0 rows affected (0.04 sec)

mysql> desc tb6;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id       | int(11)     | NO   | PRI | NULL    |       |   -- id和class_id都是PRI
| name     | varchar(20) | YES  |     | NULL    |       |
| class_id | int(11)     | NO   | PRI | NULL    |       |
+----------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> insert into tb6(id, name, class_id) values(001, '张三', 601);
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb6(id, name, class_id) values(001, '张三', 602);
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb6(id, name, class_id) values(002, '李四', 602);
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb6(id, name, class_id) values(002, '李四', 602);  -- 主键重复,主键为:2-602
ERROR 1062 (23000): Duplicate entry '2-602' for key 'PRIMARY'
mysql> select * from tb6;
+----+--------+----------+
| id | name   | class_id |
+----+--------+----------+
|  1 | 张三   |      601 |
|  1 | 张三   |      602 |     -- 一个学生可以选多门课  
|  2 | 李四   |      602 |     -- 一门课可以被多个学生选
+----+--------+----------+   -- 一个学生不能重复选一门课
3 rows in set (0.00 sec)



6、自增长

auto_increment:当对应的字段,不给值,会自动的被系统触发,系统会从当前字段中已经有的最大值+1操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。

自增长的特点:

  • 任何一个字段要做自增长,前提是本身是一个索引(key一栏有值)
  • 自增长字段必须是整数
  • 一张表最多只能有一个自增长
mysql> create table tb7(
    -> id int primary key auto_increment,   -- id:主键且自增长
    -> name varchar(20) not null
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> show create table tb7\G
*************************** 1. row ***************************
       Table: tb7
Create Table: CREATE TABLE `tb7` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> desc tb7;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |  -- 额外配置:自增长
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> insert into tb7(id, name) values(1, '张三'); 
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb7(id, name) values(2, '李四');
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb7(name) values('李四');              -- 不插入id,使用它的自增长
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb7(name) values('李四');
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb7(name) values('李四');
Query OK, 1 row affected (0.00 sec)

mysql> select * from tb7;
+----+--------+
| id | name   |
+----+--------+
|  1 | 张三   |
|  2 | 李四   |
|  3 | 李四   |
|  4 | 李四   |
|  5 | 李四   |
+----+--------+
5 rows in set (0.00 sec)

mysql> select last_insert_id();  -- 查看最后一次插入的自增长列的值:5
+------------------+
| last_insert_id() |
+------------------+
|                5 |
+------------------+
1 row in set (0.00 sec)

mysql> show create table tb7\G
*************************** 1. row ***************************
       Table: tb7
Create Table: CREATE TABLE `tb7` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8  -- 中间第二个属性表示下一次插入时自增长列的值:6
1 row in set (0.00 sec)


验证auto_increment自增长策略

mysql> insert into tb7(id, name) values(105, '张飞');
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb7(name) values('关羽');
Query OK, 1 row affected (0.01 sec)

mysql> select * from tb7;
+-----+--------+
| id  | name   |
+-----+--------+
|   1 | 张三   |
|   2 | 李四   |
|   3 | 李四   |
|   4 | 李四   |
|   5 | 李四   |
| 105 | 张飞   |
| 106 | 关羽   |            -- 接着最大值105往后增长
+-----+--------+
7 rows in set (0.00 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|              106 |
+------------------+
1 row in set (0.00 sec)

mysql> show create table tb7\G
*************************** 1. row ***************************
       Table: tb7
Create Table: CREATE TABLE `tb7` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

验证了前面说的:系统会从当前字段中已经有的最大值+1操作,得到一个新的不同的值。



7、唯一键约束

语法:

unique key(列名);

一张表中有往往有很多字段需要唯一性,数据不能重复,但是一张表中只能有一个主键:唯一键就可以解决表中有多个字段需要唯一性约束的问题。
唯一键的本质和主键差不多,唯一键允许为空,而且可以多个为空,空字段不做唯一性比较。
关于唯一键和主键的区别:
我们可以简单理解成,主键更多的是标识唯一性的。而唯一键更多的是保证在业务上,不要和别的信息出现重复(可为空)

场景:
学校统计各个学生的学号、姓名、电话号和家庭地址信息;
姓名和家庭地址可以出现重复,但是学号和电话号肯定是各不相同的,
那么我们可以选择学号或者电话号作为主键(在这里我们选一列作为主键,不是不能选两列只是在这里选两列逻辑不通)
这里学号作为主键更合适所以我们选择学号,
那么电话号怎么办,如果我们不对它进行限制,因为记录时的疏忽导致好多位同学电话号相同,这显然是不合理的。

mysql> create table stu(
    -> id int primary key,
    -> name varchar(20) not null,
    -> phone varchar(11) unique,      -- 设置唯一键约束,也可以写 unique key
    -> address varchar(32)
    -> );
Query OK, 0 rows affected (0.04 sec)

mysql> show create table stu\G
*************************** 1. row ***************************
       Table: stu
Create Table: CREATE TABLE `stu` (
  `id` int(11) NOT NULL,
  `name` varchar(20) NOT NULL,
  `phone` varchar(11) DEFAULT NULL,
  `address` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `phone` (`phone`)           -- 唯一键约束
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> desc stu;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id      | int(11)     | NO   | PRI | NULL    |       |     -- 主键约束,默认不能为空
| name    | varchar(20) | NO   |     | NULL    |       |
| phone   | varchar(11) | YES  | UNI | NULL    |       |     -- 唯一键约束UNI:unique,默认可以为null
| address | varchar(32) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

mysql> insert into stu(id, name, phone, address) values(1, '张三', '12323432', '骊珠洞天');
Query OK, 1 row affected (0.01 sec)

mysql> insert into stu(id, name, phone, address) values(2, '李四', '12323789', '骊珠洞天');
Query OK, 1 row affected (0.01 sec)

mysql> insert into stu(id, name, phone, address) values(3, '李四', '12323789', '骊珠洞天');  -- phone重复
ERROR 1062 (23000): Duplicate entry '12323789' for key 'phone'

mysql> insert into stu(id, name, phone, address) values(3, '王五', '12323789', '骊珠洞天');   -- phone重复
ERROR 1062 (23000): Duplicate entry '12323789' for key 'phone'

mysql> insert into stu(id, name, phone, address) values(3, '王五', '12323888', '骊珠洞天');  -- 插入成功
Query OK, 1 row affected (0.00 sec)

mysql> insert into stu(id, name, phone, address) values(4, '王五', null, '骊珠洞天');     -- 插入成功,电话设为null
Query OK, 1 row affected (0.00 sec)

mysql> insert into stu(id, name, address) values(5, '赵六', '骊珠洞天');           -- 插入成功,电话使用默认值(null)
Query OK, 1 row affected (0.01 sec)

mysql> select * from stu;
+----+--------+----------+--------------+
| id | name   | phone    | address      |
+----+--------+----------+--------------+
|  1 | 张三   | 12323432 | 骊珠洞天     |
|  2 | 李四   | 12323789 | 骊珠洞天     |
|  3 | 王五   | 12323888 | 骊珠洞天     |
|  4 | 王五   | NULL     | 骊珠洞天     |
|  5 | 赵六   | NULL     | 骊珠洞天     |
+----+--------+----------+--------------+
5 rows in set (0.00 sec)



8、外键约束

外键用于定义主表和从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为null。
语法:

foreign key (字段名) references 主表()

设置场景:学生选课,如果每个学生选课时都记录他的选课id,选课的名字和教课老师,那么在这张表中会有大量重复的数据,既让表变得复杂也会增加存储的代价,因此我们可以将两张表拆开存储,通过课程id关联在一起。
在这里插入图片描述

mysql> create table stu2(
    -> id int,
    -> name varchar(20) not null,
    -> course_id int,
    -> primary key(id, course_id)
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> create table cou2(
    -> id int primary key,
    -> name varchar(20) not null,
    -> teacher varchar(20) not null
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> insert into cou2 values(601, '计网', 'teacher李');
Query OK, 1 row affected (0.01 sec)

mysql> insert into cou2 values(602, '计组', 'teacher王');
Query OK, 1 row affected (0.01 sec)

mysql> insert into stu2 values(1, '张三', 601);
Query OK, 1 row affected (0.01 sec)

mysql> insert into stu2 values(1, '张三', 602);
Query OK, 1 row affected (0.01 sec)

mysql> insert into stu2 values(2, '李四', 602);
Query OK, 1 row affected (0.01 sec)

mysql> insert into stu2 values(2, '李四', 603);        -- ?? 
Query OK, 1 row affected (0.00 sec)

mysql> select * from stu2;
+----+--------+-----------+
| id | name   | course_id |
+----+--------+-----------+
|  1 | 张三   |       601 |
|  1 | 张三   |       602 |
|  2 | 李四   |       602 |
|  2 | 李四   |       603 |           -- 李四同学选了603课程,但是我们并没有这个课,这就不符合现实情况
+----+--------+-----------+  -- (不过在逻辑上并没有问题,因为我们前面说:靠课程id联系两张表,只是口头约定,数据库并不知道)
4 rows in set (0.00 sec)

mysql> select * from cou2;
+-----+--------+------------+
| id  | name   | teacher    |
+-----+--------+------------+
| 601 | 计网   | teacher李  |
| 602 | 计组   | teacher王  |
+-----+--------+------------+
2 rows in set (0.00 sec)

mysql> delete from cou2 where id = 602;       
Query OK, 1 row affected (0.00 sec)          -- 同样不合理,602课程还有学生,学校不能直接把这门课删掉

mysql> select * from cou2;
+-----+--------+------------+
| id  | name   | teacher    |
+-----+--------+------------+      
| 601 | 计网   | teacher李  |
+-----+--------+------------+
1 row in set (0.00 sec)

添加外键:

mysql> create table cou3(
    -> id int primary key,
    -> name varchar(20) not null,
    -> teacher varchar(20) not null
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> create table stu3(
    -> id int,
    -> name varchar(20) not null,
    -> course_id int,
    -> primary key(id, course_id),      
    -> foreign key(course_id) references cou3(id)  -- 外键:学生表的课程id与课程表的id相互关联
    -> );
Query OK, 0 rows affected (0.03 sec)

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

mysql> desc stu3;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | NO   | PRI | NULL    |       |
| name      | varchar(20) | NO   |     | NULL    |       |
| course_id | int(11)     | NO   | PRI | NULL    |       |  -- course_id既是主键又是外键,外键属性没有显示出来
+-----------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
 
mysql> alter table stu3 drop primary key;         -- 删除学生表的主键
Query OK, 0 rows affected (0.08 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc stu3;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| id        | int(11)     | NO   |     | NULL    |       |
| name      | varchar(20) | NO   |     | NULL    |       |
| course_id | int(11)     | NO   | MUL | NULL    |       |  -- 外键约束标识(之一):mul
+-----------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> alter table stu3 add primary key(id, course_id);   -- 学生表添加主键约束
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0


mysql> insert into cou3 values(601, '计网', 'teacher张');
Query OK, 1 row affected (0.00 sec)

mysql> insert into cou3 values(602, '计组', 'teacher王');
Query OK, 1 row affected (0.01 sec)

mysql> select * from cou3;
+-----+--------+------------+
| id  | name   | teacher    |
+-----+--------+------------+
| 601 | 计网   | teacher张  |
| 602 | 计组   | teacher王  |
+-----+--------+------------+
2 rows in set (0.00 sec)

mysql> insert into stu3 values(1, '张三', 601);
Query OK, 1 row affected (0.01 sec)

mysql> insert into stu3 values(1, '张三', 602);
Query OK, 1 row affected (0.00 sec)

mysql> insert into stu3 values(2, '李四', 602);
Query OK, 1 row affected (0.00 sec)

mysql> insert into stu3 values(2, '李四', 603);   -- 不允许插入,外键约束错误:因为课程表里没有603
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`db1`.`stu3`, CONSTRAINT `stu3_ibfk_1` FOREIGN KEY (`course_id`) REFERENCES `cou3` (`id`))

mysql> insert into stu3 values(2, '李四', 605);   -- 不允许插入
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`db1`.`stu3`, CONSTRAINT `stu3_ibfk_1` FOREIGN KEY (`course_id`) REFERENCES `cou3` (`id`))

mysql> delete from cou3 where id = 602;   -- 不允许删除,因为602课程已经有学生报名(在数据库看来:学生表中仍有记录关联着602这个字段)
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`db1`.`stu3`, CONSTRAINT `stu3_ibfk_1` FOREIGN KEY (`course_id`) REFERENCES `cou3` (`id`))
mysql> select * from stu3;
+----+--------+-----------+
| id | name   | course_id |
+----+--------+-----------+
|  1 | 张三   |       601 |
|  1 | 张三   |       602 |
|  2 | 李四   |       602 |
+----+--------+-----------+
3 rows in set (0.00 sec)

mysql> select * from cou3;
+-----+--------+------------+
| id  | name   | teacher    |
+-----+--------+------------+
| 601 | 计网   | teacher张  |
| 602 | 计组   | teacher王  |
+-----+--------+------------+
2 rows in set (0.00 sec)

此时我们的两张表才算是真正地关联了起来,对两张表的操作都需要受到外键的约束,通过外键来检查我们操作的合法性。

补充:

mysql> delete from stu3 where course_id = 602; -- 602班没有学生
Query OK, 2 rows affected (0.00 sec)

mysql> select * from stu3;
+----+--------+-----------+
| id | name   | course_id |
+----+--------+-----------+
|  1 | 张三   |       601 |
+----+--------+-----------+
1 row in set (0.00 sec)

mysql> delete from cou3 where id = 602; -- 就可以撤销这个班级
Query OK, 1 row affected (0.01 sec)

mysql> select * from cou3;
+-----+--------+------------+
| id  | name   | teacher    |
+-----+--------+------------+
| 601 | 计网   | teacher张  |
+-----+--------+------------+
1 row in set (0.00 sec)





相关推荐

最近更新

  1. TCP协议是安全的吗?

    2023-12-13 16:06:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-13 16:06:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-13 16:06:04       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-13 16:06:04       20 阅读

热门阅读

  1. 卷积的计算 - numpy的实现 2

    2023-12-13 16:06:04       29 阅读
  2. jquery实现Ajax请求

    2023-12-13 16:06:04       37 阅读
  3. YOLOv8分割训练及分割半自动标注

    2023-12-13 16:06:04       38 阅读
  4. 杨辉三角算法(leetcode第119题)

    2023-12-13 16:06:04       41 阅读
  5. C语言 编程题

    2023-12-13 16:06:04       40 阅读
  6. 通过compileall库将python文件编译为pyc文件

    2023-12-13 16:06:04       37 阅读
  7. Git常用命令大全

    2023-12-13 16:06:04       37 阅读