11.范式与反范式设计

范式

1.问题

MySQL的库表设计,在很多时候我们都是率性而为,往往在前期的设计中考虑不全面,同时对于库表结构的划分也并不明确,所以很多时候在开发过程中,代码敲着敲着会去重构某张表结构,甚至大面积重构多张表结构,这种随心所欲的设计方式,无疑给开发造成了很大困扰

2.介绍

实际上在设计DB库表结构时,也有一些共同需要遵守的规范,这些规范在数据库设计中称为范式,理解并掌握这些设计时的规范,能让咱们在项目之初,设计的库表结构更为合理且优雅。数据库范式中,声明远扬的有三大范式,但除此之外也有一些其他设计规范

范式(Normal Form)它就是指设计数据库时要遵守的一些原则

3.异常

介绍

不符合范式的关系,会产生很多异常

表数据示例

总览

  • 冗余数据:例如 学生-2 出现了两次
  • 修改异常: 修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改
  • 删除异常: 删除一个信息,那么也会丢失其它信息。例如删除了 课程-1 需要删除第一行和第三行,那么 学生-1 的信息就会丢失
  • 插入异常: 例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入

4.范式总览

5.数据库三大范式

介绍

三大范式之间,它们是递进的关系,也就是后续的范式都基于前一个范式的基础上推行

第一范式(1NF)

介绍

所有属性都不可再分,即数据项不可分

不符合的案例
SELECT * FROM `zz_student`;
+----------------------+--------+-------+
| student              | course | score |
+----------------------+--------+-------+
| 竹子,男,185cm      | 语文   |    95 |
| 竹子,男,185cm      | 数学   |   100 |
| 竹子,男,185cm      | 英语   |    88 |
| 熊猫,女,170cm      | 语文   |    99 |
| 熊猫,女,170cm      | 数学   |    90 |
| 熊猫,女,170cm      | 英语   |    95 |
+----------------------+--------+-------+

在上述的学生表中,其中有一个student学生列,这一列存储的数据则明显不符合第一范式:原子性的规定,因为这一列的数据还可以再拆分为姓名、性别、身高三项数据

修改后的案例

因此为了符合第一范式,应该将表结构更改为:

+--------------+-------------+----------------+--------+-------+
| student_name | student_sex | student_height | course | score |
+--------------+-------------+----------------+--------+-------+
| 竹子         | 男          | 185cm          | 语文   |    95 |
| 竹子         | 男          | 185cm          | 数学   |   100 |
| 竹子         | 男          | 185cm          | 英语   |    88 |
| 熊猫         | 女          | 170cm          | 语文   |    99 |
| 熊猫         | 女          | 170cm          | 数学   |    90 |
| 熊猫         | 女          | 170cm          | 英语   |    95 |
+--------------+-------------+----------------+--------+-------+

将student这一列数据,分别拆分为姓名、性别、身高三列,然后分别存储对应的数据才合理,通过这样的优化后,此时zz_student这张表则符合了数据库设计的第一范式

不满足第一范式会带来的问题
  • 客户端语言和表之间无法很好的生成映射关系
  • 查询到数据后,需要处理数据时,还需要对student字段进行额外拆分
  • 插入数据时,对于第一个字段的值还需要先拼装后才能进行写入

第二范式(2NF)

介绍

要求表中的所有列,其数据都必须依赖于主键

不符合的案例
+--------------+-------------+----------------+--------+-------+
| student_name | student_sex | student_height | course | score |
+--------------+-------------+----------------+--------+-------+
| 竹子         | 男          | 185cm          | 语文   |    95 |
| 竹子         | 男          | 185cm          | 数学   |   100 |
| 竹子         | 男          | 185cm          | 英语   |    88 |
| 熊猫         | 女          | 170cm          | 语文   |    99 |
| 熊猫         | 女          | 170cm          | 数学   |    90 |
| 熊猫         | 女          | 170cm          | 英语   |    95 |
+--------------+-------------+----------------+--------+-------+

