【如此简单!数据库入门系列】之数据库设计基础--函数依赖

news2024/12/24 0:11:23

文章目录

  • 问题
  • 函数依赖
  • 函数依赖的作用
  • 函数依赖的性质
  • 重新理解主键
  • 总结
  • 系列文章


问题

考虑一个在线商店数据库,其中包含以下表:

【订单表】

Order_Num(订单号)Product_ID(产品ID)Count(数量)Price(单价)
1102100
2101100
3203200

你觉得这张表是否存在问题?存在哪些问题?
在这里插入图片描述

  1. 数据不一致。同一产品可能有多个不同的单价,导致计算错误。例如,因写入失误,订单1和订单2中产品10的单价不一致,导致后续统计结果出错。
  2. 数据冗余。数据冗余除了可能导致数据不一致的问题之外(问题1),还存在空间浪费的问题。例如,每条包含产品10的订单,都会存储单价信息。

应该怎么解决这两个问题呢?

  • 首先要消除冗余数据,让每个产品的单价只存储一份。假设存在另一张产品单价表,保存每个产品的单价,如下所示:

【单价表】

Product_ID (产品ID,主键)Price(单价)
10100
20200
  • 然后剔除订单表中的单价列,并将产品ID作为外键,如下所示:

【优化后的订单表】

Order_Num(订单号,主键)Product_ID(产品ID,外键)Count(数量)
1102
2101
3203
  • 通过Product_ID外键关联订单表和单价表,可以解决数据冗余和数据不一致问题。

函数依赖

刚才的问题通过消除冗余数据和使用外键就能解决。有没有想过,解决方案背后的原理是什么呢?

核心在于,同一个产品始终有相同的单价(只要两个元组的产品ID相同,它们的产品单价也必须相同)。

一个属性决定另一个属性,这种约束就称为函数依赖。

函数依赖(Functional Dependency,FD)

  • 是关系中两个属性之间的一组约束
  • 表示如果两个元组在属性A1, A2,…, An上有相同的值,那么这两个元组必须在属性B1, B2,…, Bn上也有相同的值
  • 函数依赖用箭头符号(→)表示,即X→Y,其中X函数决定Y。左侧的属性决定了右侧属性的值

简单来说,函数依赖X→Y,可以理解为相同的X一定有相同的Y。例如,同一个产品始终有相同的单价,表示为:{产品ID} → {产品单价}。


函数依赖的作用

函数依赖有什么作用?或者,为什么需要函数依赖?

