数据库概论之MySQL表的增删改查 - 进阶版本1

大家好,已经好久没更新了 , 学校的学业有点忙 , 没有额外的精力去进行更新了 , 假期开始了 , 我们也要开始努力了
数据库相对来说难度不是那么高,大家只要勤加练习、熟记语法,我相信学好数据库不是什么问题,博主会从0剖析,逐步讲解数据库的知识点,并且会举很多实例。最重要的是,博主不会采用软件,使用最原始的方式 -> 命令行来讲解,这样讲解的好处是逐个语句进行书写,不会造成读者思路跟不上的问题!
在这里插入图片描述

1、数据库约束

约束就是数据库在使用的时候,对于里面存储的数据提出的限制和要求,程序员可以通过约束来对数据进行更好的校验~

1.1 约束类型

约束类型说明
NOT NULL指示某列不能存储 NULL 值。
UNIQUE保证某列的每行必须有唯一的值。
DEFAULT规定没有给列赋值时的默认值。
PRIMARY KEYNOT NULLUNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
FOREIGN KEY保证一个表中的数据匹配另一个表中的值的参照完整性。
CHECK保证列中的值符合指定的条件。对于MySQL数据库,对CHECK子句进行分析,但是忽略CHECK子句。

以上这些约束,都是针对于某个列单独设置的。只是针对这个列来说是这样的,不同的列之间没有影响

1.2 NULL约束

NOT NULL:指示某列不能存储 NULL 值。 (数据不能为空)

正常情况下 , 一个表的每个字段是允许为空的

image-20220718221255639

当我们创建表的时候 , 指定某一列为 not null 的时候 , 我们就不能插入空值了

image-20220501112807064

当我们尝试插入空值的时候,就会报错
image-20220501112854658

NOT NULL还可以同时给多个列设置,不仅仅是一个列

image-20220501113844845

1.3 UNIQUE约束

UNIQUE :保证某列的每行必须有唯一的值。(数据唯一,插入的数据不能重复,是针对于列来说的)

默认情况下 , 表里面的数据是可以重复的

image-20220718222203680

当我们指定某一列为 unique 的话 , 插入重复的值就会报错

image-20220718222511496

那么当我们id都输入成1的时候,我们发现报错了。看一下错误信息:

ERROR 1062 (23000): Duplicate entry '1' for key 'id'

Duplicate:重复的

entry:条目(记录)

意思是:重复记录了值为1的id

我们之前就见过entryMap entrySet,此处的entry也是条目的意思。

Map里面存的是键值对,Map又没有实现Interable接口,如果想遍历一个Map就需要先把Map给转成SetSet里面的元素就叫做一个entry,就包含一个键和一个值

那么MySQL怎么发现新输入的数据是和之前的数据是重复的呢?其实这种这样的插入过程,是和之前学过的二叉树的插入过程是一样的,这里面的内容也和后面要讲的索引是相关联的。之后 , 我们再详细讲解 .

1.4 DEFAULT约束

DEFAULT :设定默认值。

默认的默认值是 NULL

image-20220501115150108

可以通过 default 约束来修改这里的默认值的取值 .

image-20220501115429753

我们仍然是只插入了id这一列,但是name使用的是我们的设置的默认值匿名人士

1.5 PRIMARY约束

PRIMARY KEYNOT NULLUNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。

主键约束:相当于数据的唯一身份标识,类似于身份证号、学号

  1. 不能为空 : 相当于 not null
  2. 不能重复 : 相当于 unique
  3. 一个表里面只能有一个主键

这个是我们日常开发最常用的约束!

创建表的时候,很多时候都需要指定主键~

image-20220501115901154