虽然此时已经满足了第一范式,但此刻观察course与score这两类列数据,并不依赖与主键,同时由于这样的结构,导致前面几列的数据出现了大量冗余

修改后的案例

此时可以再次拆分一下表结构

SELECT * FROM `zz_student`;
+------------+--------+------+--------+--------------+--------------+
| student_id | name   | sex  | height | department   | dean         |
+------------+--------+------+--------+--------------+--------------+
|          1 | 竹子   | 男   | 185cm  | 计算机系     | 竹子老大     |
|          2 | 熊猫   | 女   | 170cm  | 金融系       | 熊猫老大     |
+------------+--------+------+--------+--------------+--------------+

SELECT * FROM `zz_course`;
+-----------+-------------+
| course_id | course_name |
+-----------+-------------+
|         1 | 语文        |
|         2 | 数学        |
|         3 | 英语        |
+-----------+-------------+

SELECT * FROM `zz_score`;
+----------+------------+-----------+-------+
| score_id | student_id | course_id | score |
+----------+------------+-----------+-------+
|        1 |          1 |         1 |    95 |
|        2 |          1 |         2 |   100 |
|        3 |          1 |         3 |    88 |
|        4 |          2 |         1 |    99 |
|        5 |          2 |         2 |    90 |
|        6 |          2 |         3 |    95 |
+----------+------------+-----------+-------+

经过上述结构优化后,之前的一张表此时被我们拆分成学生表,课程表,成绩表三张,每张表中的id字段作为主键,其他字段都依赖于这个主键。无论在那张表中,都可以通过id主键确定其他字段的信息

此时再将目光看到先后两张学生表,原本的学生表有六条学生记录,其中有四条是冗余数据,此时的学生表则只有两条数据,同时这张学生表中只存储学生信息相关的数据。经过本次结构优化后,每张表的业务属性都具备唯一性,也就是每张表都只会描述了一件事情,不会存在一张表中会出现两个业务属性

第三范式(3NF)

介绍

要求表中每一列数据不能与主键之外的字段有直接关系

不符合的案例
+------------+--------+------+--------+--------------+--------------+
| student_id | name   | sex  | height | department   | dean         |
+------------+--------+------+--------+--------------+--------------+
|          1 | 竹子   | 男   | 185cm  | 计算机系     | 竹子老大     |
|          2 | 熊猫   | 女   | 170cm  | 金融系       | 熊猫老大     |
+------------+--------+------+--------+--------------+--------------+

比如这张学生表,目前即符合第一范式,也符合第二范式,但看最后的两个字段,department表示当前学生所属的院校,dean则表示这个院系的院长是谁。一般来说,一个学生的院长是谁,首先是取决于学生所在的院系的,因此最后的dean字段明显与department字段存在依赖关系,因此需要进一步调整表结构

修改后的案例
SELECT * FROM `department`;
+---------------+-----------------+-----------------+
| department_id | department_name | department_dean |
+---------------+-----------------+-----------------+
|             1 | 计算机系        | 竹子老大        |
|             2 | 金融系          | 熊猫老大        |
+---------------+-----------------+-----------------+

SELECT * FROM `zz_student`;
+------------+--------+------+--------+---------------+
| student_id | name   | sex  | height | department_id |
+------------+--------+------+--------+---------------+
|          1 | 竹子   | 男   | 185cm  |             1 |
|          2 | 熊猫   | 女   | 170cm  |             2 |
+------------+--------+------+--------+---------------+

经过进一步的结构优化后,又将原本的学生表拆为了院系表、学生表两张,学生表中则是只存储一个院系ID,由院系表存储院系相关的所有数据。至此,学生表中的每个非主键字段与其他非主键字段之间,都是相互独立的,之间不会再存在任何依赖性,所有的字段都依赖于主键

不满足第三范式会带来的问题
  • 当一个院系的院长换人后,需要同时修改学生表中的多条数据
  • 当一个院长离职后,需要删除该院长的记录,会同时删除多条学生信息

