【MySQL】表的约束

🌈欢迎来到MySQL专栏

MySQL

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

  • 通过约束,让我们未来插入数据库表中的数据是符合预期的!
  • 约束的本质是通过技术手段,倒逼程序员,插入正确的数据
  • 反过来站在mysql的视角,凡是插入进来的数据,都是符合数据约束的!

约束的最终目标:保证数据的完整性和可预期性

表的约束很多,这里主要介绍如下几个: null/not null,default,comment,zerofill,primary key,auto_increment,unique key

🌏空属性

两个值 :null(默认的)和 not null(不为空)

数据库默认字段基本都是字段为空,但是实际开发时,尽可能保证字段不为空,因为数据为空没办法参与运算

cpp 复制代码
mysql> select null;
+------+
| NULL |
+------+
| NULL |
+------+

mysql> select 1+null;
+--------+
| 1+null |
+--------+
| NULL |
+--------+
1 row in set (0.00 sec)

举例:

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

站在正常的业务逻辑中:

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

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

sql 复制代码
mysql> create table myclass(
-> class_name varchar(20) not null,
-> class_room varchar(10) not null);

mysql> desc myclass;
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| class_name | varchar(20) | NO | | NULL | |
| class_room | varchar(10) | NO | | NULL | |
+------------+-------------+------+-----+---------+-------+

插入数据时可以不给other插入信息!但是class_name 、class_name 的信息在插入时少一个都不行!

sql 复制代码
mysql> insert into myclass (class_name, class_room) values('高三1班', '重点班');

mysql> select * from myclass;
+------------+------------+-----------+
| class_name | class_name | other     |
+------------+------------+-----------+
| 高三1班    | 重点班     | NULL      |
+------------+------------+-----------+

🌏默认值

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

sql 复制代码
mysql> create table if not exists t11( 
    -> name varchar(20) not null,
    -> age tinyint unsigned default 18,
    -> gender char(1) default '男',
    -> );
    
mysql> desc t11;
+--------+---------------------+------+-----+---------+-------+
| Field  | Type                | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| name   | varchar(20)         | NO   |     | NULL    |       |
| age    | tinyint(3) unsigned | YES  |     | 18      |       |
| gender | char(1)             | YES  |     | 男      |       |
+--------+---------------------+------+-----+---------+-------+

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

sql 复制代码
mysql> insert into t11 (name, age, gender) values('张三', 22, '男');
mysql> insert into t11 (name) values('卢本伟');

mysql> select * from t11;
+-----------+------+--------+
| name      | age  | gender |
+-----------+------+--------+
| 张三      |   22 | 男     |
| 卢本伟    |   18 | 男     |
+-----------+------+--------+

注意:只有设置了default的列,才可以在插入值的时候,对列进行省略

sql 复制代码
mysql> create table if not exists t11( 
    -> name varchar(20) not null,
    -> age tinyint unsigned default 18,
    -> gender char(1)not null default '男',
    -> );
sql 复制代码
mysql> insert into t11 (age, gender) values(20, '女');

ERROR 1364 (HY000): Field 'name' doesn't have a default value
sql 复制代码
mysql> insert into t11 (name, age) values('梁', 18); ✅️

根据这个报错信息,我们的name是设置了不能为空,但是我们在插入时不插入name,他就会提醒我们name没有设置默认值!不能直接插入

说明如果我们在插入时忽略了一列,但是这列是设置了not null,并且有默认值,就可以用这个默认值!

default 和 NOT NULL 不冲突!是互相补充的 ;当用户想插入时(NULL、合法数据 );当用户忽略这一列的时候,使用了默认值(如果设置了)如果没有设置,直接报错!

sql 复制代码
mysql> create table if not exists t12(
    -> name varchar(20),
    -> age tinyint unsigned
    -> );

mysql> insert into t12 (age) values(18);

       Table: t12
