数据库的约束和主键

前言❤️❤️

hello hello💕,这里是洋不写bug~😄,欢迎大家点赞👍👍,关注😍😍,收藏🌹🌹

日常使用数据库时,很容易不小心存入错误数据(比如给 "年龄" 列存负数、给 "手机号" 列存非数字)

数据库约束就是在创建表时,为列(甚至整张表)设置的规则限制,它能从源头拦截不符合规则的数据写入 ,一旦尝试存错误数据,数据库就会直接报错并拒绝操作,这部分知识是数据库的一个重点,铁汁们在学习时要多练习🐵

🎇个人主页:洋不写bug的博客
🎇所属专栏:数据库
🎇mysql8.0和navicate的安装:mysql安装教程
🎇铁汁们对于MySQL数据库的各种常用核心语法,都可以在上面的数据库专栏学习,专栏正在持续更新中🐵🐵,有问题可以写在评论区或者私信我哦~

1,约束简介

有一个学生信息的数据库,这个数据库里面有很多表,比如成绩表,发奖学金的表,还有体测表等等,那这些表之间就是通过学号关联在一起的(因为名字可能会有重复,而学号是不会的)
那这时候如果在成绩表中存学号时,手抖打错了,那就可能出现两个人学号一样的情况,那这时候不仅成绩表中一个人的数据本身是错误的,而且表之间的关联关系也是错误的(根据学号查两个人的体测成绩,那么一个人的就是错的)
为了避免这个问题,就需要使用数据库约束,例如在定义表的时候就约束学号不能两个人的相同,让数据库自动帮我们去检查

还可以对表中的数据进行很多约束,不只是唯一性,还可以约束非空,限制值等等,如下图:

2,NOT NULL 非空约束

也就是约束这列的值不能为空,我们如果能预测某个列的值是不可能为空的,比如学号,年龄,那非常建议定义表时对这列加上非空约束,这样能提高查询效率

例如创建一个学生表,定义名字不为null,尝试插入null,就会报错,提示name不能为null

sql 复制代码
CREATE table student(
    id BIGINT,
    `name` VARCHAR(32) not null
)

3,UNIQUE 唯一约束

约束一列中不能出现两个重复的数据,例如身份证号,学号之类的

跟NOT NULL用法类似,这里再创建一个叫student1的表,给id列加上UNIQUE约束,在表中加一个id为1的学生

sql 复制代码
CREATE table student1(
    id BIGINT UNIQUE,
    `name` VARCHAR(32) not null
)
INSERT INTO student1(id,`name`) VALUES(1,"mark");

当我们尝试插入一个学号相同的学生时,就会报错,原因是键'student1.id' 存在重复条目 '1'(这里的key就是键的意思)

但是如果这样写,明明id重复了,都是null,却不会报错:

查询表,就会发现这些数据确实是插入了进去,这是因为null不在unique的判断范围内

4,PRIMARY KEY 主键约束

主键的4大特征:

  1. 主键约束唯一标识数据库表中的每条记录。(也就是唯一标识数据行的身份信息)
  2. 主键必须包含唯一的值,且不能包含 NULL 值。
  3. 每个表只能有一个主键,可以由单个列或多个列组成。
  4. 通常为每张表都指定一个主键,主键列建议使用 BIGINT 类型(因为这样数据表就算有很多的行,也能进行区分)
sql 复制代码
CREATE TABLE student(
    id BIGINT primary key,
    name varchar(20) not null
)

创建一个学生表,把id作为这个表的主键,查看下表的结构,就会发现这里以及标注id是主键

按照我们前面说的主键的特性,这个id就不能是空,也不能重复,先插入一行正常数据,再进行测试。再插入重复数据,就会报错

sql 复制代码
INSERT INTO student(id,`name`) VALUES(1,"mark");

插入空值,也会报错

sql 复制代码
INSERT INTO student(id,`name`) VALUES(NULL,"carrier");

那铁汁们想一下,如果这样写,跟写成主键,是一样的吗???

sql 复制代码
CREATE TABLE student(
    id BIGINT NOT NULL UNIQUE,
    name varchar(20) not null
)

其实是不一样的,我们看前面对定义为主键的表的结构的打印,主键是有特殊的标记的,而不只是非空且唯一,前面说了4条主键的特性,非空且唯一只是其中的一个(如下图)

写非空且唯一仅仅是对这列数据的需求是这样

主键是数据表的一个特性,数据如何组织就是依赖主键的,这个了解一下即可

在表中既然插入主键重复值就会报错,那要是有个学生表,以id作为主键,我们后面可能会更新一个学生的数据,但是不确定这个学生原来在表里面是否存在,就可以提前进行预测,如果主键不冲突就正常插入,主键冲突的话就更新数据: (这个也不是很常用,大家了解下即可)

sql 复制代码
CREATE TABLE student(
    id BIGINT primary key,
    `name` varchar(20) not null,
    num BIGINT
)

