【MySQL】表的约束

目录

什么是表的约束

[空属性 - 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班" 删除,可以删除成功。