【MySQL-3】表的约束

目录

[1. 整体学习的思维导图](#1. 整体学习的思维导图)

[2. 非空约束](#2. 非空约束)

[3. default约束](#3. default约束)

[4. No Null和default约束](#4. No Null和default约束)

[5. 列描述 comment](#5. 列描述 comment)

[6. Zerofill](#6. Zerofill)

[7. 主键 primary key](#7. 主键 primary key)

复合主键

[8. 自增长 auto_increment](#8. 自增长 auto_increment)

[9. 唯一键](#9. 唯一键)

[10. 外键](#10. 外键)

[11. 实现综合案例](#11. 实现综合案例)


1. 整体学习的思维导图

2. 非空约束

正如该标题一样,这个约束作用于数据是否能为空值。

举例我们有一个Student表,表中含有:

  • 学生姓名name

  • 学生年龄age

  • 学生性别gender

sql 复制代码
mysql> create table if not exists Student( 
-> name varchar(10), 
-> age tinyint unsigned, 
-> gender char(1) );
Query OK, 0 rows affected (0.01 sec)

mysql> desc Student;
+--------+---------------------+------+-----+---------+-------+
| Field  | Type                | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| name   | varchar(10)         | YES  |     | NULL    |       |
| age    | tinyint(3) unsigned | YES  |     | NULL    |       |
| gender | char(1)             | YES  |     | NULL    |       |
+--------+---------------------+------+-----+---------+-------+

mysql> show create table Student\G;
*************************** 1. row ***************************
       Table: Student
Create Table: CREATE TABLE `Student` (
  `name` varchar(10) COLLATE utf8_bin DEFAULT NULL,
  `age` tinyint(3) unsigned DEFAULT NULL,
  `gender` char(1) COLLATE utf8_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

我们先进行测试插入:

  • 小明 18 男

  • Null 19 女

sql 复制代码
mysql> insert into Student(name, age, gender) values('小明', 18, '男');
Query OK, 1 row affected (0.00 sec)

mysql> insert into Student(name, age, gender) values(null, 19, '女');
Query OK, 1 row affected (0.00 sec)

mysql> select * from Student;
+--------+------+--------+
| name   | age  | gender |
+--------+------+--------+
| 小明   |   18 | 男     |
| NULL   |   19 | 女     |
+--------+------+--------+

我们可以看到,该表格在Null项是允许插入空的,这意味这我们可以不用填入姓名,但是为了表的统一性和规范性,我们要求某些项必须填入信息,这时候就需要使用非空约束了!

sql 复制代码
mysql> alter table Student modify name varchar(10) not null;
Query OK, 0 rows affected (0.01 sec)

mysql> desc Student;
+--------+---------------------+------+-----+---------+-------+
| Field  | Type                | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| name   | varchar(10)         | NO   |     | NULL    |       |
| age    | tinyint(3) unsigned | YES  |     | NULL    |       |
| gender | char(1)             | YES  |     | NULL    |       |
+--------+---------------------+------+-----+---------+-------+

我们再次测试插入:

  • 小明 18 男

  • Null 19 女

sql 复制代码
mysql> insert into Student(name, age, gender) values('小明', 18, '男');
Query OK, 1 row affected (0.00 sec)

mysql> insert into Student(name, age, gender) values(null, 19, '女');
ERROR 1048 (23000): Column 'name' cannot be null

mysql> select * from Student;
+--------+------+--------+
| name   | age  | gender |
+--------+------+--------+
| 小明   |   18 | 男     |
+--------+------+--------+

由此可见非空约束的作用是让表中某一个字段类型为必填项!

3. default约束

default约束是当我们没填任何信息时,表中的字段类型会根据之前设定好的default值进行使用!

sql 复制代码
mysql> create table tb10( name varchar(10) default '张三', age tinyint default 18 );
Query OK, 0 rows affected (0.01 sec)

mysql> desc tb10;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name  | varchar(10) | YES  |     | 张三    |       |
| age   | tinyint(4)  | YES  |     | 18      |       |
+-------+-------------+------+-----+---------+-------+

插入空值后,表中的数据会根据是否有default值进行填写。

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

mysql> insert into tb10 values();
Query OK, 1 row affected (0.00 sec)
mysql> select * from tb10;
+--------+------+
| name   | age  |
+--------+------+
| 张三   |   18 |
| 张三   |   18 |
+--------+------+

4. No Null和default约束

那么我们给一个字段类型同时加上No NULL和default会发生怎样的情况呢?

  • 非空约束要求我们必须填入数据,default约束可以在我们没填入的字段数据使用默认值

  • 非空+default可以使我们在不选择填入该字段类型时使用默认数据!

sql 复制代码
mysql> create table tb11(
    -> name varchar(10) not null default '张三',
    -> age tinyint unsigned);
Query OK, 0 rows affected (0.01 sec)

mysql> desc tb11;
+-------+---------------------+------+-----+---------+-------+
| Field | Type                | Null | Key | Default | Extra |
+-------+---------------------+------+-----+---------+-------+
| name  | varchar(10)         | NO   |     | 张三    |       |
| age   | tinyint(3) unsigned | YES  |     | NULL    |       |
+-------+---------------------+------+-----+---------+-------+
sql 复制代码
mysql> insert into tb11(name, age) values(null, 18);
ERROR 1048 (23000): Column 'name' cannot be null
mysql> insert into tb11(age) values(18);
Query OK, 1 row affected (0.00 sec)

mysql> select * from tb11;
+--------+------+
| name   | age  |
+--------+------+
| 张三   |   18 |
+--------+------+

5. 列描述 comment

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

sql 复制代码
mysql> create table student_from(
    -> name varchar(10) not null comment '学生的姓名',
    -> age tinyint unsigned comment '学生的年龄',
    -> id varchar(25) not null default '2023090640XXX' comment '学生的学号'
    -> );
    
mysql> show create table student_from\G;
*************************** 1. row ***************************
       Table: student_from
Create Table: CREATE TABLE `student_from` (
  `name` varchar(10) COLLATE utf8_bin NOT NULL COMMENT '学生的姓名',
  `age` tinyint(3) unsigned DEFAULT NULL COMMENT '学生的年龄',
  `id` varchar(25) COLLATE utf8_bin NOT NULL DEFAULT '2023090640XXX' COMMENT '学生的学号'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)

6. Zerofill

我们之前了解到跟在一个类型后面的数字都会表达一些特殊的含义,那么跟着int(num)的num真正的含义到底是什么呢,这就需要借助zerofill来观察了。

我们创建一个表,两个字段类型一个添加zerofill,一个不添加。

sql 复制代码
mysql> create table tb_int1(
    -> num1 int,
    -> num2 int zerofill
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> desc tb_int1;
+-------+---------------------------+------+-----+---------+-------+
| Field | Type                      | Null | Key | Default | Extra |
+-------+---------------------------+------+-----+---------+-------+
| num1  | int(11)                   | YES  |     | NULL    |       |
| num2  | int(10) unsigned zerofill | YES  |     | NULL    |       |
+-------+---------------------------+------+-----+---------+-------+

我们任意插入以下数据:

  • 10 10

  • 15 15

sql 复制代码
mysql> insert into tb_int1 values(10, 10);
Query OK, 1 row affected (0.01 sec)

mysql> insert into tb_int1 values(15, 15);
Query OK, 1 row affected (0.00 sec)

mysql> select * from tb_int1;
+------+------------+
| num1 | num2       |
+------+------------+
|   10 | 0000000010 |
|   15 | 0000000015 |
+------+------------+

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

sql 复制代码
mysql> select num1, hex(num2) from tb_int1;
+------+-----------+
| num1 | hex(num2) |
+------+-----------+
|   10 | A         |
|   15 | F         |
+------+-----------+

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

7. 主键 primary key

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

sql 复制代码
mysql> create table tb12(
    -> id int unsigned primary key comment '学生的id',
    -> name varchar(10) default '未知' comment '学生的姓名'
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> desc tb12;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id    | int(10) unsigned | NO   | PRI | NULL    |       |
| name  | varchar(10)      | YES  |     | 未知    |       |
+-------+------------------+------+-----+---------+-------+
sql 复制代码
mysql> insert into tb12(id, name) values(1, '欧阳');
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb12(id, name) values(1, '牛马');
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

复合主键

一个表中虽然只能有一个主键,但是一个主键却可以约束多个字段类型!

  • 追加主键
sql 复制代码
alter table 表名 add primary key(字段列表)  
  • 删除主键
sql 复制代码
alter table 表名 drop primary key;  
sql 复制代码
mysql> create table tb13(
    -> num1 int(1) primary key,
    -> num2 int(1)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> insert into tb13(num1, num2) values(1, 2);
Query OK, 1 row affected (0.00 sec)
mysql> insert into tb13(num1, num2) values(2, 2);
Query OK, 1 row affected (0.00 sec)

mysql> alter table tb13 drop primary key;
Query OK, 2 rows affected (0.03 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> alter table tb13 add primary key (num1, num2);
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc tb13;
+-------+--------+------+-----+---------+-------+
| Field | Type   | Null | Key | Default | Extra |
+-------+--------+------+-----+---------+-------+
| num1  | int(1) | NO   | PRI | NULL    |       |
| num2  | int(1) | NO   | PRI | NULL    |       |
+-------+--------+------+-----+---------+-------+

8. 自增长 auto_increment

我们平时注册qq时,输入完手机号和验证码,qq后端注册成功会给我们返回一个qq号,这个qq号就是自增长的产物,比如马总的qq10001,我的qq2331701342,根据注册用户的数量和注册时间分配qq号。

自增长的特点:

  • 任何一个字段要做自增长,前提是本身是一个索引(key一栏有值)

  • 自增长字段必须是整数

  • 一张表最多只能有一个自增长

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


mysql> desc tb14;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20)      | NO   |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+
sql 复制代码
mysql> insert into tb14(name) values('欧阳');
mysql> insert into tb14(name) values('牛马');
mysql> insert into tb14(name) values('张明东');
mysql> insert into tb14(name) values('鬼哥');

mysql> select * from tb14;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | 欧阳      |
|  2 | 牛马      |
|  3 | 张明东    |
|  4 | 鬼哥      |
+----+-----------+

我们可以看到id这一字段类型我们并没插入和干预,它自己增长。

假设我们自主干预插入 10 刘越,后面再次插入韩旭鹏/王星博那么他们的id号又从几号开始呢?

  • 是从5还是从11
sql 复制代码
mysql> insert into tb14(id,name) values(10, '刘越');
mysql> insert into tb14(name) values('韩旭鹏');
mysql> insert into tb14(name) values('王星博');

mysql> select * from tb14;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | 欧阳      |
|  2 | 牛马      |
|  3 | 张明东    |
|  4 | 鬼哥      |
| 10 | 刘越      |
| 11 | 韩旭鹏    |
| 12 | 王星博    |
+----+-----------+

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

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

9. 唯一键

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

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

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

我们可以简单理解成,主键更多的是标识表之间唯一性的。而唯一键更多的是保证在业务上一张表中不同字段,不要和别的字段信息出现重复。

sql 复制代码
unique key
sql 复制代码
mysql> create table tb15(
    -> id int unsigned unique key,
    -> name varchar(10)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> desc tb15;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id    | int(10) unsigned | YES  | UNI | NULL    |       |
| name  | varchar(10)      | YES  |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+
mysql> insert into tb15(id,name) values(1, '欧阳');
Query OK, 1 row affected (0.00 sec)

mysql> insert into tb15(id,name) values(1, '牛马');
ERROR 1062 (23000): Duplicate entry '1' for key 'id'    -- id唯一

mysql> insert into tb15(id,name) values(NULL, '牛马');
Query OK, 1 row affected (0.00 sec)                     -- 但是插入的id可以为NULL

mysql> select * from tb15;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 欧阳   |
| NULL | 牛马   |
+------+--------+

10. 外键

如果我们现在有两张表,一个学生表记录了学生的name,age,telphone,class_id,另外一张class表记录了学生的班级id,和班级名称。我们需要将这两张表的class_id和id关联起来,表面上我们可以通过插入时的判断进行关联,但是插入错误的也不会报错,这时候就需要约束进行强制管理了,这就是外键。

sql 复制代码
foreign key (字段名) references 主表(列)  
  • 外键用于定义主表和从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为null。
sql 复制代码
mysql> create table class(
    -> id varchar(10) primary key,
    -> class_name varchar(10) not null comment '班级名称'
    -> );

mysql> create table stu( 
    -> class_id varchar(10), 
    -> name varchar(20) not null comment '学生姓名', 
    -> age tinyint default 18, telphone varchar(15), 
    -> foreign key(class_id) references class(id));

mysql> desc class;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| id         | varchar(10) | NO   | PRI | NULL    |       |
| class_name | varchar(10) | NO   |     | NULL    |       |
+------------+-------------+------+-----+---------+-------+

mysql> desc stu;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| class_id | varchar(10) | YES  | MUL | NULL    |       |
| name     | varchar(20) | NO   |     | NULL    |       |
| age      | tinyint(4)  | YES  |     | 18      |       |
| telphone | varchar(15) | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+

mysql> insert into class(id, class_name) values(1, '软件工程001');
mysql> insert into class(id, class_name) values(2, '软件工程002');

mysql> select * from class;
+----+-----------------+
| id | class_name      |
+----+-----------------+
| 1  | 软件工程001     |
| 2  | 软件工程002     |
+----+-----------------+

mysql> insert into stu values(1, '欧阳', 18, '123456789');
mysql> insert into stu values(1, '牛马', 19, '123456780');
mysql> insert into stu values(2, '张明东', 20, '123456781');
Query OK, 1 row affected (0.00 sec)

mysql> insert into stu values(3, '卢智博', 8, '183456781');
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`Class`.`stu`, CONSTRAINT `stu_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))
-- 由于id和class_id的外键约束,并没有3

mysql> select * from stu;
+----------+-----------+------+-----------+
| class_id | name      | age  | telphone  |
+----------+-----------+------+-----------+
| 1        | 欧阳      |   18 | 123456789 |
| 1        | 牛马      |   19 | 123456780 |
| 2        | 张明东    |   20 | 123456781 |
+----------+-----------+------+-----------+

11. 实现综合案例

有一个商店的数据,记录客户及购物情况,有以下三个表组成:

  • 商品goods(商品编号goods_id,商品名goods_name, 单价unitprice, 商品类别category, 供应商provider)

  • 客户customer(客户号customer_id,姓名name,住址address,邮箱email,性别sex,身份证card_id)

  • 购买purchase(购买订单号order_id,客户号customer_id,商品号goods_id,购买数量nums)

    • 要求:

      • 每个表的主外键

      • 客户的姓名不能为空值邮箱不能重复

      • 客户的性别(男,女)

sql 复制代码
mysql> create table if not exists goods(
    -> goods_id int unsigned not null unique auto_increment comment '商品编号',
    -> goods_name varchar(30) comment '商品名',
    -> unitprice float(10,2) comment '单价',
    -> category varchar(25) comment '商品类别',
    -> provider varchar(30) comment '供应商'
    -> );


mysql> desc goods;
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| goods_id   | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| goods_name | varchar(30)      | YES  |     | NULL    |                |
| unitprice  | float(10,2)      | YES  |     | NULL    |                |
| category   | varchar(25)      | YES  |     | NULL    |                |
| provider   | varchar(30)      | YES  |     | NULL    |                |
+------------+------------------+------+-----+---------+----------------+


mysql> create table if not exists customer(
    -> customer_id int unsigned primary key auto_increment comment '客户号',
    -> name varchar(30) not null comment '姓名',
    -> address varchar(30) comment '住址',
    -> email varchar(25) unique comment '邮箱',
    -> sex enum('男', '女') comment '性别',
    -> card_id varchar(30) unique comment '身份证'
    -> );


mysql> desc customer;
+-------------+-------------------+------+-----+---------+----------------+
| Field       | Type              | Null | Key | Default | Extra          |
+-------------+-------------------+------+-----+---------+----------------+
| customer_id | int(10) unsigned  | NO   | PRI | NULL    | auto_increment |
| name        | varchar(30)       | NO   |     | NULL    |                |
| address     | varchar(30)       | YES  |     | NULL    |                |
| email       | varchar(25)       | YES  | UNI | NULL    |                |
| sex         | enum('男','女')   | YES  |     | NULL    |                |
| card_id     | varchar(30)       | YES  | UNI | NULL    |                |
+-------------+-------------------+------+-----+---------+----------------+


create table purchase( 
    -> order_id int unsigned not null unique key, 
    -> customer_id varchar(5), 
    -> goods_id varchar(5), 
    -> nums int unsigned default 0 , 
    -> foreign key(customer_id) references customer(customer_id), 
    -> foreign key(goods_id) references goods(goods_id)
);

mysql> desc purchase;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| order_id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| customer_id | varchar(5)       | YES  |     | NULL    |                |
| goods_id    | varchar(5)       | YES  |     | NULL    |                |
| nums        | int(10) unsigned | YES  |     | 0       |                |
+-------------+------------------+------+-----+---------+----------------+
相关推荐
阿巴斯甜2 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker3 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95274 小时前
Andorid Google 登录接入文档
android
黄林晴5 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab18 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿20 小时前
Android MediaPlayer 笔记
android
Jony_21 小时前
Android 启动优化方案
android
阿巴斯甜21 小时前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇21 小时前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android