关系型数据库设计指南

1. 前言

在自己独立开发一个项目的过程中,我发现了一些以往写小 Demo 从来没有遇到过的问题。

最近在独立制作一个全栈的通知管理平台。一开始我没有考虑太多,直接根据头脑中零星的想法就开撸后端数据库 model 和 API,用的是学了半成品的 MongoDb。

结果就是写到后面在遇到复杂的数据库依赖关系时,我感到崩溃。这才想起指导老师给我发了一篇计算机的论文,我便开始虚心研究。

做一个项目要经过这些过程:

  • 系统分析
    • 可行性分析
    • 用户需求分析
    • 整体功能模块分析
    • 技术分析
    • 系统流程分析
  • 系统设计
    • 系统功能模块设计
    • 系统结构设计
    • 数据库概念设计
      • 数据库设计
      • 数据库表设计
  • 系统实现
    • 功能模块的实现
    • API 接口功能的实现
  • 系统测试
    • 黑盒和白盒测试
    • 测试环境与条件
    • 功能测试

敲代码的时候思维很局限,总觉得完成了某一个单个功能就算成功。真到让我独立设计一个项目,我还真就难住了。这里就来讲讲我第一个遇到的问题,数据库怎么设计?

本文用到的工具:

eraser.io

2. 构建实体

打开一额eraser.io文件,在左侧写入所有的实体Entity,例如:

  • 用户
  • 班级
  • 通知

然后在canvas中添加一个Diagram as Code > Entity Relationship也就是E-R图。

✨ 一个最佳实践:总是从用户表User-Table开始着手你的 E-R 图设计。
这是因为,一切都是为了用户用户就是上帝。

从用户表开始,并从用户的注册开始。

我们的用户表可以是这样:

User {id string pkusername string uniqueemail stringbio string
}

20250331011304

强调一点:业务逻辑永远不要成为主键,例如这里除了id外所有的属性皆是如此。

也许你不需要一个createdAt键,但一个很中肯的建议是添加它,总有一天你会需要它的,当你需要它的时候可不能后悔。

User {id string pkusername string uniqueemail stringbio stringcreatedAt timestamp
}

同样的方法,添加班级、通知,完成后如下图所示:

20250331013108

3. 构建关系

关系分为多种:

  • 一对一
  • 一对多
  • 多对多

这里用户和班级之间存在多对多的关系,构建关系时我们也总遵循从User表开始的原则,正如之前提到的,用户是整个产品的核心。

为了加深对关系的了解,这里举个用户发推文的例子:一个用户能发多个推文,每一条推文只有一个用户作为作者。这是一对多的关系,一个用户对应多个推文,但每一条推文只能对应一个用户。

在这里,假如我希望一个班级对应多条通知,在eraser.io中可以使用这样的语法来表示:

# 一对多
Classes.id < Notifies.classId

这里用到的关系符号是<,同样的还有一对一和多对多,分别用-<>符号表示数量关系。

观察上面的代码你会发现一个问题:通知实体并没有classId这个键。

这就是我们需要创建的,这里classId是一个外键,表示引用了一个其他表的主键。

我们修改通知Entity的结构:

Notify {id string pktitle stringcontent stringcreatedAt timestampclassId string pk
}Class.id < Notify.classId

修改后大概是这样:

pk

这里我们再添加一个Media实体:

Media {id string pkfileUrl stringtype enumcreatedAt timestamp
}

很显然,一个班级对应多条通知,一条通知可能对应了多个媒体,所以媒体也需要一个类似的外键来唯一的引用一个它所对应的通知。

你有没有想过为什么反过来不行,为什么不是通知的外键引用到媒体呢?
很显然,通知对应多个媒体,一条外键是不够的,而媒体只对应一个通知,一个外键就刚好。

添加完成后我们再来加上颜色和图标就是这个效果:

20250331015205

关键其实还有语义化的功能,在看到这个外键后就知道通知与某个班级有关,媒体与某条通知相关。

在这种情况下外键是很有意义的。

如果我们的用户能够在每一条通知下进行评论,就需要一个Comments实体。很明显他用外键和唯一的用户关联表示该用户的评论。

20250331020311

在这里,用户和评论是一对多的关系,通知和评论也是一对多的关系,所以你能看到在评论的身上有两条外键分别拉到了用户和通知身上。

根据同样的一对多的原理,我们来制造一个like,也就是用户对评论的点赞:

20250331021319

4. 多对多

根据上面的例子我们不难发现,要处理一对一、一对多的关系都能直接使用外键来处理。