INSERT INTO student(id,`name`,num) VALUES(1,"marrie",10001);
sql 复制代码
INSERT INTO student(id,`name`,num) VALUES(1,"marrie",10100) 
on DUPLICATE key UPDATE name = "marrie",num = 10100;

查询看下,这行数据已经被成功更新了

5,主键自增

在定义主键时往往把主键列设置为自动增长,让数据库去维护主键列 ,这样就比较方便

把前面的student表删一下,再重新写个表,在主键后面加个auto_increment(auto是自动的意思,increment是增加的意思,合起来就是自增)
插入三条数据,只插入name,不管id

sql 复制代码
drop table student;
sql 复制代码
CREATE TABLE student(
    id BIGINT primary key AUTO_INCREMENT,
    name varchar(20) not null
)
sql 复制代码
INSERT INTO student(`name`) VALUES("carrie");
INSERT INTO student(`name`) VALUES("helena");
INSERT INTO student(`name`) VALUES("helly");

看下表结构,就会发现数据库会帮我们去维护这个主键列

有个特殊情况,就是当我们定义主键列自增后,再给主键列插入null,并不会报错,数据库会将 NULL 解读为「请求自动生成值」,这样既避免了手动指定值的麻烦,又保证了主键的非空性和唯一性

sql 复制代码
INSERT INTO student(id,`name`) VALUES(NULL,"mark");

主键是这样的:不指定id时,数据库会帮我们自动管理,指定了就按指定的
那如果手动插入了一个id为100的数据,那下次插入数据再让数据库自动管理,那插入的是101,还是5呢?

sql 复制代码
INSERT INTO student(id,`name`) VALUES(100,"oven");

这里插入一个数据看一下,就会发现是从101开始了 :

为了搞清楚这个问题,再指定插入一个id为5的这样一行数据,如果接下来不指定id插入数据,数据库自动管理,那插入的id是6,还是102呢?

再插条数据试下,会发现id其实是从102开始的

如果在mysql命令行工具里写上show create table student;(在NAVICAT中写结构显示很简单,基本上看不出来什么)

就会出现这个表的具体结构,如果我们使用主键自增的话,那这里就会显示下一次插入时的自增值

