目录
[空属性 - null / not null](#空属性 - null / not null)
[默认值 - default](#默认值 - default)
[列描述 - comment](#列描述 - comment)
[等宽输出 - zerofill](#等宽输出 - zerofill)
[主键 - primary key](#主键 - primary key)
[自增长 - auto-increment](#自增长 - auto-increment)
[唯一键 - unique](#唯一键 - unique)
[外键 - foreign key](#外键 - foreign key)
什么是表的约束
- MySQL表的约束(Constraints) ,就是加在表字段上的"规则"或"阀门"。是数据库告诉程序员:"往这个字段里写数据时,必须符合我定的规矩,否则就报错,不让存。"
- 它的核心目的是保证数据的完整性和可预期性,防止脏数据进入你的数据库。
真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合 法性,从业务逻辑角度保证数据的正确性。表的约束很多,这里主要介绍如下几个: null/not null,default, comment, zerofill,primary key,auto_increment,unique key。
空属性 - null / not null
- 两个值:null(默认的)和 not null (不能为空),可以不区分大小写
- 含义:在表中定义列时,加在数据类型后面表示该列是否允许为空。
场景:用户注册表
假设我们要存用户信息,通常手机号和邮箱至少得填一个吧?用户名必须填吧?我们来看约束怎么设计:
CREATE TABLE users (
id INT PRIMARY KEY,
username VARCHAR(20) NOT NULL, -- ① 必须填(不能为空)
phone VARCHAR(11) NULL, -- ② 可以不填(允许为空)
email VARCHAR(50) NULL, -- ③ 可以不填(允许为空)
referral_code VARCHAR(10) -- ④ 啥也不写,默认就是 NULL(允许为空)
);
现在 username 为 NOT NULL,如果在插入数据时,没有插入 username 的信息,或者给 username 插入 NULL,都是不允许的,会被 MySQL 拦截。
默认值 - default
默认值:用户不填,就使用默认值,用户填了,就使用用户的数据
场景:程序员相亲网站的用户注册表
mysql> create table user (
-> name varchar(20) not null,
-> age tinyint unsigned default 18,
-> gender enum('男','女') default '男'
-> );
可以同时设置 not null 和 default
mysql> create table user (
-> name varchar(20) not null,
-> age tinyint unsigned default 18,
-> gender enum('男','女') not null default '男'
-> );
- not null 作用在用户想输入时,约束用户不能输入 NULL
- default 作用在用户不想输入时,使用默认值,如果没有 default 直接报错,用户输入其他列的数据被拦截
如果既没有 not null 又没有 default ,MySQL 会自动添加 default null
mysql> create table user (
-> name varchar(20) not null,
-> age tinyint unsigned default 18,
-> gender enum('男','女') default '男'
-> hobby varchar(100)
-> );
// hobby 自动添加 default null 约束
列描述 - comment
列描述:comment,用来说明列的含义,会根据表创建语句保存,用来给程序员或DBA了解。
mysql> create table user (
-> name varchar(20) not null comment '姓名',
-> age tinyint unsigned default 18 comment '年龄',
-> gender enum('男','女') default '男' comment '性别',
-> hobby varchar(100) comment '兴趣爱好'
-> );
mysql> show create table user \G;
*************************** 1. row ***************************
Table: user
Create Table: CREATE TABLE `user` (
`name` varchar(20) NOT NULL COMMENT '姓名',
`age` tinyint(3) unsigned DEFAULT '18' COMMENT '年龄',
`gender` enum('男','女') DEFAULT '男' COMMENT '性别',
`hobby` varchar(100) DEFAULT NULL COMMENT '兴趣爱好'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
ERROR:
No query specified
等宽输出 - zerofill
ERROR 1113 (42000): A table must have at least 1 column
mysql> create table if not exists t1(
-> a int unsigned not null,
-> b int unsigned not null
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> show create table t1 \G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`a` int(10) unsigned NOT NULL,
`b` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
创建 t1 表之后,使用 show 显示创建 t1 表时所用的指令,发现 `a` int(10) unsigned NOT NULL, 中 int 后面多了一个 (10),它代表什么含义呢?
我们使用 zerofill 约束:
mysql> alter table t1 modify b int unsigned zerofill;
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc t1;
+-------+---------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------------------+------+-----+---------+-------+
| a | int(10) unsigned | NO | | NULL | |
| b | int(10) unsigned zerofill | YES | | NULL | |
+-------+---------------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
接着向 t1 表插入数据,观察现象:
mysql> insert into t1 value(1,1);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 value(100,100);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t1 value(12345,12345);
Query OK, 1 row affected (0.00 sec)
mysql> select * from t1;
+-------+------------+
| a | b |
+-------+------------+
| 1 | 0000000001 |
| 100 | 0000000100 |
| 12345 | 0000012345 |
+-------+------------+
3 rows in set (0.00 sec)
通过比较我们发现,加了 zerofill 约束的 b 列,显示数据的宽度都是 10,原来 zerofill 的作用就是统一显示的宽度的,类似于 printf("%10d",n); 如果数据的位数大于 10,那么数据还是原封不动的显示出来。
我们还发现 10 正好就是 int(10) 的 10,原来 int(10) 的 10 就是数据的最小显示宽度。
mysql> alter table t1 modify b int(4) unsigned zerofill;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> insert into t1 value(1,1);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t1 value(11,11);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t1 value(111,111);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 value(1111,1111);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 value(11111,11111);
Query OK, 1 row affected (0.00 sec)
mysql> select * from t1;
+-------+-------+
| a | b |
+-------+-------+
| 1 | 0001 |
| 100 | 0100 |
| 12345 | 12345 |
| 1 | 0001 |
| 11 | 0011 |
| 111 | 0111 |
| 1111 | 1111 |
| 11111 | 11111 |
+-------+-------+
8 rows in set (0.00 sec)
主键 - primary key
主键:primary key 用来约束该列的数据,不能重复,不能为空,一张表中最多只能有一个主键(一张表中主键可以添加到多个列,即复合主键)。主键所在的列通常是整数类型。
mysql> create table students (
-> id int unsigned primary key comment '学号不能为空,不能重复',
-> name varchar(20) not null
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> desc students;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | PRI | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql> show create table students \G
*************************** 1. row ***************************
Table: students
Create Table: CREATE TABLE `students` (
`id` int(10) unsigned NOT NULL COMMENT '学号不能为空,不能重复',
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
可以看到,添加 primary key 的列,MySQL 自动添加 not null。primary key(列名) 也是一种添加主键的方式。
主键可以去掉:
mysql> alter table students drop primary key;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
去掉后还可以加回来(列存在时随时都可以添加主键):
mysql> alter table students add primary key(id);
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
但是要保证 id 这一列没有 NULL 和重复,否则会添加失败。这启示我们要在建表时就考虑好那个列要添加主键。
复合主键例子:大学生选课
在创建表的时候,在所有列之后,可以使用 primary key(列名1,列名2,列名3 ...)来创建复合主键。
mysql> create table pick_cource(
-> id int unsigned,
-> course char(10) comment '课程代码',
-> score tinyint unsigned default 60 comment '成绩',
-> primary key(id, course) -- id和course为复合主键
-> );
Query OK, 0 rows affected (0.01 sec)
同一个学生不能重复选择同一门课程,所以将 id 和 course 作为复合主键。
自增长 - auto-increment
auto_increment:当对应的列,在插入的时候不给值,会自动的被赋值为当前列中的最大值 +1(如果当前列没有值,则为 1) ,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。
自增长的特点:
- 任何一个字段要做自增长,前提是本身是一个索引(key一栏有值)
- 自增长字段必须是整数
- 一张表最多只能有一个自增长
mysql> create table t(
-> id int unsigned primary key auto_increment,
-> name varchar(10) not null default ''
-> );
mysql> insert into t(name) values('a');
mysql> insert into t(name) values('b');
mysql> select * from t;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
+----+------+
注意:如果在上面的基础之上再手动的插入:
mysql> insert into t(id,name) values(1000,'c');
可以插入成功,但下次插入时如果不给 id 值,那么 id 被自动赋值为 1001。
可以自定义自增长从哪个值开始:
mysql> create table t(
-> id int unsigned primary key auto_increment,
-> name varchar(10) not null default ''
-> )auto_increment = 1000;
上面的自增长就从 1000 开始了。
唯一键 - unique
唯一键顾名思义就是防止重复的,先介绍唯一键的使用:
mysql> create table student (
-> id char(10) unique comment '学号,不能重复,但可以为空',
-> name varchar(10)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> insert into student(id, name) values('01', 'aaa');
Query OK, 1 row affected (0.00 sec)
mysql> insert into student(id, name) values('01', 'bbb'); --唯一键约束不能重复
ERROR 1062 (23000): Duplicate entry '01' for key 'id'
mysql> insert into student(id, name) values(null, 'bbb'); -- 但可以为空
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+------+------+
| id | name |
+------+------+
| 01 | aaa |
| NULL | bbb |
+------+------+
从唯一键的使用来看,唯一键好像就是可以为空的主键。唯一键与主键并不冲突,有的列被选择成为主键,并不代表其他列就不需要保证唯一性,如果非主键的列需要保证唯一性,就可以设置为唯一键。一个表可以有多个唯一键。唯一键的本质和主键差不多,唯一键允许为空,而且可以多个为空,空字段不做唯一性比较。
外键 - foreign key
外键用于定义主表 和从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或unique 约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为 null 。
语法:
[constraint '外键名'] foreign key (字段名) references 主表(列)
// constraint:可以给外键起个名字
案例:

对上面的示意图进行设计:
先创建主键表
create table myclass (
id int primary key,
name varchar(30) not null comment'班级名'
);
再创建从表
create table stu (
id int primary key,
name varchar(30) not null comment '学生名',
class_id int,
foreign key (class_id) references myclass(id)
);
正常插入数据
mysql> insert into myclass values(10, '计科5班'),(20, '软工8班');
Query OK, 2 rows affected (0.03 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> insert into stu values(100, '张三', 10),(101, '李四',20);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
插入一个班级号为30的学生,因为没有这个班级,所以插入不成功
mysql> insert into stu values(102, 'wangwu',30);
ERROR 1452 (23000): Cannot add or update a child row:
a foreign key constraint fails (mytest.stu, CONSTRAINT stu_ibfk_1
FOREIGN KEY (class_id) REFERENCES myclass (id))
插入班级id为null,比如来了一个学生,目前还没有分配班级
mysql> insert into stu values(102, 'wangwu', null);
尝试把班级表(主表)的 "计科5班" 删除,删除不成功,因为学生表还有属于 "计科5班" 的学生:
mysql> delete from class where id = 10;
// 删除失败
如果学生表没有属于 "计科5班" 的学生了,此时尝试把班级表(主表)的 "计科5班" 删除,可以删除成功。