但是多对多呢?

用户的好友是一个多对多的关系,用户可以有多个好友,很多人也可以加这个用户作为好友。

我们的班级和用户之间也是这样的关系,班级可以有很多成员而成员也能加入很多班级。

对于多对多的关系我们一般新建一个表,例如,用户好友的关系。

20250331022917

这里比较令人困惑,要仔细看看。

这张表实际上就是单独跟踪了谁关注了谁,有两个字段:关注follow,粉丝follower

如果要查询用户的粉丝可以用select * from Friends where follow = user_id就能查询到用户的所有粉丝。

如果要查询用户的关注列表就是:select * from Friends where follower = user_id

5. 总结

关于数据库的设计关键是将所有实体抽象出来,并理清楚实体之间的关系。

本次实验🧪的链接:https://app.eraser.io/workspace/1GT4Nb82OR4LTYIuOmkT

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

详解TypeScript中的类型断言及其绕过类型检查机制

TypeScript中的类型断言及其绕过类型检查机制 一、类型断言的本质与工作原理编译时与运行时的区别TypeScript编译器处理类型断言的步骤 二、类型断言的详细语法与进阶用法基础语法对比链式断言断言修饰符1. 非空断言操作符 (!)代码分析1. getLength 函数分析用法说明&#xff1…

XLSX.utils.sheet_to_json设置了blankrows:true,但无法获取到开头的空白行

在用sheetJs的XLSX库做导入&#xff0c;遇到一个bug。如果开头行是空白行的话&#xff0c;调用sheet_to_json转数组获得的数据也是没有包含空白行的。这样会导致在设置对应的起始行时&#xff0c;解析数据不生效。 目前是直接跳过了开头的两行空白行 正确应该获得一下数据 问…

PostgreSQL 数据库下载和安装

官网&#xff1a; PostgreSQL: Downloads 推荐下载网站&#xff1a;EDB downloads postgresql 我选了 postgresql-15.12-1-windows-x64.exe 鼠标双击&#xff0c;开始安装&#xff1a; 安装路径&#xff1a; Installation Directory: D:\Program Files\PostgreSQL\15 Serv…

一、Javaweb是什么?

1.1 客户端与服务端 客户端 &#xff1a;用于与用户进行交互&#xff0c;接受用户的输入或操作&#xff0c;且展示服务器端的数据以及向服务器传递数据。 例如&#xff1a;手机app&#xff0c;微信小程序、浏览器… 服务端 &#xff1a;与客户端进行交互&#xff0c;接受客户…

奇偶ASCII值判断

奇偶ASCII值判断 Description 任意输入一个字符&#xff0c;判断其ASCII是否是奇数&#xff0c;若是&#xff0c;输出YES&#xff0c;否则&#xff0c;输出NO。例如&#xff0c;字符A的ASCII值是65&#xff0c;则输出YES&#xff0c;若输入字符B(ASCII值是66)&#xff0c;则输…

OpenCV 图形API(74)图像与通道拼接函数-----合并三个单通道图像(GMat)为一个多通道图像的函数merge3()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 从3个单通道矩阵创建一个3通道矩阵。 此函数将多个矩阵合并以生成一个单一的多通道矩阵。即&#xff0c;输出矩阵的每个元素将是输入矩阵元素的…

多节点监测任务分配方法比较与分析

多监测节点任务分配方法是分布式系统、物联网&#xff08;IoT&#xff09;、工业监测等领域的核心技术&#xff0c;其核心目标是在资源受限条件下高效分配任务&#xff0c;以优化系统性能。以下从方法分类、对比分析、应用场景选择及挑战等方面进行系统阐述&#xff1a; 图1 多…

【推荐系统笔记】BPR损失函数公式

一、BPR损失函数公式 BPR 损失函数的核心公式如下&#xff1a; L BPR − ∑ ( u , i , j ) ∈ D ln ⁡ σ ( x ^ u i j ) λ ∣ ∣ Θ ∣ ∣ 2 L_{\text{BPR}} - \sum_{(u, i, j) \in D} \ln \sigma(\hat{x}_{uij}) \lambda ||\Theta||^2 LBPR​−(u,i,j)∈D∑​lnσ(x^ui…

Java 核心--泛型枚举

作者&#xff1a;IvanCodes 发布时间&#xff1a;2025年4月30日&#x1f913; 专栏&#xff1a;Java教程 各位 CSDN伙伴们&#xff0c;大家好&#xff01;&#x1f44b; 写了那么多代码&#xff0c;有没有遇到过这样的“惊喜”&#xff1a;满心欢喜地从 ArrayList 里取出数据…