Create Table: CREATE TABLE `t12` (
  `name` varchar(20) DEFAULT NULL,
  `age` tinyint(3) unsigned DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

此处我们的name并没有设置 not null 和default,但是mysql帮我设置了DEFAULT NULL,我们不写也可以 !

🌏列描述 ~ 类似注释

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

sql 复制代码
mysql> create table tt12 (
-> name varchar(20) not null comment '姓名',
-> age tinyint unsigned default 0 comment '年龄',
-> sex char(2) default '男' comment '性别'
-> );

通过desc查看不到注释信息:

通过show可以看到:

sql 复制代码
mysql> show create table tt12\G
*************************** 1. row ***************************
	Table: tt12
Create Table: CREATE TABLE `tt12` (
	`name` varchar(20) NOT NULL COMMENT '姓名',
	`age` tinyint(3) unsigned DEFAULT '0' COMMENT '年龄',
	`sex` char(2) DEFAULT '男' COMMENT '性别'
) ENGINE=MyISAM DEFAULT CHARSET=gbk

🌏zerofill

刚开始学习数据库时,很多人对数字类型后面的长度 很迷茫。通过show看看t13表的type列

sql 复制代码
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| a     | int(10) unsigned | NO   |     | NULL    |       |
| b     | int(10) unsigned | NO   |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+

其实这个括号里的数字是mysql在优化我们的命令时,帮我们添加上去的

可以看到int(10),这个代表什么意思呢?整型不是4字节码?这个10又代表什么呢?

sql 复制代码
mysql> insert into t13 (a, b) values(1, 2);

mysql> select * from t13;
+---+---+
| a | b |
+---+---+
| 1 | 2 |
+---+---+

其实没有zerofill这个属性,括号内的数字是毫无意义的

sql 复制代码
mysql> alter table t13 modify b int unsigned zerofill;

mysql> desc t13;
+-------+---------------------------+------+-----+---------+-------+
| Field | Type                      | Null | Key | Default | Extra |
+-------+---------------------------+------+-----+---------+-------+
| a     | int(10) unsigned          | NO   |     | NULL    |       |
| b     | int(10) unsigned zerofill | YES  |     | NULL    |       |
+-------+---------------------------+------+-----+---------+-------+

mysql> select * from t13;
+---+------------+
| a | b          |
+---+------------+
| 1 | 0000000002 |
+---+------------+

但是此时的b变成了0000000002,这就是zerofill属性的作用,如果宽度小于设定的宽度(这里设置的是10),自动填充0

  • 但如果插入的数据宽度超过10,那该是什么数就是什么数!

要注意的是,这只是最后显示的结果,在MySQL中实际存储的还是1。为什么是这样呢?我们可以用hex函数来证明。

sql 复制代码
mysql> select a, hex(b) from t13;
+---+--------+
| a | hex(b) |
+---+--------+
| 1 | 2      |
+---+--------+

🌏主键

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

举例:

创建表的时候直接在字段上指定主键 ------ key 中 pri表示该字段是主键

sql 复制代码
mysql> create table if not exists test_key(
    -> id int unsigned primary key,
    -> name varchar(20) not null
    -> );

mysql> desc test_key;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id    | int(10) unsigned | NO   | PRI | NULL    |       |
| name  | varchar(20)      | NO   |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+

主键约束:主键对应的字段中不能重复,一旦重复,操作失败

sql 复制代码
mysql> insert into test_key values(1, '张飞');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test_key values(1, '刘备');❌️
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
sql 复制代码
mysql> select * from test_key where id=1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 张飞   |
+----+--------+

删除主键:

sql 复制代码
alter table test_key drop primary key;

当表创建好以后但是没有主键的时候,可以再次追加主键

sql 复制代码
alter table test_key add primary key(id);

一张表中最多只有一个主键,不意味着一个表中的主键,只能添加给一列!

一个主键可以被添加到一列或者多列上 ------ 复合主键

复合主键:

在创建表的时候,在所有字段之后,使用primary key(主键字段列表)来创建主键 ,如果有多个字段作为主键,可以使用复合主键

id和course为复合主键

sql 复制代码
mysql> create table pick_course(
    -> id int unsigned,
    -> course_id int unsigned comment '课程编号',
    -> score tinyint unsigned comment '课程成绩',
    -> primary key (id, course_id)
    -> );

mysql> desc pick_course;
+-----------+---------------------+------+-----+---------+-------+
| Field     | Type                | Null | Key | Default | Extra |
+-----------+---------------------+------+-----+---------+-------+
| id        | int(10) unsigned    | NO   | PRI | NULL    |       |
| course_id | int(10) unsigned    | NO   | PRI | NULL    |       |
| score     | tinyint(3) unsigned | YES  |     | NULL    |       |
+-----------+---------------------+------+-----+---------+-------+

复合主键冲突!也就是说 12345-55 是一个整体,不能重复选!

sql 复制代码
mysql> insert into pick_course values(12345, 55, 100);

mysql> select * from pick_course;
+-------+-----------+-------+
| id    | course_id | score |
+-------+-----------+-------+
|   123 |        88 |    99 |
| 12345 |        55 |   100 |
| 12345 |        88 |   100 |
+-------+-----------+-------+
3 rows in set (0.00 sec)

mysql> insert into pick_course values(12345, 55, 10);❌️
ERROR 1062 (23000): Duplicate entry '12345-55' for key 'PRIMARY'

🌏自增长

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

自增长的特点:

  • 任何一个字段要做自增长,前提是本身是一个索引(key一栏有值)
  • 自增长字段必须是整数
  • 一张表最多只能有一个自增长
sql 复制代码
mysql> create table if not exists t10(
    -> id int unsigned primary key auto_increment,
    -> name varchar(20) not null
    -> );
    
mysql> desc t10;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20)      | NO   |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+

插入数据a b c

sql 复制代码
mysql> insert into t10 (name) values ('a');

mysql> insert into t10 (name) values ('b');

mysql> insert into t10 (name) values ('c');

mysql> select * from t10;
+----+------+
| id | name |
+----+------+
|  1 | a    |
|  2 | b    |
|  3 | c    |
+----+------+

其实在创建表时,就已经在表外设置好了,下一次应该自增的值了!(当然我们也可以手动设置!)