注意事项 :

  1. 对于一个表来说,只有一个列能被指定为主键

    image-20220501120015506

  2. 设定为主键的,既不能为空,也不能重复

    image-20220501120805343

  3. 对于主键,最典型的用法就是直接使用1、2、3、4递增的方法来进行表示

  4. 由于主键必须要填 , 而且还不能重复 . MySQL 为我们提供了一个叫自增主键的方式 , 帮助我们自动生成主键的值

    用法:在主键后面加上auto_increment

    image-20220501121045132

    当我们设定好主键之后,此时插入的记录就可以不指定自增主键的值了(直接使用null表示即可),交给MySQL自己分配即可

    image-20220501121334857

    自增主键也可以手动指定id,一旦指定之后,后续插入的数据的id就是从你指定的数字之后来排的

    image-20220501121950564

    MySQL 要想进行自增 , 就要记录下来当前的 id 已经记录到哪里了 . 而且还要保证 , 自增之后必须是不重复的.

    那么 MySQL 里面简单粗暴的方法 , 就是直接记录当前自增主键里面的最大值 , 这样就能保证自增主键速度快还不重复 .

    虽然浪费了一定的存储资源 , 但是好处还是很多的 .

    对于这个浪费 , 其实只有在数据库里面有海量的数据里面 , 才会影响到正常的使用 .

    正常情况 , 数据库里面是基本没有那么多的数据的 .

    那么自增主键是要保证生成的 id 是唯一的 , 不重复的 . 但是如果数据库是分布式部署的(分布式的意思就是数据量太大 , 一台机器存不下 , 需要用多台设备来存储数据) , 这个时候自增主键就有可能出现问题

    MySQL服务器很容易就知道自己当前主机上 id 的最大值 , 但是有多台机器的话 , 就没办法保证了

    image-20220718224306294

    举个栗子 : 结婚随份子 , 你有可能提前给新娘新郎了 , 但是没给写礼账的人 , 新郎新娘也忘记了告诉写礼账的人 , 这样他们三个的信息就没互通 , 是不知道他们三个之间的最大值的

    所以 , MySQL 生成自增主键在分布式系统上就难以满足需求了

    程序员们又想到了一个办法 :

    唯一id = 时间戳(ms) + 机房编号/机器编号 + 随机因子
    //这里面的 + 代表字符串拼接,不是算数运算
    

    未来我们见到的分布式唯一 id 算法 , 就长这个样子 .

    往数据库中不断插入数据 , 插入数据的时间不同 , 时间戳也就不同 .

    假如说 , 恰巧在同一时间插入的 , 我们还可以通过机房编号/机器编号来区分.

    最后 , 再加上随机因子 , 这样重复的概率非常非常小了

    那就真的一模一样恰巧了 , 那么我们怎么办?

    错误思路 :

    再去生成一次不就得了

    那么你怎么知道这次生成的唯一id之前没生成过 , 你不得需要把之前生成的唯一id存储起来 , 那么查找不又浪费时间了吗.

    正确思路 :

    就这么地了! 因为一模一趟的概率真的是太小太小了 , 几乎近似于0了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hDvK0Kpx-1670639701725)(https://jialebihaitao.oss-cn-beijing.aliyuncs.com/img/%E5%B0%8F%E7%8C%AB%E5%88%86%E9%9A%94%E7%AC%A6.png)]

注意:我们想要使用自增主键,就必须确保想操作的列必须是可以输入null的,即不能把这个列的约束设置成not null

image-20220501122518187

但是个人测试 , 这样子其实是可以的

image-20220718225449608

1.6 FOREIGN KEY外键约束

1.6.1 语法

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

外键约束其实就是针对两张表进行了关联。

外键约束,描述的就是两张表的两个列之间的"依赖关系",子表依赖于父表(子表引用自父表),要求子表当中对应记录得在父表当中存在

举个栗子:

image-20220501212957543

学生表依赖了班级表,那么我们就把学生表称为"子表",班级表称为"父表"

这种情况下就可以使用外键约束来描述这种关系

通过这个例子,我们需要知道:

每个学生都得有一个具体的班级,这个班级得存在!

如果学生表这里出现一个记录,班级id是1000,此时这样的数据就是非法的

来看一个例子:

首先,我们创建一个班级表,设定班级编号为主码约束。

image-20220501214136955

接下来构造一个学生表,来指定一下外键约束

create table student (studentId int primary key auto_increment,name varchar(20),classId int,foreign key(classId) references class(classId));
-- student表里面的classId 引用了 class表里面的classId列

image-20220501215151117

image-20220501215203996

我们接下来看两个表的结构

image-20220501215628967

我们可以来插入数据了

image-20220501215733078

此时我们可以发现,这条记录中指定的classId为1,在class表中存在的话,可以插入

在插入之前,MySQL会先拿着这个classIdclass表里面查询一下,看看是否存在,存在才插入成功

在父表为空的情况下 , 直接往子表里面插入数据是会报错的 .

image-20220718230548955

那么接下来,我们尝试插入一下classId为100的情况

image-20220501220012936

我们发现,插入失败~ 错误信息如下

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`sql0423`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`classId`) REFERENCES `class` (`classId`))

image-20220501220501614

这条记录,指定的classId为100,在class表中不存在,不能插入成功

那么我们再试两组数据

image-20220501220737287