这个自增值只能增加,不能回退 ,因为回退后如果新插入数据的话,就有可能会把以前的数据给覆盖掉(也可以理解成自增值就是当前表中id最大值的下一位

6,复合主键

前面提到,每个列只能有一个主键

如下,定义一个表时,同时两个列作为主键,就会报错:定义了重复的主键

那如果现在有个订货数据表,有订单编号,供应商编号,还有这批货的价格

不同供货商的订单编号存在相同,不能当作主键,价格由订单编号和供货商编号共同决定
这里就可以把这两列组合起来,共同成为一个主键,具体这样写:

sql 复制代码
CREATE TABLE `order`(
    order_number BIGINT,
    vendor_id BIGINT,
    price DOUBLE,
    PRIMARY KEY(order_number,vendor_id)
)

插入一条数据

sql 复制代码
INSERT INTO `order`(order_number,vendor_id,price) VALUES(22,3,999);

接着无论插入订单编号相同,供货商编号不同,还是插入供货商编号相同,订单编号不同的数据,都不会报错(如下图)
因为主键是两者组合在一起的,这样并没有重复。

但是插入一个订单编号和供货商编号都跟原来相同的数据,就报错了

仔细看下报错信息,就会发现:这里的主键是两列组合在一起的(22-3)

那复合主键是一个列为null就报错,还是两个列都为null才报错呢,再来测试一下:

结果是一个列为null就会报错,也就是复合主键下的两个列都不能为null

7,外键约束

外键约束用于定义主表和从表的关系

下面两个表,哪个是主表,哪个是从表呢?

这里博主有个比较好用的方法,可以通过看表的依赖关系来判断,也就是假设一个表没有了,另一个表中的记录是否还有效。

这里学生表没有了,班级表中记录的数据仍然都有效,但是如果班级表没了,那学生表中记录班级的数据就无效了,因为无法确定这些学生是哪个班级的。

因此,学生表是依赖班级表的,学生表就是从表,班级表就是主表
这里有的铁汁会有疑问,那直接在学生表直接写上每个学生的班级就可以了,为什么还要这么麻烦,搞两个表。

假设学生表里有1000个学生,有400个报的是唱歌班,假设后面唱歌班的名字改为了合唱班,如果只用一个表的话,那就要改400个数据;而用两个表,就只需要改1个数据,在班级表里把唱歌班改为合唱班就可以了。
因此设计主从表在一些修改场景下修改会方便很多。

再把之前创建的student表删了,创建student表(从表)和class表(主表),实现主从表的关系

sql 复制代码
CREATE table class(
    id BIGINT PRIMARY KEY,
    name VARCHAR(20) not NULL
)


CREATE table student(
    id BIGINT PRIMARY KEY auto_increment,
    name varchar(20) not NULL,
    gender VARCHAR(5) not NULL,
    class_id BIGINT,
    foreign key (class_id) REFERENCES class(id)
)

这里要在从表的最后选择哪个列创建为外键,还要加上外键在主表中的引用

先把class(班级表)的数据写进去:

sql 复制代码
INSERT INTO class(id,name) VALUES(1,"美容班"),(2,"厨师班"),
(3,"武术班"),(4,"唱歌班"),(5,"跳舞班");

这里尝试往student表中写入一个班级编号为6的学生数据,这个班级编号在主表里没有对应,那就会报错

仔细看下报错信息,这个报错就是外键约束冲突错误:

接下来往student表里插入一些数据

sql 复制代码
INSERT INTO student(id,name,gender,class_id) 
VALUES(1,"懒羊羊","男",1),(3,"沸羊羊","男",2),(4,"美羊羊","男",3);

这时候student表是依赖class表的,那这时候如果尝试删除class表中的数据,因为有外键的校验,也会报错:

当然,尝试删除整个class表也不行:

但是class表中id为5这一行的数据没有来自从表的引用,因此可以正常删除

删除主表时如果从表中有对主表记录的引用,则不允许删除主表中对应的内容。如果必须要删除主表,那就要先删除从表中的数据或外键关系。

这里先删除下从表,主表的数据就可以删除了

sql 复制代码
drop table student;

不想删student表的话,也可以这样写,解除外键关系(这个外键名是自动生成的,在前面的报错信息那里就能看到),这个也是很少使用,大家了解下即可

sql 复制代码
ALTER TABLE student drop FOREIGN key student_ibfk_1;

注:我们在工作中在数据库层面不创建外键,因为数据库的数据量太大了,而是在Java层面处理关联关系,保证数据正确,校验完了再入库。

8, DEFAULT默认值约束

这个其实特别简单,意思就是我们没有给某列设置值,就会填入默认值
这里创建一个student表,如果我们在插入数据时没有设置年龄值,那这里年龄就是null

sql 复制代码
CREATE table student(
   id bigint,
   name varchar(20) not null,
   age int
);

INSERT INTO student(id,name) VALUES(1,"刘备");

删除一下student表,创建时加上默认值约束(default后面加上数字18),这时候没有设置年龄值时年龄就会设为默认值,如下图:

sql 复制代码
drop table student;

CREATE table student(
   id bigint,
   name varchar(20) not null,
   age int DEFAULT 18
);

INSERT INTO student(id,name) VALUES(1,"刘备");

10,CHECK 约束

check约束可以应用于一个列或者多个列,用于限制列中的数据,从而保证数据的完整性和准确性

在mysql8.0.16版本后才开始支持check约束,之前的版本并不支持,但是写上也不会报错
重构一下学生表,加上check约束:

sql 复制代码
CREATE table student(
   id bigint PRIMARY KEY AUTO_INCREMENT,
   name varchar(20) not null,
   age int DEFAULT(18),
   gender char(1),
   check(age >= 16),
   check(gender = '男' or gender = '女')
);

接着插入不满足约束的数据尝试下,就会报错(这里的student_chk_1就是自动生成的约束名)


还可以在定义列名时就进行限制,也可以限制列跟列之间的关系,示例如下(这里的<>代表不等于)

sql 复制代码
create table t_check(
   c1 int check(c1 <> 0),
   c2 int check(c2 > 0),
   c3 int,
   CHECK(c3 >= c2)
);



11,总结

在工作中,使用频率最高的三个就是 NOT NULL非空约束、UNIQUE唯一约束、PRIMARY KEY主键约束,使用这三个约束不仅能保证数据的准确性和完整性,还可以有效的提升查询效率
CHECK约束一般就直接在Java程序(C++)程序中实现了,不写在sql语句中
FOREIGN KEY外键约束比较影响查询效率,在大型互联网项目中很少使用

相关推荐
你想考研啊2 小时前
sqlserver新建用户和表
数据库
码农水水2 小时前
京东Java面试被问:分布式会话的一致性和容灾方案
java·开发语言·数据库·分布式·mysql·面试·职场和发展
雪碧聊技术2 小时前
DBeaver连接sql server数据库时,提示驱动版本不合适
数据库·sqlserver·dbeaver·更换驱动·修改java.security
Tansmjs2 小时前
使用Pandas进行数据分析:从数据清洗到可视化
jvm·数据库·python
是三好2 小时前
redis
数据库·redis·缓存
indexsunny2 小时前
互联网大厂Java求职面试实录:Spring Boot微服务在电商场景中的应用及技术深度解析
java·数据库·spring boot·缓存·微服务·面试·电商
無森~2 小时前
HBase搭建
大数据·数据库·hbase
IT邦德2 小时前
2026年Oracle Q1季度补丁,深度解析
数据库·oracle
warton883 小时前
ubuntu24实现单节点mysql mgr配置
数据库·mysql