新能源行业供应链规划及集成计划报告(95页PPT)(文末有下载方式)

资料解读&#xff1a;《数字化供应链规划及集成计划现状评估报告》 详细资料请看本解读文章的最后内容。 该报告围绕新能源行业 XX 企业供应链展开&#xff0c;全面评估其现状&#xff0c;剖析存在的问题&#xff0c;并提出改进方向和关键举措&#xff0c;旨在提升供应链竞争力…

Centos 7 yum配置出现一下报错:

One of the configured repositories failed (CentOS-$releaserver-Base), and yum doesnt have enough cached data to continue. At this point the only safe thing yum can do is fail. There are a few ways to work "fix" this: 1.解决CentOS Yum Repositor…

Redis 常见问题深度剖析与全方位解决方案指南

Redis 是一款广泛使用的开源内存数据库&#xff0c;在实际应用中常会遇到以下一些常见问题&#xff1a; 1.内存占用问题 问题描述&#xff1a;随着数据量的不断增加&#xff0c;Redis 占用的内存可能会超出预期&#xff0c;导致服务器内存不足&#xff0c;影响系统的稳定性和…

HOOK上瘾思维模型——AI与思维模型【88】

一、定义 HOOK上瘾思维模型是一种通过设计一系列的触发&#xff08;Trigger&#xff09;、行动&#xff08;Action&#xff09;、奖励&#xff08;Reward&#xff09;和投入&#xff08;Investment&#xff09;环节&#xff0c;来促使用户形成习惯并持续使用产品或服务的思维框…

【playwright】内网离线部署playwright

背景&#xff1a;安装好python3.9后&#xff0c;由于内网无法使用pip安装playwright&#xff0c;多方收集资料&#xff0c;终于部署完成&#xff0c;现汇总如下&#xff1a; 1、playwright需要python3.7以上的版本&#xff0c;如果低于这个版本先要将python解释器升级 2、在可…

Unity动态列表+UniTask异步数据请求

Unity动态列表UniTask异步数据请求 很久没有写东西了。最近有一个需求&#xff0c;在Unity项目里&#xff0c;有几个比较长的列表&#xff0c;经历了一翻优化&#xff0c;趁这几日闲暇&#xff0c;记录下来&#xff0c;给自己留个笔记&#xff0c;也送给有缘之人共同探讨吧。 …

pandas读取Excel数据(.xlsx和.xls)到treeview

对于.xls文件&#xff0c;xlrd可能更合适&#xff0c;但需要注意新版本的xlrd可能不支持xlsx&#xff0c;不过用户可能同时需要处理两种格式&#xff0c;所以可能需要结合openpyxl和xlrd&#xff1f;或者直接用pandas&#xff0c;因为它内部会处理这些依赖。 然后&#xff0c;…

2025年Jetpack Compose集成网络请求库的完整实施方案

Compose中集成网络请求库&#xff0c;网络请求现在Retrofit是最流行的。 首先在Compose中如何进行网络请求&#xff0c;而不仅仅是集成库。因为Compose本身是UI框架&#xff0c;网络请求其实还是通过ViewModel或者Repository来处理&#xff0c;然后通过状态管理来更新UI。所以…

机器视觉开发-摄像头扫描二维码

以下是使用Python和OpenCV实现摄像头扫描二维码的最简单示例&#xff1a; import cv2 from pyzbar import pyzbar# 打开摄像头 cap cv2.VideoCapture(0)print("正在扫描二维码... (按 q 键退出)")while True:# 读取摄像头帧ret, frame cap.read()if not ret:print…

Seata服务端回滚事务核心源码解析

文章目录 前言一、doGlobalRollback3.1、changeGlobalStatus3.2、doGlobalRollback 前言 本篇介绍Seata服务端接收到客户端TM回滚请求&#xff0c;进行处理并且驱动所有的RM进行回滚的源码。 一、doGlobalRollback doGlobalRollback是全局回滚的方法&#xff1a;   首先依旧…

新闻客户端案例的实现,使用axios获取数据并渲染页面,路由传参(查询参数,动态路由),使用keep-alive实现组件缓存

文章目录 0.页面要求1.功能要求2.开始路由配置2.1.嵌套二级路由如何配置?2.2.路由重定向,NotFound页面,去除"#"号 3.实现底部导航栏的高亮效果4.渲染首页:使用axios请求数据5.路由传参5.1.回顾:查询参数传参或者动态路由传参5.2.具体代码 6.渲染详情页7.解决请求过程…