这两次插入,classId2和3均在class表的classId列中存在,因此就能插入成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0m1DPVMH-1670639701729)(https://jialebihaitao.oss-cn-beijing.aliyuncs.com/%E5%B0%8F%E7%8C%AB%E5%88%86%E9%9A%94%E7%AC%A6.png)]

那么不仅仅是新增的时候要考虑到外键约束,新增成功的数据如果进行修改,也是会失败的

image-20220501221030114

注:a child row代表子表

修改成功的案例:

image-20220501221153322

student表中的classId是可以重复存在的,只要在class表里面存在即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZyCIY1Ec-1670639701729)(https://jialebihaitao.oss-cn-beijing.aliyuncs.com/%E5%8D%9A%E5%AE%A2%E5%88%86%E9%9A%94%E7%AC%A6-%E6%9C%88%E4%BA%AE.png)]

外键约束,同样也约束着父表,当父表中的某个记录被子表依赖着的时候,此时尝试进行修改或者删除,也会出现错误

image-20220501221644792

a parent row :父表

那么正确写法是这样的:

image-20220501221813602

目前班级1里面是没有同学的,即没有被子表依赖,就可以修改或者删除。

注意:直接删表也是不行的

image-20220501221927293

1.6.2 工作原理

在子表中插入新的记录的时候,就会先根据对应的值,在父表中先查询,查询到之后才能够执行后续的插入

那么这里的查询操作,可能是一个成本较高的操作(比较耗时) . 如果查询操作触发了遍历表 , 这是十分低效的 . 但是查询触发了索引 , 这就相对快不少 .

外键约束其实要求,父表中被依赖的这一列,必须要有索引,有了索引就能大大的提高查询速度

那么这个索引,就是class表的classId这一列,需要是primary key或者unique(有了这俩约束的列,就会自动创建出索引了)

那么我们想象一个场景:淘宝购物

image-20220502093433367

按照上面描述的外键约束的关系,就可以认为 商品表就是父表,订单表就是子表,那么订单表中的商品id需要在商品表的商品id当中存在

那么我今天买了个衣服,穿着感觉不错,一段时间之后,准备再次入手,却发现这个商品下架了(也就是被商品表中删除了)。

我们刚才不是说外键约束,一旦约定好了外键约束关系,此时父表当中有关联的数据不是不能随便删除吗?(如果某个记录被子表依赖了,此时就无法删除了。)

那这是怎么回事?

其实我们采取的办法,不是真正的删除,而是逻辑上的删除

我们可以给商品表加一个列isOk,那么如果这一列值为1,就代表这是一条有效记录,如果这一列值为0,就代表这是一个无效记录(相当于被删除了)

那么有一个问题,假如删除的话,这也不是真的删除啊。只不过是isOk这一列被存为了0 , 那不还是占据了硬盘空间的吗 ?

实际上,现在硬盘空间不值钱,存了就存了。其实目前人力成本是会更高的。

2、表的设计

所谓了"数据库设计"、"表的设计"其实就是根据实际的问题场景,把表给创建出来了

那么给你一个问题场景,如何设计数据库?如何设计表?

一个好方法就是:先找出这个数据库场景当中涉及到的"实体"(对象),再找出它们之间的关系

一个典型的场景:

学生管理系统:

  1. 表示学生的基本信息
  2. 表示班级的基本信息
  3. 表示学生学习的课程的基本信息

对于咱们找到的实体来说,就需要创建对应的表来表示相关信息(那么每个实体表,里面的基本信息以及需求都是很好确定的),但是很多时候实体和实体之间并不是孤立的,而是存在对应关系。那么这样的对应关系,也需要体现在表中(实体之间的关系,这个是隐含的,需要我们挖掘出来,这里实体之间的不同的关系,会对表的设计产生影响)

其实分析实体之间的关系就是造句子

2.1 一对一

举个栗子:

以学校的教务系统为例:

student表(学生的id,学生姓名,学生班级…)

user表(用户的账户,密码…)

那么造的句子就是:一个账户对应一个学生,一个学生也只有一个账户

那么在数据库中,怎么表示一对一的关系呢?

方法一:可以把这两个实体用一张表表示

student(id,name,class,account,password);

方法二:可以用两张表来表示,其中一张表就包含了令一个表的id

​ 根据这个关系,就可以随时找到某个账户对应的学生是谁,也能找到某个学生对应的账户

image-20220503195234662

2.2 一对多

还是以学校的教务系统为例:

student表(学号,姓名…)

class表(班级编号,班级名称…)

那么造的句子就是:一个学生应该处于一个班级中,一个班级可以包含多个学生~

在数据库中,表示一对多的关系,也有两种典型方案:

方法一:在班级表当中,新增一列,表示这个班级里面的学生id组合

image-20220503203814927

但是像Redis这样的数据库,就有数组类型,就可以考虑这种方法

方法二:班级表不变,在学生表中新增一列classId

image-20220503204207785

2.3 多对多

又是以学校的教务系统为例:

学生表(学号,姓名)

课程表(课程编号,课程名字)

学生和课程就是多对多的关系

那么造的句子就是:一个学生,可以选多个课程,一个课程同时也能包含多个学生(M个学生,可以选择N门课)

多对多的关系在数据库中只有一种办法

使用一个关联表,来表示两个实体之间的关系

image-20220503210504280

有的时候,为了更方便的表示/找到 实体之间的联系(尤其是比较复杂的场景),我们还可以画E-R图(其实不咋用),但是学校必考!

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加勒比海涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值