三范式总结

  • 第一范式:确保原子性,表中每一个列数据都必须是不可再分的字段
  • 第二范式:确保唯一性,每张表都只描述一种业务属性,一张表只描述一件事
  • 第三范式:确保独立性,表中除主键外,每个字段之间不存在任何依赖,都是独立的

6.巴斯-科德范式与四五范式

巴斯-科德范式

基础知识

一般在一张表中,可以用于区分每行数据的一个列,通常会被咱们设为主键,例如常用的ID字段就是如此,这类主键通常被称为单一主键,即一个列组成的主键。但除此之外,还有一个联合主键的概念,也就是由多个列组成的主键

介绍

巴斯-科德范式也被称为3.5NF,至于为何不称为第四范式,这主要是由于它是第三范式的补充版,第三范式的要求是:任何非主键字段不能与其他非主键字段间存在依赖关系,也就是要求每个非主键字段之间要具备独立性。而巴斯-科德范式在第三范式的基础上,进一步要求:任何主属性不能对其他主键子集存在依赖

说白了就是,规定了联合主键中的某列值,不能与联合主键中的其他列存在依赖关系

不符合的案例
+-------------------+---------------+--------+------+--------+
| classes           | class_adviser | name   | sex  | height |
+-------------------+---------------+--------+------+--------+
| 计算机-2201班     | 熊竹老师      | 竹子   | 男   | 185cm  |
| 金融-2201班       | 竹熊老师      | 熊猫   | 女   | 170cm  |
| 计算机-2201班     | 熊竹老师      | 子竹   | 男   | 180cm  |
+-------------------+---------------+--------+------+--------+

例如这张学生表,此时假设以classes班级字段、class_adviser班主任字段、name学生姓名字段,组合成一个联合主键,在这里我们可以通过联合主键,确定学生表中任何一个学生的信息

班主任字段其实也依赖于班级字段

修改后的案例
SELECT * FROM `zz_classes`;
+------------+-------------------+---------------+
| classes_id | classes_name      | class_adviser |
+------------+-------------------+---------------+
|          1 | 计算机-2201班     | 熊竹老师      |
|          2 | 金融-2201班       | 竹熊老师      |
+------------+-------------------+---------------+

SELECT * FROM `zz_student`;
+------------+--------+------+--------+
| classes_id | name   | sex  | height |
+------------+--------+------+--------+
|          1 | 竹子   | 男   | 185cm  |
|          2 | 熊猫   | 女   | 170cm  |
|          1 | 子竹   | 男   | 180cm  |
+------------+--------+------+--------+

经过结构调整后,原本的学生表则又被拆为了班级表、学生表两张,在学生表中只存储班级ID,然后使用classes_id班级ID和name学生姓名两个字段作为联合主键

不满足巴斯-科德范式会带来的问题
  • 当一个班级的班主任老师换人后,需要同时修改学生表中的多条数据
  • 当一个班主任老师离职后,需要删除该老师的记录,会同时删除多条学生信息
  • 想要增加一个班级时,同时必须添加学生姓名数据,因为主键不允许为空

第四范式(4NF)

介绍

第四范式是基于BC范式之上的

一个表中至少需要有三个独立的字段才会出现多值依赖问题,多值依赖是指表中的字段之间存在一对多的关系,也就是一个字段的具体值会由多个字段来决定

不符合的案例
SELECT * FROM `zz_user_role_permission`;
+-----------+----------+-------+------------+
| user_name | user_sex | role  | permission |
+-----------+----------+-------+------------+
| 竹子      | 男       | ROOT  | *          |
| 熊猫      | 女       | ADMIN | BACKSTAGE  |
| 竹子      | 男       | ADMIN | BACKSTAGE  |
| 熊猫      | 女       | USER  | LOGIN      |
| 竹子      | 男       | USER  | LOGIN      |
| 子竹      | 男       | USER  | LOGIN      |
+-----------+----------+-------+------------+

