目录
为什么要有约束,其实是为了保证数据的合法性,在上一章中我们说到数据类型其实就是一种约束,比如tinyint类型的取值范围是-128-127,如果超过了这个范围,就直接报错,而不是发生c语言中的截断,这样可以倒逼程序员必须插入合法的数据。
1.空属性
- 两个值:null(默认的)和not null(不为空)
- 数据库默认字段基本都是字段为空,但是实际开发时,尽可能保证字段不为空,因为数据为空没办法参与运算。
这里需要区分一下' ' 和 null, mysql中的null表示什么都没有,而' '表示的是空串,其实也是一种数据。
案例:创建一个班级表,包含班级名和班级所在的教室。
站在正常的业务逻辑中:
如果班级没有名字,你不知道你在哪个班级
如果教室名字可以为空,就不知道在哪上课
所以我们在设计数据库表的时候,一定要在表中进行限制,满足上面条件的数据就不能插入到表中。这就是“约束”。
mysql> create table myclass(
-> class_name varchar(20) not null,
-> class_room varchar(10) not null
-> );
Query OK, 0 rows affected (0.02 sec)
我们可以看到在null那一列属性当中,就变成了no,也就是不能为空,此时在插入数据时,class_name和class_room就不能为空了。
mysql> insert into myclass(class_name) values('class1');
ERROR 1364 (HY000): Field 'class_room' doesn't have a default value
mysql> insert into myclass(class_name, class_room) values('class1', '601');
Query OK, 1 row affected (0.00 sec)
2.默认值defalut
默认值:某一种数据会经常性的出现某个具体的值,可以在一开始就指定好,在需要真实数据的时候,用户可以选择性的使用默认值。
mysql> create table t10(
-> name varchar(20) not null,
-> age tinyint unsigned default 18,
-> gender char(1) default '男'
-> );
Query OK, 0 rows affected (0.02 sec)
可以看到default属性那一列也发生了变化。
我们再插入一些数据看看。
mysql> insert into t10(name, age, gender) values('张三', 20, '女');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t10(name, gender) values('李四', '男');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t10(name, age) values('王五', 32);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t10(name) values('王五');
Query OK, 1 row affected (0.00 sec)
如果设置了默认值,并且没有填写,使用的就是默认值。
- 那如果既设置了not null又设置了default呢
mysql> create table t11(
mysql> name varchar(20) not null,
mysql> age tinyint(4),
mysql> gender char(2) not null default '男'
mysql> );
Query OK, 0 rows affected (0.03 sec)
再插入几个数据试试。
mysql> insert into t11(name, age, gender) values('张三', 12, '男');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t11(name, gender) values('张三', '男');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t11(name) values('张三');
Query OK, 1 row affected (0.00 sec)
- not null和default是可以同时存在的。
- 同时存在时,不传也不会出错,因为会使用默认值default。
- not null和defalut一般不需要同时出现,因为default本身有默认值,不会为空
- 如果创建表时,没有填not null和default,则默认,default字段默认为null,null字段为yes
3.列描述
列描述:comment,没有实际含义,专门用来描述字段,会根据表创建语句保存,用来给程序员或DBA来进行了解。
有点像语言中的注释
mysql> create table tt12 (
-> name varchar(20) not null comment '姓名',
-> age tinyint unsigned default 0 comment '年龄',
-> sex char(2) default '男' comment '性别'
-> );
Query OK, 0 rows affected (0.02 sec)
- 使用desc无法观察到
- 需要使用show
4.zerofill
前面在学习数据库的过程当中,我们看到整型类型后面会加一个长度
mysql> show create table tt3\G
***************** 1. row *****************
Table: tt3
Create Table: CREATE TABLE `tt3` (
`a` int(10) unsigned DEFAULT NULL,
`b` int(10) unsigned DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=gbk
1 row in set (0.00 sec)
可以看到int(10),这个代表什么意思呢?整型不是4字节码?这个10又代表什么呢?其实没有zerofill这个属性,括号内的数字是毫无意义的。a和b列就是前面插入的数据,如下:
mysql> insert into tt3 values(1,2);
Query OK, 1 row affected (0.00 sec)
mysql> select * from tt3;
+------+------+
| a | b |
+------+------+
| 1 | 2 |
+------+------+
但是对列添加了zerofill属性后,显示的结果就有所不同了。修改tt3表的属性:
mysql> alter table tt3 change a a int(5) unsigned zerofill;
mysql> show create table tt3\G
*************************** 1. row ***************************
Table: tt3
Create Table: CREATE TABLE `tt3` (
`a` int(5) unsigned zerofill DEFAULT NULL, --具有了zerofill
`b` int(10) unsigned DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=gbk
1 row in set (0.00 sec)
mysql> select * from tt3;
+-------+------+
| a | b |
+-------+------+
| 00001 | 2 |
+-------+------+
这次可以看到a的值由原来的1变成00001,这就是zerofill属性的作用,如果宽度小于设定的宽度(这里设置的是5),自动填充0。要注意的是,这只是最后显示的结果,在MySQL中实际存储的还是1。
如果括号里面设置的是4,但我们实际的数超过了4位,那就会按数据本身显示。
5.主键约束
5.1主键
主键:primary key用来唯一的约束该字段里面的数据,不能重复,不能为空,一张表中最多只能有一个主键;主键所在的列通常是整数类型
mysql> create table tt13 (
-> id int unsigned primary key comment '学号不能为空',
-> name varchar(20) not null
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> desc tt13;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | PRI | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+-------+
key中的PRI表示的就是主键,主键是自带非空约束的。
- 主键约束:主键对应的字段中不能重复,一旦重复,操作失败。
mysql> insert into tt13 values(1, 'aaa');
Query OK, 1 row affected (0.00 sec)
mysql> insert into tt13 values(1, 'aaa');
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
主键的作用就是为了保证记录的唯一性,比如身份证号,手机号,qq号之类无法重复的数据就可以使用主键。
- 当表创建好以后但是没有主键的时候,可以再次追加主键
alter table 表名 add primary key(字段列表)
- 删除主键
alter table 表名 drop primary key;
mysql> alter table tt13 drop primary key;
mysql> desc tt13;
+-------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id | int(10) unsigned | NO | | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+-------+
5.2复合主键
在创建表的时候,在所有字段之后,使用primary key(主键字段列表)来创建主键,如果有多个字段作为主键,可以使用复合主键
mysql> create table t13(
-> a int,
-> b int,
-> c int,
-> primary key(a, b)
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> desc t13;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a | int(11) | NO | PRI | NULL | |
| b | int(11) | NO | PRI | NULL | |
| c | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
3 rows in set (0.00 sec)
前面说过一个表只能有一个主键,这个例子中,是否是存在两个主键?不算是,a和b共同构成了一个主键,所以只有a,b完全相同时才会发生冲突,a相同b不相同的情况下是不会构成冲突的。
mysql> insert into t13 values(10, 20, 30);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t13 values(10, 10, 30);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t13 values(20, 20, 30);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t13 values(10, 20, 30);
ERROR 1062 (23000): Duplicate entry '10-20' for key 'PRIMARY'
6.自增长
auto_increment:当对应的字段,不给值,会自动的被系统触发,系统会从当前字段中已经有的最大值+1操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。
自增长的特点:
- 任何一个字段要做自增长,前提是本身是一个索引(key一栏有值)
- 自增长字段必须是整数
- 一张表最多只能有一个自增长
mysql> create table t14(
-> a int primary key auto_increment,
-> b varchar(10) not null
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> desc t14;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| a | int(11) | NO | PRI | NULL | auto_increment |
| b | varchar(10) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
可以看到Extra属性那里就变成了auto_increnment。
插入几个数据试试。
mysql> insert into t14 values(10, 'hello');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t14 (b) values('hello');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t14 (b) values('hello');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t14 (b) values('hello');
Query OK, 1 row affected (0.01 sec)
mysql> insert into t14 (b) values('hello');
Query OK, 1 row affected (0.01 sec)
当我们第一次显示的给a传值的时候,a就是10,后面我们再没给a值的时候,a就是在上一次插入的基础上继续插入。
这上面的AUTO_INCREMENT就是下次默认插入的值,我们也可以使用last_insert_id()这个函数来获取上一次AUTO_INCREMENT的值。
- 创建表的时候设置起始增长值
mysql> create table t15(
-> a int primary key auto_increment,
-> b varchar(10)
-> )auto_increment=100;
Query OK, 0 rows affected (0.02 sec)
mysql> insert into t15 (b) values ('hello');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t15 (b) values ('hello');
Query OK, 1 row affected (0.01 sec)
mysql> insert into t15 (b) values ('hello');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t15;
+-----+-------+
| a | b |
+-----+-------+
| 100 | hello |
| 101 | hello |
| 102 | hello |
+-----+-------+
3 rows in set (0.00 sec)
7.唯一键
一张表中有往往有很多字段需要唯一性,数据不能重复,但是一张表中只能有一个主键:唯一键就可以解决表中有多个字段需要唯一性约束的问题。
唯一键的本质和主键差不多,唯一键允许为空,而且可以多个为空,空字段不做唯一性比较。
举个例子,在一个公司当中,我们以员工工号作为主键,但是还有很多其他信息是不能重复的,比如员工的身份证号,手机号,那么我们就可以把身份证号和手机号设置为唯一键。
mysql> create table student(
-> id char(20) unique,
-> name varchar(32) not null
-> );
Query OK, 0 rows affected (0.03 sec)
mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | char(20) | YES | UNI | NULL | |
| name | varchar(32) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
key属性那里就变成了UNI。
mysql> insert into student values ('111', '张三');
Query OK, 1 row affected (0.00 sec)
mysql> insert into student values ('222', '张三');
Query OK, 1 row affected (0.00 sec)
mysql> insert into student (name) values ('张三');
Query OK, 1 row affected (0.00 sec)
mysql> insert into student values ('111', '张三');
ERROR 1062 (23000): Duplicate entry '111' for key 'id'
mysql> select * from student;
+------+--------+
| id | name |
+------+--------+
| 111 | 张三 |
| 222 | 张三 |
| NULL | 张三 |
+------+--------+
3 rows in set (0.00 sec)
唯一键是可以为空的,并且插入了相同的值的时候也会报错。
主键和唯一键并不冲突并且是相互补充的。
主键和唯一键的侧重点:
- 主键主要是标志了表中某一行的唯一性。
- 唯一键是保证当前列数据不冲突
8.外键
外键用于定义主表和从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为null。
foreign key (字段名) references 主表(列)
先创建主表。
mysql> create table myclass(
-> id int primary key,
-> name varchar(30) not null comment'班级名'
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc myclass;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(30) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
再创建从表。
mysql> create table stu(
-> id int primary key,
-> name varchar(30) not null comment'学生名',
-> class_id int,
-> foreign key (class_id) references myclass(id)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc stu;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(30) | NO | | NULL | |
| class_id | int(11) | YES | MUL | NULL | |
+----------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> insert into myclass values(10, 'C++大牛班'),(20, 'java大神班');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> insert into stu values(100, '张三', 10),(101, '李四',20);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> insert into stu values(102, 'wangwu',30);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`user5`.`stu`, CONSTRAINT `stu_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `myclass` (`id`))
mysql> insert into stu values(102, 'wangwu', null);
Query OK, 1 row affected (0.00 sec)
第三次插入时,因为主表中的id并不存在30,所以插入失败了
分析:
我们不创建外键约束,就正常建立学生表,以及班级表,该有的字段我们都有。
此时,在实际使用的时候,可能会出现什么问题?
有没有可能插入的学生信息中有具体的班级,但是该班级却没有在班级表中?其实是很有可能的,比如一个程序员粗心了,输入错误导致。因为此时两张表在业务上是有相关性的,但是在业务上没有建立约束关系,那么就可能出现问题。
解决方案就是通过外键完成的。建立外键的本质其实就是把相关性交给mysql去审核了,提前告诉mysql表之间的约束关系,那么当用户插入不符合业务逻辑的数据的时候,mysql不允许你插入。
如果我们想删除主表中的数据呢
mysql> delete from myclass where id = 10;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`user5`.`stu`, CONSTRAINT `stu_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `myclass` (`id`))
直接删除是不行的,因为还有其他从表和他关联着,想要删除,必须把id=10的从表中的数据全部删除。
mysql> select * from stu;
+-----+--------+----------+
| id | name | class_id |
+-----+--------+----------+
| 100 | 张三 | 10 |
| 101 | 李四 | 20 |
| 102 | wangwu | NULL |
+-----+--------+----------+
3 rows in set (0.00 sec)
mysql> delete from stu where class_id = 10;
Query OK, 1 row affected (0.00 sec)
mysql> delete from myclass where id = 10;
Query OK, 1 row affected (0.01 sec)
mysql> select * from myclass;
+----+---------------+
| id | name |
+----+---------------+
| 20 | java大神班 |
+----+---------------+
1 row in set (0.00 sec)