《数据库系统概念》读书笔记,第六章、形式化关系查询语言

  • 引入:

    这一章主要就是从数学角度,给出了三种 形式化语言(关系代数,元祖关系演算,域关系演算) 的定义,他们是各种查询关系语言的基础。


  • 关系代数:

    SQL查询语言就是以关系代数为基础的,这种语言强调的是查询的操作过程,也就是说它是一种过程化查询语言。所以只需要按照其给定的“查询结果的过程序列”一步一步完成就可以得到最终需要查询的内容。
    首先先进行一些基本概念的描述,或者说这一部分是我对于关系代数体系的理解总结

    1. 关系代数一定是“关系”之间的运算,也就是说它的基本运算单位是“关系”。
    2. 对于一个“关系”,它可以看成是一个“元组”的集合,也就是说,很多个结构相同的元组可以组成一个“关系”,关系携带的信息就是它的所有元组。
    3. 对于一组代数关系,既然定义了它的基本操作元素(关系),下一步就是要定义基本的运算:选择、投影、并、集合差、笛卡尔积、更名。
    4. 为了更加方便的表示运算,我们进一步的封装了一些附加的关系代数运算:集合交、自然连接、赋值。这些运算都是通过上面的基本运算定义出来的,所以他们都是可以被基本运算所表示,他们之所以出现只是为了更方便的进行表达。
  1. 还有一些扩展的关系代数运算,这些运算是不能用基本的关系代数运算代替的,比如:广义投影、聚集。

下面的内容就是对于每一个关系代数运算的具体描述