上述是一个经典的业务,也就是一张用户角色权限表,先简单介绍一下表中各字段的信息:

满足第三范式,但对于BC范式仅是勉强满足,因为「用户、角色、权限」之间存在一些依赖关系,不过这里先不管,毕竟是举例说明,因此假设是满足BC范式

多值依赖

此时假设我们需要新增一条数据,那表中的权限字段究竟填什么?这个值是需要依赖多个字段决定的,权限来自于角色,而角色则来自于用户。也就是说,一个用户可以拥有多个角色,同时一个角色可以拥有多个权限,所以此时咱们无法单独根据用户名去确定权限值,权限值必须依赖用户、角色两个字段来决定,这种一个字段的值取决于多个字段才能确定的情况,就被称为多值依赖

修改后的案例
SELECT * FROM `zz_users`;
+---------+-----------+----------+----------+---------------------+
| user_id | user_name | user_sex | password | register_time       |
+---------+-----------+----------+----------+---------------------+
|       1 | 熊猫      | 女       | 6666     | 2022-08-14 15:22:01 |
|       2 | 竹子      | 男       | 1234     | 2022-09-14 16:17:44 |
|       3 | 子竹      | 男       | 4321     | 2022-09-16 07:42:21 |
+---------+-----------+----------+----------+---------------------+

SELECT * FROM `zz_roles`;
+---------+-----------+---------------------+
| role_id | role_name | created_time        |
+---------+-----------+---------------------+
|       1 | ROOT      | 2022-08-14 15:12:00 |
|       2 | ADMIN     | 2022-08-14 15:12:00 |
|       3 | USER      | 2022-08-14 15:12:00 |
+---------+-----------+---------------------+

SELECT * FROM `zz_permissions`;
+---------------+-----------------+---------------------+
| permission_id | permission_name | created_time        |
+---------------+-----------------+---------------------+
|             1 | *               | 2022-08-14 15:12:00 |
|             2 | BACKSTAGE       | 2022-08-14 15:12:00 |
|             3 | LOGIN           | 2022-08-14 15:12:00 |
+---------------+-----------------+---------------------+

SELECT * FROM `zz_users_roles`;
+----+---------+---------+
| id | user_id | role_id |
+----+---------+---------+
|  1 |       1 |       1 |
|  2 |       1 |       2 |
|  3 |       1 |       3 |
|  4 |       2 |       2 |
|  5 |       2 |       3 |
|  6 |       3 |       3 |
+----+---------+---------+

SELECT * FROM `zz_roles_permissions`;
+----+---------+---------------+
| id | role_id | permission_id |
+----+---------+---------------+
|  1 |       1 |             1 |
|  2 |       2 |             2 |
|  3 |       3 |             3 |
+----+---------+---------------+

第五范式(5NF)/完美范式

省略…

7.实际上使用范式要注意的问题

经过一系列的阐述后,其实不难发现,越到后面的范式,越难令人理解,同时为了让表满足更高级别的范式,越往后付出代价也越大,而且拆分出的表数量也会越多,所以一般实际开发中,对于库表的设计最高满足BC范式即可,再往后就没意义了,因为表数量一多,查询也好,写入也罢,性能会越来越差

8.反范式设计

范式设计的优点

遵循数据库范式设计的结构优点很明显,它避免了大量的数据冗余,节省了大量存储空间,同时让整体结构更为优雅,能让SQL操作更加便捷且减少出错。但随着范式的级别越高,设计出的结构会更加精细化,原本一张表的数据会被分摊到多张表中存储,表的数量随之越来越多

范式设计的问题

但随之而来的不仅仅只有好处,也存在一个致命问题,也就是当同时需要这些数据时,只能采用联表查询的形式检索数据,有时候甚至为了一个字段的数据,也需要做一次连表查询才能获得。这其中的开销无疑是花费巨大的,尤其是当连接的表不仅两三张而是很多张时,有可能还会造成索引失效,这种情况带来的资源、时间开销简直是一个噩梦,这会严重地影响整个业务系统的性能

