> 文章列表 > MySQL-----表的约束

MySQL-----表的约束

MySQL-----表的约束

文章目录

  • 前言
  • 一、空属性
  • 二、默认值
  • 三、列描述
  • 四、zerofil
  • 五、主键
  • 六、自增长
  • 七、唯一键
  • 八、外键
  • 总结

前言

真正约束字段是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,
从业务逻辑角度保证数据的正确性.比如有一个字段是email,要求是唯一的.

表的约束很多,这里主要介绍如下几个: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 null+1;
+--------+
| null+1 |
+--------+
|   NULL |
+--------+
1 row in set (0.00 sec)mysql> select NULL;
+------+
| NULL |
+------+
| NULL |
+------+
1 row in set (0.00 sec)

案例:
创建一个班级表,包含班级名和班级所在的教室.

站在正常的业务逻辑中:

  • 如果班级没有名字,你不知道你在哪个班级.
  • 如果教室名字可以为空,就不知道在哪上课

所以我们在设计数据库表的时候,一定要在表中进行限制,满足上面条件的数据就不能插入到表中.这个就是**“约束”**.

mysql> create table if not exists myclass(-> class_name varchar(20) not null,-> class_room varchar(20) not null-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc myclass;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| class_name | varchar(20) | NO   |     | NULL    |       |
| class_room | varchar(20) | NO   |     | NULL    |       |
+------------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)mysql> insert into myclass values ('20060316','301');
Query OK, 1 row affected (0.00 sec)mysql> insert into myclass values ('20060317','302');
Query OK, 1 row affected (0.00 sec)mysql> insert into myclass (class_name) values ('20060317');
ERROR 1364 (HY000): Field 'class_room' doesn't have a default value
mysql> insert into myclass (class_name) values (null);
ERROR 1048 (23000): Column 'class_name' cannot be null
mysql> insert into myclass (class_room) values ('501');
ERROR 1364 (HY000): Field 'class_name' doesn't have a default value
# 插入数据时,没有给教室数据插入失败或者插入空失败:mysql> select * from myclass;
+------------+------------+
| class_name | class_room |
+------------+------------+
| 20060316   | 301        |
| 20060317   | 302        |
+------------+------------+
2 rows in set (0.00 sec)

二、默认值

默认值:某一种数据会经常性的出现某个具体的值,可以在一开始就指定好,在需要真实数据的时候,用户可以选择性的使用默认值.