基础关系代数运算:

  1. 选择( σ \sigma σ
    可以看成一种带限制的函数,自变量也就是输入信息就是一组关系x,函数值为一组新的关系y,y中的元组是x的元组的一个子集,或者说y中的元组是x中所有满足谓词限制p的元组。
    • 用途:选出关系中满足一定条件的元组组成一个新的关系。
    • 表达式形式: σ s a l a r y 900 d e p t _ n a m e = x c x ( i n s t r u c t o r ) \sigma_{salary \le 900 \bigwedge dept\_name='xcx' }(instructor) σsalary900dept_name=xcx(instructor)
  2. 投影运算( Π \Pi Π
    • 用途:忽略关系中的一部分属性,只留下需要的属性,并且保留所有元组。
    • 表达式形式: Π I D , n a m e , s a l a r y ( i n s t r u c t o r ) \Pi_{ID,name,salary}(instructor) ΠID,name,salary(instructor)
  3. 并运算( \bigcup
    • 用途:对于两个关系,让他们的元组集合取并,也就是相当于合并两个相同结构的关系同时保证内部元组的互异性。
    • 表达形式: E 1 E 2 E_1 \bigcup E_2 E1E2
  4. 集合差运算( -
    • 用途:去除 E 1 E_1 E1 中同时在 E 2 E_2 E2 元组,更进一步可以看成是为了去除一个集合中,满足某些条件的一系列元组。
    • 表达式形式: E 1 E 2 E_1- E_2 E1E2
  5. 笛卡尔积运算( × \times ×
    • 用途:把两个关系中的元组任意两两组合得到一个新的关系(一般会在继续进一步的进行选择运算),特殊的当两个关系相同的时候,他们进行笛卡尔积运算可以看成一个两两比较的过程(经常用于取最大,取最小等)。
    • 表达式形式: E 1 × E 2 E_1\times E_2 E1×E2
  6. 更名运算( ρ \rho ρ
    • 用途:为了避免后面引用时避免歧义,对于一个关系的各个属性或者关系本身进行更名。
    • 表达式形式: ρ x ( E ) \rho_{x}(E) ρx(E) ρ x ( A 1 , A 2 , . . . , A n ) ( E ) \rho_{x(A_1,A_2,...,A_n)}(E) ρx(A1,A2,...,An)(E)

附加关系代数运算:

  1. 集合交运算( \bigcap
    • 用途:既然有集合并也就肯定会自然的想到集合交,但是集合交确实可以有上面的基础运算关系表示。
    • 表达式形式: E 1 E 2 E 1 ( E 1 E 2 ) E_1 \bigcap E_2 \Longleftrightarrow E_1-(E_1-E_2) E1E2E1(E1E2)
  2. 自然连接运算( \bowtie
    • 用途:对于笛卡尔积和选择运算的组合,因为很多时候都是要将两个有一部分相同属性的关系进行笛卡尔积,并选出他们相同属性内容完全相同的元组,这时候就可以使用自然连接运算,也就是找出真是存在的所有实体的信息元组。
    • 表达式形式: E 1 E 2 Π E 1 E 2 ( σ E 1 . A 1 = E 2 . A 1 E 1 . A 2 = E 2 . A 2 . . . E 1 . A n = E 2 . A n ( E 1 × E 2 ) ) E_1 \bowtie E_2 \Longleftrightarrow \Pi_{E_1\bigcup E_2} (\sigma_{E_1.A_1 = E_2.A_1 \bigwedge E_1.A_2 = E_2.A_2 \bigwedge ... \bigwedge E_1.A_n = E_2.A_n}(E_1 \times E_2)) E1E2ΠE1E2(σE1.A1=E2.A1E1.A2=E2.A2...E1.An=E2.An(E1×E2))
      其中: E 1 E 2 = { A 1 , A 2 , . . . , A n } E_1 \bigcap E_2 = \{A_1,A_2,...,A_n\} E1E2={A1,A2,...,An}
    • 这里其实强行用表达式描述的话,理解起来会有一点繁琐,所以还是选择用简单的比较容易接受的语言来表述吧。如果不写谓词角标则表示,将两个关系的所有相同属性上信息相同的元组合并,然后两个相同的属性也只保留一个属性,这样生成出一个元组的集合就是计算返回的关系。如果有谓词角标,就选择两个属性笛卡尔
  3. theta连接运算( θ \bowtie_{\theta} θ
    • 用途:它是自然连接的扩展,用于描述更普遍的笛卡尔积+选择的结构。谓词即为选择符号的谓词
    • 表达式形式: E 1 θ E 2 σ θ ( E 1 × E 2 ) E_1 \bowtie_{\theta} E_2 \Longleftrightarrow \sigma_{\theta}(E_1 \times E_2) E1θE2σθ(E1×E2)
      注: θ \theta θ 是一个谓词。
  4. 赋值运算( \leftarrow
    • 用途:开临时关系变量,方便保存也容易理解逻辑结构,避免一个表达式过长
    • 表达式形式: E 1 E 2 E_1 \leftarrow E_2 E1E2
  5. 外连接运算(左外连接、右外连接、全外连接)
    这个实在没找到 Latex 里面怎么表示它的符号,自行查阅吧。看起来就是自然连接符号左边或者右边或者看两边出两个横线。
    • 用途(以左连接运算为例):考虑实际应用情况,在对两个元素进行自然连接运算的时候,如果E1的一个元组的某一个属性,这个属性在E2中也出现了,但是这个元组的这个属性的信息在E2关系中,并没有出现过,那么这个元组在自然连接运算的时候就被忽略掉了, 这个时候就需要一个左外连接运算,然后让E1的每一个元组至少在自然连接之后出现一次,然后其对应未知属性(出现在E2但是没出现在E1的属性)值为空(NULL)。右连接运算同理就是保证E2中的每个元组信息至少出现一次。全连接就是E1,E2都要保证至少出现。

扩展关系代数:

  1. 广义投影( Π \Pi Π
    其实就是投影的进一步功能扩展,并部复杂
    • 用途:在投影属性一栏加入了数值运算功能,提供了元组信息修改功能。(上面的操作都是在对元组进行选择、重新组合,这时元组是最小的不可分单位,元组的属性信息不会发生改变)
    • 表达式形式: Π I D , s a l a r y / 12 ( i n s t r u c t o r ) \Pi_{ID,salary/12}(instructor) ΠID,salary/12(instructor)
  2. 聚集
    聚类函数输入值得一个汇集,将单一结果返回。
    • 用途:求一组数据的平均值,最大值,数值累加和,不同元素数量等等
    • 表达式形式: G 1 , G 2 , . . . , G n G F 1 ( A 1 ) , F 2 ( A 2 ) , . . . , F m ( A m ) ( E ) _{G_1,G_2,...,G_n}G_{F_1(A_1),F_2(A_2),...,F_m(A_m)}(E) G1,G2,...,GnGF1(A1),F2(A2),...,Fm(Am)(E)
    • 实际应用举例: G s u m ( s a l a r y ) ( i n s t r u c t o r ) G_{sum(salary)}(instructor) Gsum(salary)(instructor) G c o u n t _ d i s t i n c t ( I D ) ( t e a c h e r s ) G_{count\_distinct(ID)}(teachers) Gcount_distinct(ID)(teachers) d e p t _ n a m e G a v e r a g e ( s a l a r y ) ( i n s t r u c t o r ) _{dept\_name}G_{average(salary)}(instructor) dept_nameGaverage(salary)(instructor)

附加:关于多重集关系代数

上面的关系都是类似于集合的存在,即必须满足一个关系中元组的互异性。但是和SQL不同的是,SQL的一个关系可以有多个相同的元组,所以文中又简单的扩展定义了多重集。后面又依次将基本关系代数运算对应的在多重集上的运算方式进行了定义。


  • 元组关系演算

如果说关系代数过程化的查询语言,那么与之对应的元组关系演算就是非过程性的查询语言。换句话说,他关注的不是“通过怎样的查询方法来查到需要查询的关系(元组集合)”,而是更加关注“如何才能更加清晰的描述出来我需要的元组满足的性质“。
因为查询的是一个元组的集合,所以我们可以猜到,元组关系演算中的查询表达基本形式: { t P ( t ) } \{t|P(t)\} {tP(t)}
注: t t t 表示元组, P P P 是谓词, t [ A ] t[A] t[A] 表示元组 t t t 在属性 A A A 上的取值。

  • 引入存在量词和全称量词
    存在量词: t r ( Q ( t ) ) \exists t \in r(Q(t)) tr(Q(t)) 表示关系 r r r 中存在元组 t t t 使得谓词 Q ( t ) Q(t) Q(t) 为真
    全称量词: t r ( Q ( t ) ) \forall t \in r(Q(t)) tr(Q(t)) 表示对于关系 r r r 中所有元组 t t t,谓词 Q ( t ) Q(t) Q(t) 为真
    { t t i n s t r u c t o r t [ s a l a r y ] > 80000 } \{t|t\in instructor \wedge t[salary] > 80000\} {ttinstructort[salary]>80000} { t s i n s t r u c t o r ( t [ I D ] = s [ I D ] s [ s a l a r y ] > 80000 ) } \{t|\exists s \in instructor(t[ID] = s[ID] \wedge s[salary]>80000)\} {tsinstructor(t[ID]=s[ID]s[salary]>80000)} 这是引入两个量词的关键。
    第一个表示的是,查找 i n s t r u c t o r instructor instructor 中的所有满足条件的元组
    第二个表示的是,只查询 i n s t r u c t o r instructor instructor 中满足条件的 I D ID ID属性,因为 I D ID ID 是唯一对 t t t 进行限制的属性,所以 t t t 的模式为 ( I D ) (ID) (ID)
  • 形式化定义
    书中有一套形式化的递归形式的定义,这里不再复述。
  • 表达式的安全性
    最后一个可能产生意外的地方就是,对于这一类的表达式: { t ¬ ( t i n s t r u c t o r ) } \{t|\neg (t\in instructor)\} {t¬(tinstructor)},显然不在 i n s t r u c t o r instructor instructor 中的元组有无限多个,显然这样不是我们需要的结果。所以这里我们引入一个新的概念“ P P P 的域”记为 d o m ( P ) dom(P) dom(P) ,它是 P P P 所引用的所有值的集合。严格来说, P P P 的域就是: P P P 中显式出现的值及名称出现在 P P P 中的那些关系的所有值的集合。所以对于表达式 { t t P ( t ) } \{t| t \in P(t)\} {ttP(t)},只有它的结果的所有值均来自于 d o m ( P ) dom(P) dom(P) 时,我们才说这个表达式是安全的。

  • 域关系演算

    域关系演算使用的不再是“元组”作为对象,而是使用“从属性域中取值的域变量”作为对象。对比关系代数是SQL语言的基础,域关系演算是被广泛采用的QBE语言的理论基础。

    • 形式化定义
      { < x 1 , x 2 , . . . , x n > P ( x 1 , x 2 , . . . , x n ) } \{<x_1,x_2,...,x_n>|P(x_1,x_2,...,x_n)\} {<x1,x2,...,xn>P(x1,x2,...,xn)}
      x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn代表域变量, P P P 代表原子构成的公式。
      原子构造公式略(和上一个定义类似,递归结构定义)
      另加一个缩写: a ( b ( c ( P ( a , b , c ) ) ) ) \exists a(\exists b( \exists c(P(a,b,c)))) a(b(c(P(a,b,c))))缩写为 a , b , c ( P ( a , b , c ) ) \exists a,b,c(P(a,b,c)) a,b,c(P(a,b,c))
    • 表达式规则
      还是全凭借感觉吧,书中也是给出了这样几个例子,首先先在这里重复列出这几个例子,最后在简单说一下对于这个规则的理解总结。
      1. 例子
        • 找出工资超过 80 000 的教师的 I D n a m e d e p t _ n a m e s a l a r y ID、name、dept\_name、salary IDnamedept_namesalary
          { < i , n , d , s > < i , n , d , s > i n s t r u c t o r s > 80000 ) } \{<i,n,d,s>|<i,n,d,s>\in instructor \wedge s>80 000)\} {<i,n,d,s><i,n,d,s>instructors>80000)}
        • 找出工资超过 80 000 的教师的 I D ID ID
          { < i > n , d , s ( < i , n , d , s > i n s t r u c t o r s > 80000 ) } \{<i>|\exists n,d,s(<i,n,d,s>\in instructor \wedge s>80 000)\} {<i>n,d,s(<i,n,d,s>instructors>80000)}
        • 找出在物理系的所有教师的姓名,以及他们教授的所有课程的 c o u r s e _ i d course\_id course_id
          { < n , c > i , a , s e , y ( < i , c , a , s e , y > s e c t i o n d , s ( < i , n , d , s > i n s t r u c t o r d = " p h y s i c s ) ) } \{<n,c>|\exists i,a,se,y(<i,c,a,se,y>\in section \wedge \exists d,s(<i,n,d,s>\in instructor \wedge d="physics))\} {<n,c>i,a,se,y(<i,c,a,se,y>sectiond,s(<i,n,d,s>instructord="physics))}
        • 找出在2009年秋季学期或者2010年春季学期或者这两个学期都开设的所有课程的集合 c o u r s e _ i d course\_id course_id
          { < c > a , s , y , b , r , t ( < c , a , s , y , b , r , t > s e c t i o n s = " F a l l " y = " 2009 " ) a , s , y , b , r , t ( < c , a , s , y , b , r , t > s e c t i o n s = " S p r i n g " <mtext>   </mtext> w e d g e y = " 2010 " ) } \{<c>|\exists a,s,y,b,r,t(<c,a,s,y,b,r,t>\in section \wedge s="Fall"\wedge y="2009") \vee \exists a,s,y,b,r,t(<c,a,s,y,b,r,t>\in section \wedge s="Spring" \ wedge y = "2010") \} {<c>a,s,y,b,r,t(<c,a,s,y,b,r,t>sections="Fall"y="2009")a,s,y,b,r,t(<c,a,s,y,b,r,t>sections="Spring" wedgey="2010")}
        • 找出选了生物系开设的全部课程的所有学生 I D ID ID
        • { < i > n , d , t c ( < i , n , d , t c > s t u d e n t ) c i , t i , d n , c r ( < c i , t i , d n , c r > c o u r s e d n = " B i o l o g y " s i , s e , y , g ( < i , c i , s i , s e , y , g > t a k e s ) ) } \{<i>|\exists n,d,tc(<i,n,d,tc>\in student ) \wedge \forall ci,ti,dn,cr(<ci,ti,dn,cr>\in course \wedge dn="Biology"\Longrightarrow \exists si,se,y,g(<i,ci,si,se,y,g>\in takes))\} {<i>n,d,tc(<i,n,d,tc>student)ci,ti,dn,cr(<ci,ti,dn,cr>coursedn="Biology"si,se,y,g(<i,ci,si,se,y,g>takes))}
      2. 理解总结
        首先查询的元组的属性就是竖线左边的尖括号中的内容,然后类似于select语句的层层限制即可,注意这里面的每个变量都表示的是一个域(取值集合)。
    • 表达式安全性
      同上元组关系演算的相关要求。(未具体阅读

  • 最后小结

    首先对于关系代数表达式的描述是比较清晰的,因为是SQL语句的基础嘛,然后对于后面的元组关系演算和域关系演算,多加练习吧。。。。
    最后再从表达能力的角度考察:
    基本关系代数运算、限制在安全表达式范围内的元组关系演算、限制在安全表达式范围内的域关系演算。这三者的表达能力是相同的。