真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性。所谓约束,就是避免犯一些低级错误,比如类似于语法错误,编译器的编译失败实际上也算是一种约束。
表中一定要有各种约束,通过约束,让我们未来插入数据库表中的数据是符合预期的。
约束的本质: 通过技术手段倒逼程序员插入正确的数据。反过来站在mysql的视角,凡是插入进来的数据,都是符合数据约束的。
约束的最终目标: 保证数据的完整性和可预期性。
为什么数据库这么严格?
数据库作为维护用户数据的最后一道防线,必须要保证数据库的数据是满足预期的,因此数据库的约束必须严格。为了更好的约束,就需要更多的约束条件,这也是这一节的内容。
通过desc操作,其后四列就属于约束条件:
总结一下:约束就是表结构的设计者在其他人插入数据之前,预先把规则定好,再让被别人按照他的规则进行数据的增删查改,不满足约束的数据不会被插入到对应的表中,这样下来,插入后的数据就是完整的,并且是可预期的。
表的约束很多,这里主要介绍如下几个: null/not null
,default
, comment
, zerofill
,primary key
,auto_increment
,unique key
。
在MySQL数据类型已经说过,这二者是不同的,NULL代表什么都没有,而' '
代表一个空串。
在select语句中,NULL不会参与到相应的计算操作中。
在这个约束中,有两个选择:
数据库默认字段基本都是字段允许为空,但是实际开发时,尽可能保证字段不为空,因为数据为空没办法参与运算。因此要添加非空约束,不让其中一个属性为空时插入。
创建表时约束不为空:not null
create table myclass(
class_name varchar(20) not null,
class_room varchar(20) not null,
other varchar(20)
);
这样,在NULL列中就会发现显示的是No,也就是不允许为空,即我们插入数据时,代表No的两行必须插入有效数据,否则就无法插入。
实际上就是缺省值,建表时设定之后,即便不对这个字段进行插入,其对应的值也会默认为default后面跟着的值。
插入default对应的字段后,就会将缺省值覆盖。
实际上,default就是在创建表之前将缺省值也插入进去。如果not null和default同时设置,not null的限制也会被default的缺省值补齐,因此并不是真的不插入,而是插入了缺省值。
我们在没有default约束,也没用not null约束时,是允许为空的,这个null值实际上却是mysqld底层优化出来的default的缺省值。
实际上comment描述的设置并不会阻止数据进行插入,其可以等同于注释,即对列的介绍。
那为什么还说comment是一种约束呢?实际上什么都约束不了啊?
其实,通过程序员的插入数据,comment会对思想上做出约束,告诉你这是一种什么数据。也算是一种软约束。
zerofill约束,就是补位。对于我们所创建的表,会发现在一个整形数据类型中带有括号字段。
而这个字段就代表着整形int的数据的最大长度,一般的数据达不到这样的长度,那么如果使用zerofill约束,就会用0将没用到的位置进行占位。
插入数据:
可以看出二者的区别。
索引和主键之间是有联系的。
主键:primary key用来唯一的约束该字段里面的数据,不能重复,不能为空,一张表中最多只能有一个。
主键;主键所在的列通常是整数类型,比如学号,编号等具有唯一性质的数据。
为了保证不能重复,不能为空,相应的创建语句在mysqld中也会添加not null,Key也会标记PRI保证不能重复。
这样,插入的数据就可以重复,但是通过观察,仍不能为空,因为not null在创建表时的添加约束期间会一并添加到约束。不过有了主键,才能更好的查找数据,因为其具有唯一性。
需要注意的是,添加primary key的字段,里面已经插入的数据不能存在重复值,否则就会违背主键的概念,导致约束添加失败。因此,为了避免这种情况,最好在创建表时一并添加主键约束。
回到上述定义,一张表中最多只能有一个主键,但这并不意味着一个表中的主键只能添加到一列。一个主键也可以被添加到多列上,此时的主键被称为复合主键。
两个或者多个字段一并添加为主键,采用如下方式:
这就将id, course_id同时设置为主键,这两个字段就成为复合主键。即这两个字段只要不同时相同,那么就是唯一的。
验证:
只要二个字段不是都相同,就可以进行插入,因为确定唯一性的字段有两个。
自增长也算主键的一种。将其设置为主键,就不用继续管它,插入数据时它本身会为了防止重复而自增+1。看看定义是怎么给的
auto_increment:当对应的字段,不给值,会自动的被系统触发,系统会从当前字段中已经有的最大值+1操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。
自增长的特点
那么此时,即便是主键的id,我们也可以不用理会,直接插入name字段也不会发生错误:
当然,就如同缺省值default一样,这个由于主键的唯一性多了一个自增的条,因此id也可以插入指定的数字:
如果继续插入,不理会id,那么它就会从1000开始自增,下一个就是1001:
为什么能够接着自定义的数据继续自增计数呢?
这是由于在表结构内部存在一个能够存储下一个id值的值,当下一条数据插入,就会将这个值赋值给新的数据对应的id字段:
而我们手动插入的值,实际上就会更改这个值,即将手动的值+1保存到此位置。因此,在建表时,我们也可以手动设置这个值:
通过last_insert_id函数,可以获取上一次的AUTO_INCREMENT的值:
select last_insert_id();
唯一键:唯一键约束后,该字段也具备了和主键一样的唯一性。唯一键允许为空,而且可以多个为空,因为空字段不做唯一性比较。
唯一键和主键的区别:
对于主键的标识唯一性以及唯一键的业务唯一性的理解:
一个表中若存在id,name,telephone三个字段,无疑id是要被标识唯一性主键的。在插入数据中,telephone作为业务上的数据,不能出现重复,一旦插入出现纰漏,将相同的telephone的数据给了不同的id,那么就造成了无法精准确定个人信息的情况,为了避免这种情况的发生,就需要将telephone进行唯一性约束
唯一性约束语法:unique
create table student(
id char(10) primary key '学生的id',
name varchar(20) comment '学生的名字',
telephone varchar(11) unique comment '电话号具有唯一性'
);
对于学生表和班级表,学生一定隶属于某一个班级,因此两个表之间一定存在所属关系,学生属于班级,设计表时通过外键约束学生就属于从表,班级就属于主表。
如果将学生表和班级表结合成一个表,那么在插入数据时,就需要插入大量的字段造成没必要的冗余。若将其分成两个表,那么通过一一对应的映射,来减少没必要的数据。
为什么会造成没必要的冗余?
假设学生表有三个字段,班级表有两个字段(其中有一个为公共属性)。在两个表的前提下,若要新增一名学生信息,只需要插入三个字段;若合并成一个表,那么就需要插入3+2-1=4个字段,并且我们知道组合起来的情况也会更多,两个表合并的结果就是数据位置上的相乘,比如:
合并成一个表
张三,李四的数据都分别出现了两次,所以一个表会产生数据冗余。
外键是用于定义主表和从表之间的关系
外键约束主要定义在从表上,主表则必须是有主键约束或unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为null。
语法:
foreign key(字段名) references 主表(列)
实例
创建两个表,student和class
插入对应信息
上面插入的都是符合逻辑正常的插入。
如果在学生表中插入class_id=3的学生,虽然可以插入,但是class表中并没有对应的班级;如果操作delete from class where id=1;
让通信101班级不存在,但学生表仍有通信101对应id的学生。因此,这就需要让两个表产生联动,插入class_id=3的学生的前提是存在id=3的班级,删除id=1的班级最后会连着学生表中所属class_id=1的学生信息一并删除,这就涉及到了外键的约束。
外键存在两种关系:
此时student中的class_id存在外键之名(关联关系),但是没有外键之实。
所以,我们需要重新建立一个从表student,目的是引入外键的逻辑关系。
目前的学生表是空的,先插入数据:
若删除id=1的班级,也不会成功,因为student中还存在class_id=1的学生。只有在student中不存在id=1的学生,才能删除。
这就叫做外键约束。外键的本质就是产生关联,增加约束,保证表和表之间的完整性。
注:主表在从表存在的前提下,不能drop table 主表。
外键约束中,也存在constraint将外键约束命名,不过mysqld内部会自动做这样的操作。
有一个商店的数据,记录客户及购物情况,有以下三个表组成:
要求:
SQL操作:
-- 创建数据库
create database if not exists cfy_database
default character set utf8;
-- 选择数据库
use cfy_database;
-- 创建数据库表
-- 商品
create table if not exists goods
(
goods_id int primary key auto_increment comment '商品编号',
goods_name varchar(32) not null comment '商品名称',
unitprice int not null default 0 comment '单价,单位分',
category varchar(12) comment '商品分类',
provider varchar(64) not null comment '供应商名称'
);
-- 客户
create table if not exists customer
(
customer_id int primary key auto_increment comment '客户编号',
name varchar(32) not null comment '客户姓名',
address varchar(256) comment '客户地址',
email varchar(64) unique key comment '电子邮箱',
sex enum('男','女') not null comment '性别',
card_id char(18) unique key comment '身份证'
);
-- 购买
create table if not exists purchase
(
order_id int primary key auto_increment comment '订单号',
customer_id int comment '客户编号',
goods_id int comment '商品编号',
nums int default 0 comment '购买数量',
foreign key (customer_id) references customer(customer_id),
foreign key (goods_id) references goods(goods_id)
);