在数据库的实际操作中难免会有一些操作不规范的地方,比方说在一张学生成绩表中,成绩的最高值为100,那么如果在这张表中如果出现了超过100分的成绩那显然是不合理的,因次需要添加约束来避免这些错误
表的约束
真正约束字段的是数据类型(比方说int类型只能存储整数),但是数据类型约束很单一,需要一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性。比如有一个字段是tel,要求是唯一的。
表的约束很多,本篇主要介绍:null/ not null, default, comment, zerofill, primary key, auto_increment, unique key
空属性
- 两个值:null(默认的)和not null
- 数据库默认字段基本都是字段可以为空,但是实际开发时,尽可能保证字段不为空,因为数据为空没办法参与运算
mysql> select null;
+------+
| NULL |
+------+
| NULL |
+------+
1 row in set (0.00 sec)
mysql> select 1+null;
+--------+
| 1+null |
+--------+
| NULL |
+--------+
1 row in set (0.00 sec)
案例:
创建一个班级表,包含班级名和班级所在的教室,班级和教室名字不能为空。
mysql> create table myclass(
-> class_name varchar(20) not null,
-> class_room varchar(10) not null
-> );
Query OK, 0 rows affected (0.05 sec)
mysql> desc myclass;
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| class_name | varchar(20) | NO | | NULL | |
| class_room | varchar(10) | NO | | NULL | |
+------------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql> insert into myclass(class_name,class_room) values('class1', 'a101');
Query OK, 1 row affected (0.00 sec)
mysql> insert into myclass(class_name,class_room) values('class2');
ERROR 1136 (21S01): Column count doesn't match value count at row 1
默认值
mysql> create table tt13(
-> name varchar(20) not null,
-> age tinyint unsigned default 0,
-> sex char(2) default '男'
-> );
mysql> desc tt13;
+-------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------------+------+-----+---------+-------+
| name | varchar(20) | NO | | NULL | |
| age | tinyint(3) unsigned | YES | | 0 | |
| sex | char(2) | YES | | 男 | |
+-------+---------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> insert into tt13(name) values('jack');
Query OK, 1 row affected (0.01 sec)
mysql> select * from tt13;
+------+------+------+
| name | age | sex |
+------+------+------+
| jack | 0 | 男 |
+------+------+------+
1 row in set (0.00 sec)
列描述
列描述:comment,没有实际含义,专门用来描述字段,会根据表创建语句保存,用来提醒程序员或DBA
mysql> create table tt14( name varchar(20) not null comment'姓名', age tinyint unsigned default 0 comment '年龄', sex char(2) default '男' comment '姓别' );
Query OK, 0 rows affected (0.07 sec)
通过desc看不到注释信息,但可以通过show看到:
mysql> show create table tt14\G
*************************** 1. row ***************************
Table: tt14
Create Table: CREATE TABLE `tt14` (
`name` varchar(20) NOT NULL COMMENT '姓名',
`age` tinyint(3) unsigned DEFAULT '0' COMMENT '年龄',
`sex` char(2) DEFAULT '男' COMMENT '姓别'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
zerofill
在创建表时,有时会看见数字类型后面会加一个数字比方说下面的int(10)
,这代表什么意思?
mysql> create table tt15(
-> a int(10) unsigned,
-> b int(10) unsigned
-> );
Query OK, 0 rows affected (0.06 sec)
如果没有zerofill
这个属性,括号内的数字时毫无意义的
// 没有zerofill属性
mysql> insert into tt15(a,b) values(1,2);
Query OK, 1 row affected (0.04 sec)
mysql> select * from tt15;
+------+------+
| a | b |
+------+------+
| 1 | 2 |
+------+------+
1 row in set (0.00 sec)
//添加zerofill属性
mysql> alter table tt15 change a a int(5) unsigned zerofill;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table tt15\G
*************************** 1. row ***************************
Table: tt15
Create Table: CREATE TABLE `tt15` (
`a` int(5) unsigned zerofill DEFAULT NULL,
`b` int(10) unsigned DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> select * from tt15;
+-------+------+
| a | b |
+-------+------+
| 00001 | 2 |
+-------+------+
1 row in set (0.00 sec)
这次可以看到a的值由1变成00001,这就是zerofill属性的作用,如果宽度小于设定的宽度,自动填充0,不过这只是显示的结果,在MySQL中实际存储的还是1,可以用hex()
(十六进制转换)函数来证明
mysql> select a, hex(a) from tt15;
+-------+--------+
| a | hex(a) |
+-------+--------+
| 00001 | 1 |
+-------+--------+
1 row in set (0.00 sec)
主键
主键primary key用来约束该字段里的数据不能重复,不能为空,一张表最多只能由一个主键;主键所在的列通常是整数类型。
案例:
- 创建表的时候直接在字段上指定主键
mysql> create table tt16( id int unsigned primary key, name char(20) not null );
Query OK, 0 rows affected (0.04 sec)
mysql> desc tt16;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | PRI | NULL | |
| name | char(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
- 主键约束:主键对应的字段中不能重复,一旦重复,操作失败
mysql> insert into tt16(id,name) values(1,'aaa');
Query OK, 1 row affected (0.01 sec)
mysql> insert into tt16(id,name) values(1,'aaa');
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
- 当表创建好以后但是没有主键的时候,可以再次追加主键
alter table tablename add primary key(coloum);
- 删除主键
mysql> alter table tt16 drop primary key;
Query OK, 1 row affected (0.09 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> desc tt16;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | | NULL | |
| name | char(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
- 复合主键
在创建表的时候,在所有字段之后,使用primary key(主键字段列表)来创建主键,如果有多个字段作为主键,可以使用复合主键
mysql> create table tt17(
-> id int unsigned,
-> course char(10),
-> score tinyint unsigned default 60,
-> primary key(id,course)
-> );
Query OK, 0 rows affected (0.04 sec)
mysql> desc tt17;
+--------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | PRI | NULL | |
| course | char(10) | NO | PRI | NULL | |
| score | tinyint(3) unsigned | YES | | 60 | |
+--------+---------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
#插入数据
mysql> insert into tt17(id,course,score) values(1,'123',60);
Query OK, 1 row affected (0.01 sec)
mysql> insert into tt17(id,course,score) values(1,'123',60);
ERROR 1062 (23000): Duplicate entry '1-123' for key 'PRIMARY'
mysql> insert into tt17(id,course,score) values(2,'123',60);
Query OK, 1 row affected (0.00 sec)
mysql> insert into tt17(id,course,score) values(1,'124',60);
Query OK, 1 row affected (0.01 sec)
从上面的例子可以看出复合主键插入时,只有复合主键中的每一个数据都与另一条数据完全匹配才算主键冲突。
自增长
auto_increment:当对应的字段不给值,会自动被系统触发,系统会用当先字段中已有的最大值做+1操作,得到一个新的值,通常和主键搭配使用,作为逻辑主键。
- 一个字段要做自增长,前提是本身是一个索引(key一栏有值)
- 自增长字段必须是整数
- 一张表最多只能有一个自增长
案例:
mysql> create table tt18( id int unsigned primary key auto_increment, name varchar(10)not null default '' );
Query OK, 0 rows affected (0.04 sec)
mysql> insert into tt18(name) values('a');
Query OK, 1 row affected (0.01 sec)
mysql> insert into tt18(name) values('b');
Query OK, 1 row affected (0.01 sec)
mysql> select * from tt18;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
+----+------+
2 rows in set (0.00 sec)
在插入后获取上次插入的AUTO_INCREMENT的值(批量插入获取的是第一个值)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
索引:
在关系数据库中,索引是一种单独的,物理的对数据库列表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到目标内容。
索引提供指向存储在表的指定列中的数据值的指针,然后根据指定的排序顺序对指针排序。数据库使用索引来找到特定值,然后顺时针找到包含该值的行。这样可以使SQL语句执行得更快,更快速访问数据库表中的特定信息。
关于索引,会在后面的文章更加详细地介绍。
唯一键
一张表中往往有很多字段需要唯一性,数据不能重复,但是一张表中只能有一个主键,不过唯一键就可以解决表中有多个字段需要唯一性约束的问题。
唯一键的本质和主键差不多,唯一键允许为空,而且可以多个为空,空字段不做唯一性比较。
唯一键和主键的区别:
主键更多标识唯一性,而唯一键更多在保证业务,不要和别的信息出现重复,下面来举一个例子:
某学校要做一个学生管理系统,需要保存的数据包括学生的手机号,同时需要一个字段来作为学生的主键。那么我们可以用学生的学号来作为主键吗?学生的手机号可能是会发生变化的,那么当学生的手机号发生变化时,系统的主键也会发生变化,在业务调整时,尽量不要对主键做过多调整。在上面的例子中,不同学生的手机号其实只需要保证唯一性就行了,所以我们可以选择手机号作为唯一键。
那么我们应该选择什么值来作为主键呢?尽量选择与业务无关的数据!也就是说,我们可以在存储学生信息时,新建一列id,只用来作为数据的主键
案例:
mysql> create table stu( id char(10) unique, name varchar(10) );
Query OK, 0 rows affected (0.07 sec)
mysql> insert into stu(id,name) values('01','aaa');
Query OK, 1 row affected (0.01 sec)
mysql> insert into stu(id,name) values('01','bbb'); //不可以插入相同的值
ERROR 1062 (23000): Duplicate entry '01' for key 'id'
mysql> insert into stu(id,name) values(null,'bbb'); //可以为空
Query OK, 1 row affected (0.01 sec)
mysql> select * from stu;
+------+------+
| id | name |
+------+------+
| 01 | aaa |
| NULL | bbb |
+------+------+
2 rows in set (0.00 sec)
外键
外键用于定义主表和从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为null。
foreign key coloum references 主表(coloum);
- 创建主键表
mysql> create table class(
-> id int primary key,
-> name varchar(30) not null
-> );
- 创建从表
mysql> create table stu(
-> id int primary key,
-> name varchar(30) not null,
-> class_id int,
-> foreign key(class_id) references class(id)
-> );
- 正常插入数据
mysql> insert into class value(10, '向日葵班'),(20, '玫瑰班');
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> insert into stu values(1, '野原新之助',10),(2, '阿豹', 20);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
- 插入一个班级号为30 的学生,因为没有这个班级,所以插入不成功
mysql> insert into stu values(3, 'ssj', 30);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`restrain`.`stu`, CONSTRAINT `stu_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))
- 插入班级id为null,比如来了一个学生,目前还没有分配班级
mysql> insert into stu values(3, '风间彻', null);
Query OK, 1 row affected (0.01 sec)
如何理解外键约束
首先,世界上的数据很多都是具有相关性的。
理论上,上面的例子,我们不创建外键约束,就正常建立学生表,班级表,该有的字段也都有,那么在实际使用时会出现什么问题?
可能插入的学生信息中有具体的班级,但是该班级却没有在班级表中。
此时,我们认为两张表在业务上是有相关性的,但是业务上没有建立约束关系,那么就可能出现问题。
因为两张表在业务上是有相关性的,但是在业务上没有建立约束关系,那么就可能出现问题。
解决方案就是通过外键完成的,建立外键的本质其实就是把相关性交给MySQL去审核了,提前告诉MySQL表之间的约束关系,那么当用户插入不符合业务逻辑的时候,MySQL不允许你插入。