sql 复制代码
       Table: t10
Create Table: CREATE TABLE `t10` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

如果第四个数插入时999,下一次继续插入数据,会自增为1000!

获取上次插入的 AUTO_INCREMENT 的值(批量插入获取的是第一个值)

sql 复制代码
mysql> select last_insert_id();

+------------------+
| last_insert_id() |
+------------------+
|                3 |
+------------------+

索引:

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

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

🌏唯一键

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

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

关于唯一键和主键的区别:

我们可以简单理解成,主键更多的是标识唯一性的 。而唯一键更多的是保证在业务上,不要和别的信息出现重复。乍一听好像没啥区别,我们举一个例子

下面的例子:我们选择了id作为主键 ,但是电话号码我也想保证唯一性,所以设置为唯一键!

sql 复制代码
mysql> create table student(
    -> id char(20) primary key,
    -> name varchar(20) not null,
    -> telphone char(20) unique
    -> );

在插入时的数据,不仅仅id要不同,telphone 也要保证其每个人不相同(唯一性)!

上手:

sql 复制代码
mysql> create table stu(
    -> id char(20) unique comment '这是学生的唯一键!',
    -> name varchar(32) not null
    -> );
    
mysql> desc stu;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | char(20)    | YES  | UNI | NULL    |       |
| name  | varchar(32) | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

紧接着尝试做插入!发现可以多次插入NULL,但是主键是不允许这样操作的!

sql 复制代码
mysql> insert into stu (id, name) values (NULL, '李四');
mysql> insert into stu (id, name) values (NULL, '李四');
mysql> insert into stu (id, name) values (NULL, '李四');

mysql> select * from stu;
+------+--------+
| id   | name   |
+------+--------+
| 1234 | 张三   |
| NULL | 李四   |
| NULL | 李四   |
| NULL | 李四   |
| NULL | 李四   |
+------+--------+

🌏外键

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

  • 从表和主表的关联关系
  • 产生外键约束!
sql 复制代码
foreign key (字段名) references 主表(列)

学生表是依附于班级表的,所以班级表是主表,学生表是从表

先创建主键表:

sql 复制代码
mysql> create table if not exists class(
    -> id int primary key,
    -> name varchar(32) not null
    -> );

再创建从表

sql 复制代码
mysql> create table student (
    -> id int unsigned primary key,
    -> name varchar(20) not null,
    -> telphone varchar(32) unique,
    -> class_id int,
    -> foreign key(class_id) references class(id)
    -> );

+----+-----------------+
| id | name            |
+----+-----------------+
|  1 | 人工智能        |
|  2 | 计算机技术      |
+----+-----------------+
  
+----------+------------------+------+-----+---------+-------+
| Field    | Type             | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+-------+
| id       | int(10) unsigned | NO   | PRI | NULL    |       |
| name     | varchar(20)      | NO   |     | NULL    |       |
| telphone | varchar(32)      | YES  | UNI | NULL    |       |
| class_id | int(11)          | YES  | MUL | NULL    |       |
+----------+------------------+------+-----+---------+-------+

正常插入数据

sql 复制代码
mysql> insert into student values(111, '张三', '123456', 1);✅️
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values(222, '李四', '12345678', 2);✅️
Query OK, 1 row affected (0.00 sec)

插入一个班级号为3的学生,因为没有这个班级,所以插入不成功

sql 复制代码
mysql> insert into student values(333, '王五', '1234567890', 3);❌️
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test_db`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))

插入班级id为null,比如来了一个学生,目前还没有分配班级

sql 复制代码
insert into student values(444, '刘', '12340', null);

此时我们尝试删去class中id为1的班级,删除失败,因为此班级里还有关联了几个学生

sql 复制代码
mysql> delete from class where id=1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test_db`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))

如何理解外键约束

🌏综合案例

商品表:

sql 复制代码
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 '供应商名称'
);

客户

sql 复制代码
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 '身份证'
);

购买表

sql 复制代码
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)
);

📢写在最后

接下来登场的是 基本查询

相关推荐
kiracrimson1 小时前
STL简单引入:把数据结构做成模板
开发语言·数据结构·c++
爱吃牛肉的大老虎1 小时前
Redis之分布式 Session问题
数据库·redis·分布式
Kapaseker1 小时前
为什么程序员始终无法彻底解决内存泄漏?
android·kotlin
guo_wen_qiang1 小时前
mongodb集群模式的原理
服务器·数据库·mongodb
captain3761 小时前
Spring Boot日志
java·数据库·spring boot
无足鸟ICT1 小时前
【RHCA+】yum命令(查询、安装、升级和卸载软件包)
linux
阿标在干嘛1 小时前
分布式锁的“场景适配“:我们为什么在部分链路中选择了数据库乐观锁?
数据库·分布式
jiidd1 小时前
关于SQL SERVICE数据库里换行问题
数据库·sql·oracle
kyle~1 小时前
数据结构 --- 无锁队列(Lock-Free Queue)
linux·数据结构·c++·多线程·并发