例如,根据{产品ID} →{单价},可以将订单表{#订单号,产品ID,数量,单价},拆分成两个表:

  • 单价表{#产品ID,单价},产品ID作为主键,确保每个产品ID是唯一的
  • 订单表{#订单号,产品ID,数量},订单号作为主键,产品ID作为外键,关联到单价表

这么做有以下作用。

1. 保证数据一致性

  • 插入一致性:当插入一个新的产品记录时,只要产品ID已经存在,系统将不允许插入,因为这会违反函数依赖,保证每个产品的单价信息是唯一的。
  • 更新一致性:如果需要更新产品单价信息,只需通过产品ID定位到特定的单价记录进行更新。如果没有这样的函数依赖,可能会出现同一个产品的多条记录拥有不同的信息,导致数据不一致。
  • 删除一致性:同理,删除操作也可以通过产品ID精确地移除某个产品单价信息,而不会影响其他产品单价数据。

2. 减少数据冗余

这种设计减少了数据的冗余存储,因为单价信息只存储一次,而不是在每个订单记录中重复存储。

3. 优化查询性能

例如,想查询产品单价,直接从单价表中查询即可,性能非常好(主键等值查询)。而在原始的订单表中查询某个产品的单价,,则需要执行过滤和去重操作。

4. 设计规范化的数据库

函数依赖是数据库设计规范化的基础方法(下篇文章介绍数据库规范化内容)。


函数依赖的性质

我们进一步讨论函数依赖具备的性质。

阿姆斯特朗公理(Armstrong’s Axioms)

阿姆斯特朗公理是一组规则,包括:

  1. 自反规则(Reflexive rule):如果α是一个属性集,β是α的子集,那么α→β。例如,如果我们有属性集{A, B},那么根据自反规则,我们可以得出函数依赖{A} → {A}和{B} → {B},因为每个属性自己就是自己的子集。

  2. 增广规则(Augmentation rule):如果a → b成立,并且y是一个属性集,那么ay → by也成立。也就是说,在依赖中增加属性,不会改变依赖关系。例如,如果我们有函数依赖{A} → {B},并且我们有另一个属性C,那么根据增广规则,我们可以得出函数依赖{AC} → {BC}。

  3. 传递规则(Transitivity rule):与代数中的传递规则相同,如果a → b成立并且b → c成立,那么a → c也成立。例如,如果我们有函数依赖{A} → {B}和{B} → {C},那么根据传递规则,我们可以得出函数依赖{A} → {C}。

平凡函数依赖(Trivial Functional Dependency)

函数依赖可以分为三种情况:

  • 平凡 :如果一个函数依赖 X → Y成立,其中Y是X的子集,那么它被称为平凡函数依赖。平凡函数依赖总是成立的。
  • 非平凡 : 如果 X → Y成立,其中Y不是X的子集,那么它被称为非平凡函数依赖。
  • 完全非平凡 : 如果 X→Y成立,其中X与Y交集为空,则称为完全非平凡函数依赖。

自反规则和平凡函数依赖密切相关:

  • 假设属性集{A,B},根据自反规则,{A,B} → {B}
  • 因为{A,B} → {B},所以是平凡函数依赖

阿姆斯特朗公理和平凡函数依赖有什么用?

  • 作为其它函数依赖的基础
  • 用于推导出新的隐式的函数依赖

重新理解主键

函数依赖是一种表模式设计的基础方法,与主键有密切关系(一个属性如何决定其他属性),如何从函数依赖的角度理解主键?

从函数依赖的角度,可以这样理解主键:

  • 表模式定义为:{X,Y},X和Y分别表示属性集(包含1~N个属性)
  • 假设,X→Y成立,且Y包含除了X以外的所有其他属性,那么X就被认为是一个超键
  • 如果X没有冗余,即不能再移除任何属性而保持其唯一性,则X可认为是候选键,可当选为主键

总结

通过函数依赖,我们知道应该如何分解关系模式,以保证数据一致性并消除数据冗余 — 即如何进行数据库规范化设计。

在规范化过程中,我们使用函数依赖来:

  • 识别候选键。候选键必须满足以下条件:
    • 候选键中的属性对关系中的所有其他属性都有函数依赖关系。
    • 候选键中的属性没有冗余。
  • 分解关系。一旦识别了候选键,就可以使用函数依赖来分解关系。分解涉及将关系拆分为多个较小的关系,每个关系都包含候选键。这有助于消除冗余并确保数据完整性。

更多有关数据库规范化的内容,我们下次再接着讨论。


系列文章

  • 【如此简单!数据库入门系列】之初识数据库 – 数据库基本概念
  • 【如此简单!数据库入门系列】之学霸数据库 – 数据库基本原理
  • 【如此简单!数据库入门系列】之学霸数据库(2) – 数据库基本原理
  • 【如此简单!数据库入门系列】之学霸数据库(3) – 数据库基本原理
  • 【如此简单!数据库入门系列】之前世今生 – 数据库历史
  • 【如此简单!数据库入门系列】之前世今生(2) – 数据库历史
  • 【如此简单!数据库入门系列】之ER模型快速入门
  • 【如此简单!数据库入门系列】之关系模型简介

如果喜欢这篇文章,请不要忘记关注、点赞和收藏哦!
您的鼓励将是我创作的最大动力!

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

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

相关文章

【C++】深入理解string类

一、熟悉string类 1.1 string类的由来: C语音中的字符串需要我们自己管理底层空间,容易内存泄露。而C是面向对象语音,所以它把字符串封装成一个string类。 C中对于string的定义为:typedef basic_string string; 也就是说C中的str…

负债56亿,购买理财产品遭违约,操纵虚假粉丝,流量在下滑,客户数量减少,汽车之家面临大量风险(九)

本文由猛兽财经历时5个多月完成。猛兽财经将通过以下二十二个章节、8万字以上的内容来全面、深度的分析汽车之家这家公司。 由于篇幅限制,全文分为(一)到(十)篇发布。 本文为全文的第二十二章。 目录 一、汽车之家公…

Transformers:它们如何转换您的数据?

一、说明 在快速发展的人工智能和机器学习领域,一项创新因其对我们处理、理解和生成数据的方式产生深远影响而脱颖而出:Transformers。Transformer 彻底改变了自然语言处理 (NLP) 及其他领域,为当今一些最先进的 AI 应…

代码随想录-二叉树(节点)

目录 104. 二叉树的最大深度 题目描述: 输入输出描述: 思路和想法: 111. 二叉树的最小深度 题目描述: 输入输出描述: 思路和想法: 222. 完全二叉树的节点个数 题目描述: ​输入输出描…

关于举办《Llama3关键技术深度解析与构建Responsible AI、算法及开发落地实战》线上高级研修讲座的通知

关于举办《Llama3关键技术深度解析与构建Responsible AI、算法及开发落地实战》线上高级研修讲座的通知

Mybatis逆向工程的2种方法,一键高效快速生成Pojo、Mapper、XML,摆脱大量重复开发

一、写在开头 最近一直在更新《Java成长计划》这个专栏,主要是Java全流程学习的一个记录,目前已经更新到Java并发多线程部分,后续会继续更新;而今天准备开设一个全新的专栏 《EfficientFarm》。 EfficientFarm:高效农…

Tomcat PUT方法任意写文件漏洞(CVE-2017-12615)

1 漏洞原理 在Apache Tomcat服务器中,PUT方法通常用于上传文件。攻击者可以通过发送PUT请求,将恶意文件上传到服务器。 当攻击者发送PUT请求时,Tomcat服务器会将请求中的数据写入指定的文件。如果攻击者能够控制文件路径,那么他们…

Mac brew安装Redis之后更新配置文件的方法

安装命令 brew install redis 查看安装位置命令 brew list redis #查看redis安装的位置 % brew list redis /usr/local/Cellar/redis/6.2.5/.bottle/etc/ (2 files) /usr/local/Cellar/redis/6.2.5/bin/redis-benchmark /usr/local/Cellar/redis/6.2.5/bin/redis-check-ao…

跨平台桌面客户端开发框架

跨平台桌面客户端开发框架允许开发者创建能够在多个操作系统上运行的桌面应用程序。以下是一些流行的跨平台桌面客户端开发框架。这些框架各有优势,选择哪个框架取决于项目需求、团队的技术栈以及对特定特性的偏好。 1.Electron : 使用JavaScript, HTML…

Java IO流(三)

1. 字符流 1.1 什么是字符流 在Java中,字符流是指提供了基于字符的I/O能力的API。 Java 1.0中提供的基于字节的I/O流API只能支持8位字节流,无法妥善地处理16位Unicode字符。由于需要支持Unicode处理国际化字符,因此Java 1.1 对基础流式I/O库…

vue3+ts 原生 js drag drop 实现

vue3ts 原生 js drag drop 实现 一直以来没有涉及的一个领域就是 drag drop 拖动操作,研究了下,实现了,所以写个教程。 官方说明页面及实例:https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API 最终效果&…

SpringCloud整合Gateway结合Nacos

目录 一、引入依赖 二、开启两个测试项目 2.1 order service ​编辑 2.2 user service 三、gateway项目 3.1 新建一个bootstrap.yml文件 3.2 将我们的的网关配置写道nacos里的配置里 3.3 测试:看能够根据网关路由到两个测试的项目 四、 优化 4.1 将项目打包…

数据库(MySQL)—— DQL语句(聚合,分组,排序,分页)

数据库(MySQL)—— DQL语句(聚合,分组,排序,分页) 聚合函数常见的聚合函数语法 分组查询语法 排序查询语法 分页查询语法 DQL的执行顺序 我们今天来继续学习MySQL的DQL语句的聚合和分组查询&…

【面试经典 150 | 数组】接雨水

文章目录 写在前面Tag题目来源解题思路方法一:预处理方法二:单调栈方法三:双指针 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分析题目为主&#xff…

【大模型学习】Transformer(学习笔记)

Transformer介绍 word2vec Word2Vec是一种用于将词语映射到连续向量空间的技术,它是由Google的Tomas Mikolov等人开发的。Word2Vec模型通过学习大量文本数据中的词语上下文信息,将每个词语表示为高维空间中的向量。在这个向量空间中,具有相似…

【C++】学习笔记——string_2

文章目录 六、string类2. 反向迭代器const迭代器 string类对象的容量操作(补)size() 3. string类的元素访问4. string类的修改 未完待续 结合文档食用~ 六、string类 2. 反向迭代器 一般来说,迭代器都是正向的遍历容器,虽然可以…

LuaJIT源码分析(三)字符串

LuaJIT源码分析(三)字符串 要表示一个字符串,核心就是需要知道字符串的长度,以及存放字符串具体数据的地址。lua的字符串是内化不可变的,也就是lua字符串变量存放的不是字符串的拷贝,而是字符串的引用。那么…

C语言⼆级指针如何操作字符串数组(指针数组)?

一、问题 对于字符串数组该如何操作(⽽且是使⽤指针数组存储)? 二、解答 使⽤指针的指针实现对字符串数组中字符串的输出。指向指针的指针即是指向指针数据的指针变量。这⾥创建⼀个指针数组 strings,它的每个数组元素相当于⼀个…

springcloud自定义全局异常

自行创建一个实体类 /*** 全局异常处理类**/ ControllerAdvice public class GlobalExceptionHandler {ExceptionHandler(Exception.class) ResponseBody public Result error(Exception e){e.printStackTrace(); return Result.fail();}/*** 自定义异常处理方法* param e * re…

神奇的Vue3 - 组件探索

神奇的Vue3 第一章 神奇的Vue3—基础篇 第二章 神奇的Vue3—Pinia 文章目录 神奇的Vue3了解组件一、注册组件1. 全局注册​2. 局部注册3. 组件命名 二、属性详解1. Props(1)基础使用方法(2)数据流向:单项绑定原则&…