diff --git a/source/_drafts/functional-dependency.md b/source/_drafts/functional-dependency.md deleted file mode 100644 index c725720..0000000 --- a/source/_drafts/functional-dependency.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: 函数依赖 -tags: [架构知识, 数据库设计, 数据库范式, 函数依赖, 数据依赖] -categories: - - [架构知识, 数据库设计] -mathjax: true ---- diff --git a/source/_posts/functional-dependency.md b/source/_posts/functional-dependency.md new file mode 100644 index 0000000..fd8fe77 --- /dev/null +++ b/source/_posts/functional-dependency.md @@ -0,0 +1,74 @@ +--- +title: 函数依赖 +tags: + - 架构知识 + - 数据库设计 + - 函数依赖 + - 数据依赖 +categories: + - - 架构知识 + - 数据库设计 +mathjax: true +date: 2021-04-19 09:01:42 +--- + +函数依赖是一个从数学派生过来的术语,是最重要最基本的一种数据依赖关系体现。所有关系型数据库的设计理论核心就坐落于其上。函数依赖内容在软考中出现的几率非常大,几乎接近必考的内容,但也是几乎所有人都为之头疼的问题。其实函数依赖不是难于理解,而是函数依赖是来自数学概念的,这就导致函数依赖的概念变得有一些晦涩。 + +## 定义 + +教科书上的定义一般是比较准确的,函数依赖在教科书上的定义是这样的。 + +!!! note "" + 设$R(U)$是属性$U$上的一个关系模式,$X$和$Y$是$U$的子集,$r$是$R$的任意一个关系,如果对于$r$中的任意两个元组$u$和$v$,只要存在$u[X]=v[X]$,就有$u[Y]=v[Y]$,则称$X$函数决定$Y$,或称$Y$函数依赖于$X$,记为$X \rightarrow Y$。 + +看到这个教科书形式的定义,想必数学不好的人已经完全懵圈了。其实要想理解这个函数依赖的定义,只需要明白这个定义中所使用的几个概念。 + +- **属性**,在这个定义中,**属性**的概念和我们平时常说的属性并不是同一个概念。这里的属性实际上指代的是**集合**。 +- **关系模式**,实际上就是几个数据间关系的描述。通常记为$R(U, D, dom, F)$,其中$R$是关系名,$U$是组成这个关系的属性名称集合,$D$是属性集合$U$中属性所来自的域,$dom$是属性向域的映像集合,$F$是属性间数据依赖关系的集合。关系模式$R$通常会被简记为$R(U)$或者$R(A_1, A_2, \ldots, A_n)$,在这里面$A_n$表示一个具体的属性名称。在一个数据库中,一个关系模式是由一张或者多张数据表构成,$F$中每一个关系实际上对应一个数据表。 +- **元组**,一个属性中记录值的集合。为了便于理解可以认为是一条数据库记录。 + +> 常见的关系模式记录方式是这样的:$R\langle U=\lbrace A, B, C, D \rbrace, F=\lbrace A \rightarrow B, B \rightarrow C \rbrace \rangle$。 + +函数依赖的概念在数学中是用来表示两个函数间的关系的,主要用于表达两个函数间的依赖关系。假设有$A$、$B$两个函数,如果$A \rightarrow B$(从函数$A$能推出函数$B$),那么就可以称函数$B$函数依赖于函数$A$。这个概念在引申到数据间关系上以后,可以这样来表述。 + +!!! note "" + 在关系$R$中,如果属性集$A$中两个元组的值相等,而如果这两个元组中对应的属性集$B$中的值也相同,就可以记作$A \rightarrow B$。也就是说属性集$A$函数决定属性集$B$,属性集$B$函数依赖于属性集$A$。 + +如果用现实生活中可以接触到的数据库结构来解释,那么可以视属性集$A$为数据表的主键,属性集$B$是数据表中的非主键字段。那么可以看出通过作为主键的属性集$A$是可以找到其对应的属性集$B$的,或者用比较数学的语言说,通过属性集$A$可以推出属性集$B$。 + +## 函数依赖的类型 + +根据函数依赖的不同形式,函数依赖可以被细分成更多的几个类型。以下均假定$X$和$Y$、$Z$都是关系$R$上的属性集合。 + +### 平凡函数依赖 + +当属性集$Y$是属性集$X$的子集时($Y \subset X$),必然存在函数依赖$X \rightarrow Y$,那么这种函数依赖就被称为平凡函数依赖。 + +### 非平凡函数依赖 + +当属性集$Y$不是属性集$X$的子集时($Y \not\subset X$),但存在函数依赖$X \rightarrow Y$,那么这种函数依赖就被称为非平凡函数依赖。 + +### 部分函数依赖 + +如果有属性集$X$的真子集$X^\prime$,如果存在$X^\prime \rightarrow Y$,那么就称$Y$对$X$部分函数依赖。 + +!!! info "真子集" + 如果集合$A$是集合$B$的子集($A \subset B$),但是集合$B$不是集合$A$的子集($B \not\subset A$),那么集合$A$就是集合$B$的真子集($A \subseteq B$),即集合$A$完全包含于集合$B$。 + +### 完全函数依赖 + +如果属性集$X$的任何一个真子集$X^\prime$,都不存在$X^\prime \rightarrow Y$,那么就可以称$Y$对$X$完全函数依赖。 + +!!! info "" + 完全函数依赖的意义在于属性集$Y$只能被属性集$X$的整体确定,而不是可被$X$的一部分确定。如果$X$是数据表中的主键,那么$X$和$Y$组成的数据表就已经是不可再被精简的组合了。 + +### 传递函数依赖 + +如果$X \rightarrow Y$,$Y \rightarrow Z$,但是没有$Y \rightarrow X$,且$Y \not\in X$,$Z \not\in Y$,那么可以称$Z$对$X$存在传递函数依赖。 + +传递函数依赖常常用来推导多个属性间的关系或者是多个属性集之间的关系,用在数据库设计上,可以对数据结构做进一步的拆分,提升数据库的范式级别。此外,传递函数依赖还是软考中出题概率最大的内容。 + +## 系列文章 + +1. {% post_link functional-dependency %} +1. {% post_link schema-decomposition %} \ No newline at end of file diff --git a/source/_drafts/functional-dependency/.gitkeep b/source/_posts/functional-dependency/.gitkeep similarity index 100% rename from source/_drafts/functional-dependency/.gitkeep rename to source/_posts/functional-dependency/.gitkeep diff --git a/source/_posts/schema-decomposition.md b/source/_posts/schema-decomposition.md new file mode 100644 index 0000000..211cdb2 --- /dev/null +++ b/source/_posts/schema-decomposition.md @@ -0,0 +1,140 @@ +--- +title: 模式分解 +tags: + - 架构知识 + - 数据库设计 + - 数据库范式 + - 函数依赖 + - 关系模式分解 + - 模式分解 +categories: + - - 架构知识 + - 数据库设计 +mathjax: true +date: 2021-04-19 09:01:52 +--- + +模式分解就是将一个大的关系模式$R\langle U, F \rangle$分解为若干小的关系模式。模式分解的过程就是一个系统进行设计时其数据结构和数据结构的设计过程。 + + +!!! info "模式分解" + 关系模式$R \langle U, F \rangle$的分解是指$R$被它的一组子集$\rho = \lbrace R_1 \langle U_1, F_1 \rangle, R_2 \langle U_2, F_2 \rangle, \ldots, R_n \langle U_n, F_n \rangle \rbrace$所替代的过程。其中$U=U_1 \cup U_2 \cup \ldots \cup U_n$,并且对于任何$1 \le i, j \le n$,均存在$U_i \not\subseteq U_j$;$F_i$是$F$在$U_i$上的投影,也就是$F_i=\lbrace X \rightarrow Y | X \rightarrow Y \in F^+ \land XY \subseteq U_i \rbrace$。 + +## 键 + +键也称为码,是用来标识其他元组的属性集。通常有以下几种类型。 + +- **超键**(超码),在关系模式中能唯一标识元组的属性集。 +- **候选键**(候选码),在关系模式中,能唯一标识元组并且不包含多余属性的属性集。 +- **主键**(主码),在关系模式中的若干个候选键中随意指定的一个候选码作为关键字,这个关键字即为主键。 +- **外键**,如果一个关系模式$R$中的属性$F$是其他关系模式的主键,那么$F$在模式$R$中就被称为外键。 + +从以上定义可以看出来,主键、候选键和超键之间存在着包含关系:$主键 \in 候选键 \in 超键$。 + +## 闭包 + +这里的闭包不是各个编程语言中的闭包,它所指代的意义是关系模式中所有属性集关系的推导结果。以下是闭包的定义。 + +!!! info "闭包" + 令$\alpha$为一个属性集,由$\alpha$确定的的所有属性的集合,即为$\alpha$的闭包,记为$\alpha^+$。 + +闭包的意义在于如果$\alpha+$中包含关系模式$R$中的所有属性,那么属性集$\alpha$就是$R$的超键。 + +!!! note "闭包的示例" + 例如有一个依赖集:$F = \lbrace A \rightarrow B, B \rightarrow C \rbrace$。那么$F^+$就为以下集合$\lbrace A \rightarrow B, A \rightarrow C, AB \rightarrow A, AB \rightarrow B, AB \rightarrow C, \ldots , ABC \rightarrow ABC \rbrace$ + +### Armstrong公理 + +一个依赖集的闭包的推算可以通过Armstrong公理来计算。Armstrong公理是一系列的函数依赖推导规则,由三种原始公理和三个补充定律组成。 + +- **自反律**:若$\beta \subseteq \alpha$,则$\alpha \rightarrow \beta$。 +- **增补律**:若$\alpha \rightarrow \beta$,则$\gamma\alpha \rightarrow \gamma\beta$。 +- **传递律**,若$\alpha \rightarrow \beta$且$\beta \rightarrow \gamma$,则$\alpha \rightarrow \gamma$。 +- **合并律**:若$\alpha \rightarrow \beta$于$\alpha \rightarrow \gamma$成立,则$\alpha \rightarrow \beta\gamma$成立。 +- **分解率**:若$\alpha \rightarrow \beta\gamma$成立,则$\alpha \rightarrow \beta$于$\alpha \rightarrow \gamma$成立。 +- **伪传递率**:若$\alpha \rightarrow \beta$与$\gamma\beta \rightarrow \delta$成立,则$\alpha\gamma \rightarrow \delta$成立。 + +现在可以用以上六个定律尝试推导一下前面的示例。 + +## 无损连接和函数依赖保持的判定 + +关系模式的分解必须遵循以下两个准则: + +1. **无损连接**:信息不能发生增减,保证不失真。 +1. **保持函数依赖**:不破坏属性间存在的依赖关系。 + +### 无损连接 + +!!! info "无损连接" + 若$\rho=\lbrace R_1 \langle U_1, F_1 \rangle, R_2 \langle U_2, F_2 \rangle, \ldots, R_n \langle U_n, F_n \rangle \rbrace$是$R\langle U, F \rangle$的一个分解,如果对于$R\langle U, F \rangle$的任何一个关系$r$均有$r=m_p(r)$成立,则分解$\rho$具有无损连接性。 + +无损连接其实比较好理解,关系模式在分解成比较小的关系模式以后,通过投影、连接以后仍能恢复原来的关系模式,也就是没有丢失信息,则这个分解被称为无损分解,否则就是损失分解。 + +对于无损分解的检验,一般可以使用以下技巧来进行:如果关系模式$R=\langle U, F \rangle$被分解为$\rho=\lbrace R_1, R_2 \rbrace$,那么如果$R_1 \cap R_2 = R_1 - R_2$或者$R_1 \cap R_2 = R_2 - R_1$,那么这个分解$\rho$就是无损分解。换句话说,$R_1 \cap R_2$是$R_1$或者$R_2$的超键,那么就可以判定分解是无损分解。 + +> 如果分解出的关系模式超过两个,例如$\rho=\lbrace R_1, R_2, R_3 \rbrace$,那么就不能采用上述的方法进行快速检验了,只能采用表格列举法来逐个依赖进行推导。 + +### 函数依赖保持 + +!!! info "函数依赖保持" + 若$F^+=\left( \bigcup_{i=1}^k F_i \right)^+$,则$R\langle U, F \rangle$的分解$\rho=\lbrace R_1 \langle U_1, F_1 \rangle, R_2 \langle U_2, F_2 \rangle, \ldots, R_n \langle U_n, F_n \rangle \rbrace$保持函数依赖。 + +简而言之,函数依赖保持实际上就是$F=F_1 \cup F_2 \cup \ldots \cup F_n$,即两者的闭包相等。 + +对于函数依赖保持的快速判定可以采用以下技巧来进行:如果F上的每一个函数依赖都在其分解后的某一个关系上成立,则这个分解是保持依赖的。 + +## 范式 + +范式是符合某种基本的关系模式的集合。在日常工作中比较经常听到的是第一范式、第二范式、第三范式和BC范式。这四种范式是递进严格的,并且$BCNF \subset 3NF \subset 2NF \subset 1NF$。 + +> 更加严格的范式还有第四范式(4NF)和第五范式(5NF),但是它们的规定过于严格,虽然有利于使数据结构达到最简,但是并不一定对日常的查询提供优化,所以在日常设计中很少使用。 + +### 第一范式(1NF) + +如果关系模式$R$的每个关系$r$的属性都是不可再分的原子值,那么关系模式$R$就是第一范式的模式。用最简单的话说,第一范式对于数据库的设计要求是数据表的每一行都**没有重复的数据**(冗余数据)。 + +!!! warning "" + 在实际的数据库结构设计中,第一范式也常常被违反,因为必要的冗余数据可以提高数据库的查询速度,所以这也是在实际应用中为什么会使用反范式设计。 + +### 第二范式(2NF) + +如果关系模式$R$是1NF,并且每个非主属性都完全函数依赖于候选键,那么关系模式$R$就是第二范式。同样用最简单的话说,第二范式对于数据库设计的要求是所有的**非主键数据都完全依赖于主键**。 + +### 第三范式(3NF) + +如果关系模式$R$是1NF,并且每个非主属性都不传递依赖于$R$的候选键,那么关系模式$R$就是第三范式。同样用最简单的话来描述,第三范式对于数据库设计的要求是**非主键数据不能依赖于外键**。 + +!!! warning "" + 大部分的数据库结构设计都不能满足第三范式,通常第二范式是最常用的。 + +### BC范式(BCNF) + +如果关系模式$R$是1NF,并且每个属性都不传递依赖于$R$的候选键,那么关系模式$R$就是BC范式。简而言之,BC范式要求整个关系模式都不可再分且不能依赖于外键,即所有属性(包括主属性和非主属性)都不能传递依赖于$R$的任何候选码键。 + +!!! warning "" + BC范式是四种范式中最严格的存在,是一种最为理想化的数据库设计目标,但是在实际数据库设计中,能够达到BCNF的数据库是及其罕见的。 + +## 软考例题 + +!!! note "模式分解" + 有$R=\langle U=\lbrace ABCD \rbrace, F=\lbrace A \rightarrow B, B \rightarrow C, B \rightarrow D, C \rightarrow A \rbrace \rangle$,现将$R$分解为$U_1=\lbrace AB\rbrace$和$U_2=\lbrace ACD\rbrace$两个关系,求$R_1$和$R_2$。 + +这里可以根据依赖关系,直接推导出$R_1=\langle \lbrace AB \rbrace, \lbrace A \rightarrow B, B \rightarrow A \rbrace$,$R_2=\langle \lbrace ACD \rbrace, \lbrace A \rightarrow C, C \rightarrow A, A \rightarrow D \rbrace$。 + +!!! note "无损连接与函数依赖保持判断" + 设关系模式$R\langle U, F\rangle$,其中$U=\lbrace A, B, C, D, E\rbrace$,$F=\lbrace A \rightarrow BC, C \rightarrow D, BC \rightarrow E, E \rightarrow A\rbrace$,则分解$\rho=\lbrace R_1(ABCE), R_2(CD) \rbrace$满足( ) 。
+ A. 具有无损连接性、保持函数依赖
+ B. 不具有无损连接性、保持函数依赖
+ C. 具有无损连接性、不保持函数依赖
+ D. 不具有无损连接性、不保持函数依赖 + +首先来确定无损连接性。计算$R_1 \cap R_2$得到$\lbrace C \rbrace$,然后计算其闭包$C^+$。经过计算,$C^+=\lbrace CD \rbrace$,所以$C$是$R_2$的超键,故分解$\rho$是具备无损连接性的。 + +然后再确定函数依赖保持。$A \rightarrow BC$、$BC \rightarrow E$、$E \rightarrow A$都在$R_1$上成立,$C \rightarrow D$在$R_2$上成立,所以分解是保持函数依赖的。 + +故这道题选A。 + +## 系列文章 + +1. {% post_link functional-dependency %} +1. {% post_link schema-decomposition %} \ No newline at end of file diff --git a/source/_posts/schema-decomposition/.gitkeep b/source/_posts/schema-decomposition/.gitkeep new file mode 100644 index 0000000..e69de29