介绍

因为范式设计的问题,在设计库表结构时,我们不一定要100%遵守范式准则。这种违反数据库范式的设计方法,就被称之为反范式设计

反范式的问题

  • 存储空间变大
  • 一个表中字段做了修改,另一个表中冗余的字段也需要做同步修改,否则数据不一致
  • 若采用存储过程来支持数据的更新、删除等额外操作,如果更新频繁,会非常消耗系统资源
  • 在数据量小的情况下,反范式不能体现性能的优势,可能还会让数据库的设计更加复杂

适用场景

当冗余信息有价值或者能大幅度提高查询效率的时候,我们才会采取反范式的优化

什么时候考虑增加冗余字段

  • 这个冗余字段不需要经常进行修改
  • 这个冗余字段查询的时候不可或缺
  • 历史信息的需要

典型场景

9.范式设计与反范式设计的对比

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/547302.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

bestvike 资料 --Spring Boot 2.5.0

Spring Boot 2.5.0 SSM环境搭建 springspringmvcmybatisspring springmvc mybatis # 项目 - 需求分析 概要设计(库表设计) 详细设计(验证库表正确性) 编码(环境搭建业务代码) 测试 部署上线# 员工添加 查询所有功能 SSM - 库表 库: ssm 数据库:mysql 表: id na…

spring-cloud微服务gateway

核心部分:routes(路由), predicates(断言),filters(过滤器) id:可以理解为是这组配置的一个id值,请保证他的唯一的,可以设置为和服务名一致 uri:可以理解为是通过条件匹配之后需要路由到&…

RabbbitMQ基本使用及其五种工作模型

初识MQ 同步通讯和异步通讯 什么是同步通讯呢?举个例子,你认识了一个小姐姐,聊的很火热,于是你们慢慢开始打电话,视频聊天,这种方式就成为同步通讯,那什么是一部通讯呢,同样的&…

gitlab(docker)安装及使用

GitLab GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的Web服务。 下载(docker) 查询docker镜像gitlab-ce gitlab-ce是它的社区版 [rootlocalhost ~]# docker search gitlab-ce NAME …

OpenCV基本图像处理操作(六)——直方图与模版匹配

直方图 cv2.calcHist(images,channels,mask,histSize,ranges) images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]channels: 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的…

SETR——Rethinking系列工作,展示使用纯transformer在语义分割任务上是可行的,但需要很强的训练技巧

题目:Rethinking Semantic Segmentation from a Sequence-to-Sequence Perspective with Transformers 作者: 开源:https://fudan-zvg.github.io/SETR 1.研究背景 1.1 为什么要研究这个问题? 自[ 36 ]的开创性工作以来,现有的语义分割模型主要是**基于全卷积网络( FCN )的…

ubuntu20.04安装+ros-noetic安装+内网穿透frp

刷机后的系统安装 ubuntu20.04安装安装ros-noetic安装各种必要的插件安装vscode内网穿透连接实验室主机配置frpc和frps文件运行完成自动化部署免密登录linux的免密登录windows上的免密登录 内网穿透的参考链接:如何优雅地访问远程主机?SSH与frp内网穿透配…

Python学习笔记 - 正则表达式

前言 正则表达式(Regular Expression,在代码中常简写为 regex、regexp、RE 或 re)是预先定义好的一个“规则字符串”,通过这个“规则字符串”可以匹配、查找、替换那些符合“规则”的文本,也就是说正则表达式针对的目标…

MSTP/RSTP的保护功能

目录 原理概述 实验目的 实验内容 实验拓扑 1.配置RSTP/MSTP 2.配置BPDU保护 3.配置根保护 4.配置环路保护 5.配置TC-BPDU保护 原理概述 在RSTP或MSTP交换网络中,为了防止恶意攻击或临时环路的产生,可配置保护功能来增强网络的健壮性和安全性。…

C++vector类(个人笔记)