mysql> create table t13(-> name varchar(20) not null,-> age tinyint unsigned default 18,-> gender char(2) default '男'-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc t13;
+--------+---------------------+------+-----+---------+-------+
| Field  | Type                | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| name   | varchar(20)         | NO   |     | NULL    |       |
| age    | tinyint(3) unsigned | YES  |     | 18      |       |
| gender | char(2)             | YES  |     ||       |
+--------+---------------------+------+-----+---------+-------+
3 rows in set (0.01 sec)mysql> insert into t13 (name) values ('梁朝伟'); # 此时并没有指名年龄和性别
Query OK, 1 row affected (0.00 sec)mysql> select * from t13;
+-----------+------+--------+
| name      | age  | gender |
+-----------+------+--------+
| 梁朝伟    |   18 ||
+-----------+------+--------+
1 row in set (0.00 sec)mysql> insert into t13 (name,age) values ('周星驰',60);
Query OK, 1 row affected (0.00 sec)mysql> insert into t13 (name,gender) values ('汤唯','女');
Query OK, 1 row affected (0.00 sec)mysql> select * from t13;
+-----------+------+--------+
| name      | age  | gender |
+-----------+------+--------+
| 梁朝伟    |   18 ||
| 周星驰    |   60 ||
| 汤唯      |   18 ||
+-----------+------+--------+
3 rows in set (0.00 sec)mysql> insert into t13 (name,age,gender) values ('王祖贤',30,'女');
Query OK, 1 row affected (0.00 sec)mysql> select * from t13;
+-----------+------+--------+
| name      | age  | gender |
+-----------+------+--------+
| 梁朝伟    |   18 ||
| 周星驰    |   60 ||
| 汤唯      |   18 ||
| 王祖贤    |   30 ||
+-----------+------+--------+
4 rows in set (0.00 sec)mysql> insert into t13 (age,gender) values (30,'女');
ERROR 1364 (HY000): Field 'name' doesn't have a default value
# name设置not null,所以出现错误

default 和 null

  • default 在我们不显示的向指定列中插入,default自动会起效果.
  • NULL:当我们显示的向一列插入,如果插入的是正常值,就正常工作,否则,插入NULL,NOT NULL就约束你不让你插入.

默认值的生效:数据在插入的时候不给该字段赋值,就使用默认值!

mysql> create table t14(-> name varchar(20) not null,-> age int not null default 18-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc t14;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name  | varchar(20) | NO   |     | NULL    |       |
| age   | int(11)     | NO   |     | 18      |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)mysql> insert into t14 values ('张学友',66);
Query OK, 1 row affected (0.01 sec)mysql> insert into t14 (name) values ('黎明');
Query OK, 1 row affected (0.00 sec)mysql> insert into t14 values ('郭富城',null); # 为空就不能进行插入
ERROR 1048 (23000): Column 'age' cannot be nullmysql> select * from t14;
+-----------+-----+
| name      | age |
+-----------+-----+
| 张学友    |  66 |
| 黎明      |  18 |
+-----------+-----+
2 rows in set (0.00 sec)
--注意:只有设置了default的列,才可以在插入值的时候,对列进行省略

MySQL-----表的约束
default : 可以保证我们表中数据的完整性!!!

注意:not null和defalut一般不需要同时出现,因为default本身有默认值,不会为空!!!

三、列描述

列描述:comment,没有实际含义,专门用来描述字段,会根据表创建语句保存,用来给程序猿或者DBA来进行了解.

mysql> create table if not exists t15(-> name varchar(20) not null comment '这是用户的姓名',-> age tinyint unsigned default 18 comment '这是用户的年龄',-> gender char(1) not null default '男' comment '这是用户的性别'-> );
Query OK, 0 rows affected (0.01 sec)# 通过desc查看不到注释信息:
mysql> desc t15;
+--------+---------------------+------+-----+---------+-------+
| Field  | Type                | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| name   | varchar(20)         | NO   |     | NULL    |       |
| age    | tinyint(3) unsigned | YES  |     | 18      |       |
| gender | char(1)             | NO   |     ||       |
+--------+---------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)mysql> insert into t15 (name,age,gender) values ('彭于晏',30,'男');
Query OK, 1 row affected (0.00 sec)mysql> insert into t15 (name,age) values ('黄景瑜',30);
Query OK, 1 row affected (0.00 sec)mysql> insert into t15 (name,gender) values ('胡歌','男');
Query OK, 1 row affected (0.00 sec)mysql> insert into t15 (name) values ('刘昊然');
Query OK, 1 row affected (0.00 sec)mysql> insert into t15 (name,age) values ('孙红雷',NULL);
Query OK, 1 row affected (0.00 sec)mysql> select * from t15;
+-----------+------+--------+
| name      | age  | gender |
+-----------+------+--------+
| 彭于晏    |   30 ||
| 黄景瑜    |   30 ||
| 胡歌      |   18 ||
| 刘昊然    |   18 ||
| 孙红雷    | NULL ||
+-----------+------+--------+
5 rows in set (0.00 sec)# 通过show可以看到:
mysql> show create table t15\\G
*************************** 1. row ***************************Table: t15
Create Table: CREATE TABLE `t15` (`name` varchar(20) NOT NULL COMMENT '这是用户的姓名',`age` tinyint(3) unsigned DEFAULT '18' COMMENT '这是用户的年龄',`gender` char(1) NOT NULL DEFAULT '男' COMMENT '这是用户的性别'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

四、zerofil

刚开始学习数据库时,可能会对数字类型后面的长度很迷茫.通过show看看表的建表语句:

mysql> create table if not exists t16(-> a int not null,-> b int unsigned not null-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc t16;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| a     | int(11)          | NO   |     | NULL    |       |
| b     | int(10) unsigned | NO   |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)mysql> show create table t16\\G
*************************** 1. row ***************************Table: t16
Create Table: CREATE TABLE `t16` (`a` int(11) NOT NULL,`b` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

可以看到int(10),这个代表什么意思呢?整形不是四个字节吗?这个10又代表什么呢?其实没有zerofill这个属性,括号内的数字是毫无意义的.a和b就是前面插入的数据,如下:

mysql> insert into t16 values (1,2);
Query OK, 1 row affected (0.01 sec)mysql> select * from t16;
+---+---+
| a | b |
+---+---+
| 1 | 2 |
+---+---+
1 row in set (0.00 sec)

但是对列添加了zerofill属性后,显示的结果就有所不同了.

修改t16表的属性:

mysql> alter table t16 change a a int(5) unsigned zerofill;
Query OK, 1 row affected (0.03 sec)
Records: 1  Duplicates: 0  Warnings: 0mysql> show create table t16\\G
*************************** 1. row ***************************Table: t16
Create Table: CREATE TABLE `t16` (`a` int(5) unsigned zerofill DEFAULT NULL,  # 具有了zerofill属性`b` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)mysql> select * from t16;
+-------+---+
| a     | b |
+-------+---+
| 00001 | 2 |
+-------+---+
1 row in set (0.00 sec)

这次可以看到a的值由原来的1变成了00001,这就是zerofill属性的作用,如果宽度小于设定的宽度(这里设置的是5),自动填充0.要注意的是,这只是最后显示的结果,在MySQL中实际存储的还是1.为什么是这样呢?我们可以用hex函数来证明.

mysql> select * from t16 where a=1;
+-------+---+
| a     | b |
+-------+---+
| 00001 | 2 |
+-------+---+
1 row in set (0.00 sec)mysql> select hex(a),b from t16;
+--------+---+
| hex(a) | b |
+--------+---+
| 1      | 2 |
+--------+---+
1 row in set (0.00 sec)
# hex转化称为10进制

可以看出数据库内部存储的还是1,00001只是设置了zerofill属性后的一种格式化输出而已!

五、主键

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

案例:

  • 创建表的时候直接在字段上指定主键
mysql> create table if not exists t17(-> id int unsigned primary key comment '学生的学号是主键',-> name varchar(20) not null-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc t17;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id    | int(10) unsigned | NO   | PRI | NULL    |       | <= key 中 pri表示
该字段是主键 默认是not null
| name  | varchar(20)      | NO   |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)mysql> insert into t17 (id,name) values (1,'邓超');
Query OK, 1 row affected (0.00 sec)mysql> insert into t17 (id,name) values (2,'陈赫');
Query OK, 1 row affected (0.00 sec)mysql> insert into t17 (id,name) values (3,'陈赫');
Query OK, 1 row affected (0.00 sec)mysql> select * from t17;
+----+--------+
| id | name   |
+----+--------+
|  1 | 邓超   |
|  2 | 陈赫   |
|  3 | 陈赫   |
+----+--------+
3 rows in set (0.00 sec)
# 名字允许出现重复
  • 主键约束 : 主键对应的字段不能重复,一旦重复,操作失败.
mysql> insert into t17 (id,name) values (3,'鹿晗'); # 主键不能重复
ERROR 1062 (23000): Duplicate entry '3' for key 'PRIMARY'
mysql> insert into t17 (id,name) values (4,'鹿晗');
Query OK, 1 row affected (0.01 sec)mysql> select * from t17;
+----+--------+
| id | name   |
+----+--------+
|  1 | 邓超   |
|  2 | 陈赫   |
|  3 | 陈赫   |
|  4 | 鹿晗   |
+----+--------+
4 rows in set (0.00 sec)
  • 删除主键
mysql> alter table t17 drop primary key;
Query OK, 4 rows affected (0.03 sec)
Records: 4  Duplicates: 0  Warnings: 0mysql> desc t17;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id    | int(10) unsigned | NO   |     | NULL    |       |
| name  | varchar(20)      | NO   |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)# 没有主键后就可以插入重复数据啦!!!
mysql> insert into t17 (id,name) values (1,'王勉');
Query OK, 1 row affected (0.00 sec)mysql> select * from t17;
+----+--------+
| id | name   |
+----+--------+
|  1 | 邓超   |
|  2 | 陈赫   |
|  3 | 陈赫   |
|  4 | 鹿晗   |
|  1 | 王勉   |
+----+--------+
5 rows in set (0.00 sec)
  • 当表创建好以后但是没有主键的时候,可以再次追加主键
mysql> alter table t17 add primary key(id);
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
# 因为我们有重复数据1,所以mysql不让我们插入mysql> delete from t17 where name='王勉'; # 删除重复的数据
Query OK, 1 row affected (0.01 sec)mysql> alter table t17 add primary key(id);
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0mysql> desc t17;
+-------+------------------+------+-----+---------+-------+
| 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 t17\\G
*************************** 1. row ***************************Table: t17
Create Table: CREATE TABLE `t17` (`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> create table if not exists t18(-> id varchar(20),-> course varchar(30) comment '课程代码',-> score tinyint unsigned default 60 comment '特定id的学生在特定课程中的得分',-> primary key(id,course)-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc t17;
+-------+------------------+------+-----+---------+-------+
| 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 t18\\G
*************************** 1. row ***************************Table: t18
Create Table: CREATE TABLE `t18` (`id` varchar(20) NOT NULL,`course` varchar(30) NOT NULL COMMENT '课程代码',`score` tinyint(3) unsigned DEFAULT '60' COMMENT '特定id的学生在特定课程中的得分',PRIMARY KEY (`id`,`course`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)mysql> insert into t18 (id,course,score) values ('1','C语言','80');
Query OK, 1 row affected (0.00 sec)mysql> insert into t18 (id,course,score) values ('1','C++','90');
Query OK, 1 row affected (0.00 sec)mysql> insert into t18 (id,course,score) values ('2','C++','90');
Query OK, 1 row affected (0.00 sec)mysql> insert into t18 (id,course,score) values ('1','C语言','60'); # 无法被插入
ERROR 1062 (23000): Duplicate entry '1-C语言' for key 'PRIMARY'mysql> select * from t18;
+----+---------+-------+
| id | course  | score |
+----+---------+-------+
| 1  | C++     |    90 |
| 1  | C语言   |    80 |
| 2  | C++     |    90 |
+----+---------+-------+
3 rows in set (0.00 sec)

复合主键不允许两个主键列的值同时相同!!!局部冲突是不影响的!!!

如何设计主键呢?

  1. 结合你的业务,就可以选择一个唯一的列属性作为主键.
  2. 选择与业务无关的唯一值(特点设计的)–>这样设计,主键值和业务无关,业务调整,不影响整体主键的表结构.

六、自增长

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

自增长的特点:

  • 任何一个字段要做自增长,前提是本身是一个索引(key一栏有值)
  • 自增长字段必须是整数
  • 一张表最多只能有一个自增长

案例:

mysql> create table t19(-> id int unsigned auto_increment,-> name varchar(20) not null-> );
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
# 自增长必须设置为主键mysql> create table t19(-> id int primary key unsigned auto_increment,-> name varchar(20) not null-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc t19;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20)      | NO   |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)mysql> show create table t19\\G
*************************** 1. row ***************************Table: t19
Create Table: CREATE TABLE `t19` (`id` int(10) unsigned 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> insert into t19 (id,name) values (1,'曾小贤');
Query OK, 1 row affected (0.00 sec)mysql> insert into t19 (name) values ('胡一菲');
Query OK, 1 row affected (0.00 sec)mysql> insert into t19 (name) values ('吕子乔');
Query OK, 1 row affected (0.00 sec)mysql> insert into t19 (name) values ('美嘉');
Query OK, 1 row affected (0.00 sec)mysql> insert into t19 (name) values ('大姨妈');
Query OK, 1 row affected (0.00 sec)mysql> select * from t19;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | 曾小贤    |
|  2 | 胡一菲    |
|  3 | 吕子乔    |
|  4 | 美嘉      |
|  5 | 大姨妈    |
+----+-----------+
5 rows in set (0.00 sec)mysql> insert into t19 (id,name) values (10,'关谷');
Query OK, 1 row affected (0.00 sec)mysql> insert into t19 (name) values ('陆展博');
Query OK, 1 row affected (0.00 sec)mysql> insert into t19 (name) values ('诸葛大力');
Query OK, 1 row affected (0.01 sec)mysql> select * from t19;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | 曾小贤       |
|  2 | 胡一菲       |
|  3 | 吕子乔       |
|  4 | 美嘉         |
|  5 | 大姨妈       |
| 10 | 关谷         |
| 11 | 陆展博       |
| 12 | 诸葛大力     |
+----+--------------+
8 rows in set (0.00 sec)mysql> insert into t19 (id,name) values (6,'婉瑜');
Query OK, 1 row affected (0.00 sec)mysql> insert into t19 (name) values ('张伟');
Query OK, 1 row affected (0.00 sec)mysql> select * from t19;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | 曾小贤       |
|  2 | 胡一菲       |
|  3 | 吕子乔       |
|  4 | 美嘉         |
|  5 | 大姨妈       |
|  6 | 婉瑜         |
| 10 | 关谷         |
| 11 | 陆展博       |
| 12 | 诸葛大力     |
| 13 | 张伟         |
+----+--------------+
10 rows in set (0.00 sec)# 我们如何知道下一个主键该插入的值是多少呢
mysql> show create table t19\\G
*************************** 1. row ***************************Table: t19
Create Table: CREATE TABLE `t19` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(20) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)# AUTO_INCREMENT=14这个字段帮我们维护mysql> insert into t19 (id,name) values (1000,'小布');
Query OK, 1 row affected (0.00 sec)mysql> select * from t19;
+------+--------------+
| id   | name         |
+------+--------------+
|    1 | 曾小贤       |
|    2 | 胡一菲       |
|    3 | 吕子乔       |
|    4 | 美嘉         |
|    5 | 大姨妈       |
|    6 | 婉瑜         |
|   10 | 关谷         |
|   11 | 陆展博       |
|   12 | 诸葛大力     |
|   13 | 张伟         |
| 1000 | 小布         |
+------+--------------+
11 rows in set (0.00 sec)mysql> show create table t19\\G
*************************** 1. row ***************************Table: t19
Create Table: CREATE TABLE `t19` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(20) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

那么如果我要自增长从10000开始呢?

mysql> create table t20 (-> id int unsigned auto_increment,-> name varchar(20) not null,-> primary key(id)-> )auto_increment=10001;
Query OK, 0 rows affected (0.02 sec)mysql> insert into t20 (name) values ('马化腾');
Query OK, 1 row affected (0.00 sec)mysql> insert into t20 (name) values ('张志东');
Query OK, 1 row affected (0.00 sec)mysql> insert into t20 (name) values ('曾李青');
Query OK, 1 row affected (0.00 sec)mysql> select * from t20;
+-------+-----------+
| id    | name      |
+-------+-----------+
| 10001 | 马化腾    |
| 10002 | 张志东    |
| 10003 | 曾李青    |
+-------+-----------+
3 rows in set (0.00 sec)

索引 : 在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中的一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单.缩印的作业相当于图书的目录,可以根据目录中的页码快速找到所需的内容.

索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针进行排序.数据库使用索引以找到特定值,然后顺时针找到包含该值的行.这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息.

七、唯一键

一张表中往往有很多字段需要唯一性,数据不能重复,但是一张表中只能有一个主键;唯一键就可以解决表中有多个字段需要唯一性约束的问题!!!

唯一键的本质和主键差不多,唯一键允许为空,而且可以多个为空,空字段不做唯一性比较.

关于唯一键和主键的区别:我们可以简单理解成,主键更多的是标识唯一性的.而唯一键更多的是保证在业务上,不要和别的信息出现重复.

案例:
我们数据库中的学生表,本质其实是用来描述一堆学生信息.以一个学生为例:表中存的都是我们身上的属性值.我们身上的属性值,有的可以重复的,有的是不会重复的!

唯一键和主键并不冲突,反而是互相补充的!共同维护表的完整性!!!

案例:

mysql> create table if not exists stus(-> stu_id bigint unsigned primary key auto_increment,-> name varchar(20) not null,-> qq varchar(20) not null,-> tel varchar(16) not null-> )auto_increment=100;mysql> desc stus;
+--------+---------------------+------+-----+---------+----------------+
| Field  | Type                | Null | Key | Default | Extra          |
+--------+---------------------+------+-----+---------+----------------+
| stu_id | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| name   | varchar(20)         | NO   |     | NULL    |                |
| qq     | varchar(20)         | NO   |     | NULL    |                |
| tel    | varchar(16)         | NO   |     | NULL    |                |
+--------+---------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)mysql> insert into stus (name,qq,tel) values ('章若楠','123qq.com','182');
Query OK, 1 row affected (0.00 sec)mysql> insert into stus (name,qq,tel) values ('孙千','123qq.com','183');
Query OK, 1 row affected (0.00 sec)mysql> insert into stus (name,qq,tel) values ('张新成','456qq.com','182');
Query OK, 1 row affected (0.00 sec)mysql> select * from stus;
+--------+-----------+-----------+------+
| stu_id | name      | qq        | tel  |
+--------+-----------+-----------+------+
|    100 | 章若楠    | 123qq.com | 182  |
|    101 | 孙千      | 123qq.com | 1883 |
|    102 | 张新成    | 456qq.com | 182  |
+--------+-----------+-----------+------+
3 rows in set (0.00 sec)

现实生活中,你的身上有非常多的具有唯一性的值.一般而言,主键只是在众多具有唯一性的属性列中被选择成为了主键而已.其他字段的唯一性,我们在建表的时候,也要保证它的唯一性!!但是因为主键只能有一个,所以mysql提供了另一种保证数据列信息唯一性的方案 — 唯一键

主键:更多的是为了保证我们在查找的时候,找到唯一的一条记录!

唯一键:更多是为了保证在表中,各个不同的值,一定要在mysql层面也保证它的唯一性!

mysql> create table if not exists stus(-> id bitint unsigned primary key auto_increment,-> name varchar(20) not null,-> qq varchar(20) unique key comment 'qq号码虽然不是主键,但是它的唯一性也需要保证',-> tel varchar(20) unique key comment '电话号码也要保证唯一性'-> )auto_increment=10000;
Query OK, 0 rows affected (0.01 sec)mysql> desc stus;
+-------+---------------------+------+-----+---------+----------------+
| Field | Type                | Null | Key | Default | Extra          |
+-------+---------------------+------+-----+---------+----------------+
| id    | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20)         | NO   |     | NULL    |                |
| qq    | varchar(20)         | YES  | UNI | NULL    |                |
| tel   | varchar(20)         | YES  | UNI | NULL    |                |
+-------+---------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)mysql> insert into stus (name,qq,tel) values ('章若楠','123qq.com','182');
Query OK, 1 row affected (0.00 sec)mysql> insert into stus (name,qq,tel) values ('孙千','123qq.com','183'); # qq重复,不允许插入
ERROR 1062 (23000): Duplicate entry '123qq.com' for key 'qq'
mysql> insert into stus (name,qq,tel) values ('张新成','456qq.com','182');# 电话重复,不允许插入
ERROR 1062 (23000): Duplicate entry '182' for key 'tel'
mysql> select * from stus;
+-------+-----------+-----------+------+
| id    | name      | qq        | tel  |
+-------+-----------+-----------+------+
| 10000 | 章若楠    | 123qq.com | 182  |
+-------+-----------+-----------+------+
1 row in set (0.00 sec)

唯一键设置为非空就成为了主键!!!

mysql> create table t21(-> id int unique key not null-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc t21;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | NO   | PRI | NULL    |       |	# 主键PRI
+-------+---------+------+-----+---------+-------+
1 row in set (0.01 sec)mysql> show create table t21\\G
*************************** 1. row ***************************Table: t21
Create Table: CREATE TABLE `t21` (`id` int(11) NOT NULL,UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

八、外键

外键用于定于主表和从表之间的关系,维护主表和从表之间的约束,外建约束只要定义在从表上,主表则必须是有主键约束或unique key约束.当定义外键后,要求外键列数据必须在主表的主键列存在或者为null.

语法:

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

案例:
MySQL-----表的约束
如果将班级表中的数据都设计在每个学生表的后面,那就会出现数据冗余,所以我们只要设计成让stu->class_id和myclass->id形成关联的关系 -> 外键约束

对上面的示意图进行设计:

  • 先创建主表
mysql> create table if not exists class_tb(-> id bigint primary key,-> name varchar(20) not null-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc class_tb;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | bigint(20)  | NO   | PRI | NULL    |       |
| name  | varchar(20) | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)mysql> show create table class_tb\\G
*************************** 1. row ***************************Table: class_tb
Create Table: CREATE TABLE `class_tb` (`id` bigint(20) NOT NULL,`name` varchar(20) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
  • 创建从表
mysql> create table if not exists student_tb(-> id bigint primary key,-> name varchar(20) not null,-> class_id bigint-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc student_tb;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id       | bigint(20)  | NO   | PRI | NULL    |       |
| name     | varchar(20) | NO   |     | NULL    |       |
| class_id | bigint(20)  | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)mysql> show create table student_tb\\G
*************************** 1. row ***************************Table: student_tb
Create Table: CREATE TABLE `student_tb` (`id` bigint(20) NOT NULL,`name` varchar(20) NOT NULL,`class_id` bigint(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

案例:

mysql> insert into class_tb values (1,'20060316');
Query OK, 1 row affected (0.00 sec)mysql> insert into student_tb values (33060,'hulu',1);
Query OK, 1 row affected (0.00 sec)mysql> insert into student_tb values (33061,'axy',1);
Query OK, 1 row affected (0.01 sec)mysql> insert into student_tb values (33062,'zzy',1);
Query OK, 1 row affected (0.00 sec)mysql> select * from student_tb;
+-------+------+----------+
| id    | name | class_id |
+-------+------+----------+
| 33060 | hulu |        1 |
| 33061 | axy  |        1 |
| 33062 | zzy  |        1 |
+-------+------+----------+
3 rows in set (0.00 sec)mysql> insert into class_tb values (2,'20060317');
Query OK, 1 row affected (0.00 sec)mysql> insert into student_tb values (33063,'wjy',1);
Query OK, 1 row affected (0.01 sec)mysql> insert into student_tb values (33064,'lxy',2);
Query OK, 1 row affected (0.01 sec)mysql> insert into student_tb values (33065,'lxy',null);
Query OK, 1 row affected (0.01 sec)mysql> select * from student_tb;
+-------+------+----------+
| id    | name | class_id |
+-------+------+----------+
| 33060 | hulu |        1 |
| 33061 | axy  |        1 |
| 33062 | zzy  |        1 |
| 33063 | wjy  |        1 |
| 33064 | lxy  |        2 |
| 33065 | lxy  |     NULL |
+-------+------+----------+
6 rows in set (0.00 sec)mysql> select * from class_tb;
+----+----------+
| id | name     |
+----+----------+
|  1 | 20060316 |
|  2 | 20060317 |
+----+----------+
2 rows in set (0.00 sec)mysql> update student_tb set name='zyz' where id=33065;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0mysql> insert into student_tb values (33066,'fyd',3);
Query OK, 1 row affected (0.00 sec)mysql> select * from student_tb;
+-------+------+----------+ 
| id    | name | class_id |
+-------+------+----------+
| 33060 | hulu |        1 |
| 33061 | axy  |        1 |
| 33062 | zzy  |        1 |
| 33063 | wjy  |        1 |
| 33064 | lxy  |        2 |
| 33065 | zyz  |     NULL |
| 33066 | fyd  |        3 |
+-------+------+----------+
7 rows in set (0.00 sec)
# 但是我们现在发现明明没有班级id=3的班级,但是插入了一个同学,班级所在id=3,这个就出现了问题!!!# 删除104班级
mysql> delete from class_tb where id=1;
Query OK, 1 row affected (0.00 sec)mysql> select * from class_tb;
+----+----------+
| id | name     |
+----+----------+
|  2 | 20060317 |
+----+----------+
1 row in set (0.00 sec)mysql> select * from student_tb;
+-------+------+----------+
| id    | name | class_id |
+-------+------+----------+
| 33060 | hulu |        1 |
| 33061 | axy  |        1 |
| 33062 | zzy  |        1 |
| 33063 | wjy  |        1 |
| 33064 | lxy  |        2 |
| 33065 | zyz  |     NULL |
| 33066 | fyd  |        3 |
+-------+------+----------+
7 rows in set (0.00 sec)
# 在这里发现删除了1号班级,但是班级里的学生还在,这在现实中是不可取的!!!

我们刚刚,在语义上,只有外键的关系,但是,其实默认还是两张独立的表.即表和表之间是不存在约束的!!!

  • 设置外键约束
mysql> create table if not exists class_tb(-> id bigint primary key,-> name varchar(20) not null-> );
Query OK, 0 rows affected (0.01 sec)mysql> create table if not exists student_tb(-> id bigint primary key auto_increment,-> name varchar(20) not null,-> class_id bigint,-> foreign key(class_id) references class_tb(id)-> );
Query OK, 0 rows affected (0.01 sec)mysql> desc class_tb;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | bigint(20)  | NO   | PRI | NULL    |       |
| name  | varchar(20) | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)mysql> desc student_tb;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | bigint(20)  | NO   | PRI | NULL    | auto_increment |
| name     | varchar(20) | NO   |     | NULL    |                |
| class_id | bigint(20)  | YES  | MUL | NULL    |                |
+----------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)mysql> show create table student_tb\\G
*************************** 1. row ***************************Table: student_tb
Create Table: CREATE TABLE `student_tb` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(20) NOT NULL,`class_id` bigint(20) DEFAULT NULL,PRIMARY KEY (`id`),KEY `class_id` (`class_id`),CONSTRAINT `student_tb_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class_tb` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)mysql> show create table class_tb\\G
*************************** 1. row ***************************Table: class_tb
Create Table: CREATE TABLE `class_tb` (`id` bigint(20) NOT NULL,`name` varchar(20) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)mysql> insert into class_tb values (1,'20060316');
Query OK, 1 row affected (0.00 sec)mysql> insert into student_tb values (33060,'张艺兴',1);
Query OK, 1 row affected (0.00 sec)mysql> insert into student_tb (name,class_id) values ('孙红雷',1);
Query OK, 1 row affected (0.00 sec)# 不能插入班级号为2,因为没有这个班级
mysql> insert into student_tb (name,class_id) values ('孙红雷',2);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`hulu_db`.`student_tb`, CONSTRAINT `student_tb_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class_tb` (`id`))
mysql> insert into class_tb values (2,'20060317');
Query OK, 1 row affected (0.01 sec)mysql> insert into student_tb (name,class_id) values ('黄磊',2);
Query OK, 1 row affected (0.00 sec)mysql> insert into student_tb (name,class_id) values ('罗志祥',3);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`hulu_db`.`student_tb`, CONSTRAINT `student_tb_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class_tb` (`id`))mysql> insert into student_tb (name,class_id) values ('罗志祥',2);
Query OK, 1 row affected (0.00 sec)mysql> insert into student_tb (name,class_id) values ('王迅',null);
Query OK, 1 row affected (0.00 sec)mysql> insert into student_tb (name,class_id) values ('黄渤',2);
Query OK, 1 row affected (0.00 sec)mysql> select * from student_tb;
+-------+-----------+----------+
| id    | name      | class_id |
+-------+-----------+----------+
| 33060 | 张艺兴    |        1 |
| 33061 | 孙红雷    |        1 |
| 33063 | 黄磊      |        2 |
| 33065 | 罗志祥    |        2 |
| 33066 | 王迅      |     NULL |
| 33067 | 黄渤      |        2 |
+-------+-----------+----------+
6 rows in set (0.00 sec)mysql> select * from class_tb;
+----+----------+
| id | name     |
+----+----------+
|  1 | 20060316 |
|  2 | 20060317 |
+----+----------+
2 rows in set (0.00 sec)# 删除班级号为1的班级
mysql> select * from class_tb;
+----+----------+
| id | name     |
+----+----------+
|  1 | 20060316 |
|  2 | 20060317 |
+----+----------+
2 rows in set (0.00 sec)mysql> delete from class_tb where id=1; --不能删除
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`hulu_db`.`student_tb`, CONSTRAINT `student_tb_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class_tb` (`id`))
mysql> select * from class_tb;
+----+----------+
| id | name     |
+----+----------+
|  1 | 20060316 |
|  2 | 20060317 |
+----+----------+
2 rows in set (0.00 sec)# 先删除这个班级的学生
mysql> delete from student_tb where class_id=1;
Query OK, 2 rows affected (0.00 sec)mysql> select * from sudent_tb;
ERROR 1146 (42S02): Table 'hulu_db.sudent_tb' doesn't exist
mysql> select * from student_tb;
+-------+-----------+----------+
| id    | name      | class_id |
+-------+-----------+----------+
| 33063 | 黄磊      |        2 |
| 33065 | 罗志祥    |        2 |
| 33066 | 王迅      |     NULL |
| 33067 | 黄渤      |        2 |
+-------+-----------+----------+
4 rows in set (0.00 sec)# 然后在进行删除就可以了
mysql> delete from class_tb where id=1;
Query OK, 1 row affected (0.00 sec)mysql> select * from class_tb;
+----+----------+
| id | name     |
+----+----------+
|  2 | 20060317 |
+----+----------+
1 row in set (0.00 sec)

什么叫做外键?

  • 外键不仅仅是产生表和表之间的关联的,还有一个重要属性往往被人忽略,外键在MySQL中还具有特点的约束规则,保证表和表的数据完整性和一致性.
  • 存在约束的关联字段,才叫做外键.
  • 区分:关联 VS 约束
首先我们承认,这个世界是数据很多都是相关性的。
理论上,上面的例子,我们不创建外键约束,就正常建立学生表,以及班级表,该有的字段我们都有。
此时,在实际使用的时候,可能会出现什么问题?
有没有可能插入的学生信息中有具体的班级,但是该班级却没有在班级表中?
比如只有20060316和20060317两个班级,但是在上课的学生里面竟然有20060318班的学生(这个班目前并
不存在),这很明显是有问题的。
因为此时两张表在业务上是有相关性的,但是在业务上没有建立约束关系,那么就可能出现问题。
解决方案就是通过外键完成的。建立外键的本质其实就是把相关性交给mysql去审核了,提前告诉mysql
表之间的约束关系,那么当用户插入不符合业务逻辑的数据的时候,mysql不允许你插入。

总结

(本章完!!!)