vector类 1.熟悉vector接口以及使用1.1vector的定义1.2vector迭代器使用1.3vector空间增长1.4vector增删查改1.5vector迭代器失效问题(重点) 2.vector的一些笔试题3.模拟实现vector 1.熟悉vector接口以及使用 vector的C官网文档 1.1vector的定义 (con…

用python快速读取大文件几个GB以上的csv数据文件

用python快速读取大文件几个GB以上的csv数据文件 遇到几个GB的csv大文件,用python读取时,可以通过next()函数一行行来读取以提高效率,然后分批量进行处理。 1、文件格式例图 其中第一、第二行是数据行数、列数汇总。 2、流程 1、把csv第一、第二行的数据说明,先读取出来…

Windows远程桌面连接虚拟机Linux

Windows远程桌面连接虚拟机Linux 需要先打开虚拟机的启用VNC连接使用VNC客户端进行连接 yum install -y tigervnc-server #安装tigervnc-server vncserver #启动一个vnc进程 #第一次启动会要求设置密码 #如果需要更改密码可以使用vncpasswd进行更改密码 vncserver -list #查看…

ASUS华硕ROG幻13笔记本电脑GV301R工厂模式原厂OEM预装Windows11系统,恢复出厂开箱状态

适用于型号:GV301RC、GV301RE、GV301RA 工厂模式安装包:https://pan.baidu.com/s/1gLme1VqidpUjCLocgm5ajQ?pwddnbk 提取码:dnbk 工厂模式Win11安装包带有ASUS RECOVERY恢复功能、自带所有驱动、出厂主题壁纸、系统属性专属联机支持标志…

java算法day55 | 动态规划part16 ● 583. 两个字符串的删除操作 ● 72. 编辑距离

583. 两个字符串的删除操作 思路: 和1143.最长公共子序列这道题思路相同,只不过需要对return的数据做一些操作。 class Solution {public int minDistance(String word1, String word2) {int[][] dpnew int[word1.length()1][word2.length()1];for(int …

06_定时器中断

72分频 72MHz 72000000 经过72分频 1000000

【攻防世界】ics-07

<?php session_start();if (!isset($_GET[page])) {show_source(__FILE__);die(); }if (isset($_GET[page]) && $_GET[page] ! index.php) {include(flag.php); }else {header(Location: ?pageflag.php); } <?phpif ($_SESSION[admin]) {$con $_POST[con];$…

可溶性PFA材质三角漏斗耐腐蚀进口聚四氟乙烯漏斗低溶出析出

PFA全名为可溶性聚四氟乙烯、全氟烷氧基树脂&#xff0c;成品外观透明可视&#xff0c;便于观察&#xff0c;有着良好的化学稳定性、耐温性&#xff0c;耐受强酸强碱以及各种有机溶剂&#xff0c;且PFA原料本身较为洁净&#xff0c;金属离子溶出析出少&#xff0c;经过清洗后可…

OpenCV——SUSAN边缘检测

目录 一、SUSAN算法二、代码实现三、结果展示 OpenCV——SUSAN边缘检测由CSDN点云侠原创&#xff0c;爬虫自重。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫。 一、SUSAN算法 Susan边缘检测是一种经典的边缘检测算&#xff0c;它由Susan Smith…

1044: 顺序栈基本操作的实现

解法&#xff1a; #include<iostream> #include<stack> using namespace std; int main() {int n, a, k;stack<int> sk;cin >> n;while (n--) {cin >> a;sk.push(a);}cin >> k;while (k--) {sk.pop();}if (!sk.empty()) {cout << s…

C语言 | 自定义类型:struct结构体(详解)

目录&#xff1a; --前言 1. 结构体类型的定义与基础结构 2. 结构体的使用 3. typedef相关 4. 结构体的自引用 5. 结构体内存对齐 6. 结构体传参 7. 结构体实现位段 --前言&#xff1a; c语言中内置类型&#xff0c;也有自定义的类型。 例如&#xff1a;内置类型 in…