↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

不受支持的版本: 6.5 / 6.4
历史版本PostgreSQL 6.5 已于 2004 年 6 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本手册首页。

59.3. 关系数据模型中的操作

在上一节(关系数据模型的形式化定义)中, 我们定义了关系模型的数学概念。现在我们知道如何用关系数据模型存储数据了, 但还不知道对这些表做些什么才能从数据库中检索内容。例如有人可能询问销售 零件'Screw'的所有供应商的名称。为此,人们定义了两种相当不同的用于表达 关系上操作的记法:

  • 关系代数,一种代数记法,查询通过将专门的 操作符应用于关系来表达。

  • 关系演算,一种逻辑记法,查询通过表述答案中的 元组必须满足的某些逻辑限制来表达。

59.3.1. 关系代数

关系代数由 E. F. Codd 于 1972 年提出。它由一组 关系上的操作组成:

  • SELECT(σ):从关系中提取满足给定限制的元组。 设 R 是一张包含属性 A 的表。 σA=a(R) = {t ∈ R [mid ] t(A) = a} 其中 t 表示 R 的一个元组, 而 t(A) 表示元组 t 的属性 A 的值。

  • PROJECT(π):从关系中提取指定的属性(列)。 设 R 是一个包含属性 X 的关系。 πX(R) = {t(X) [mid ] t ∈ R}, 其中 t(X) 表示元组 t 的属性 X 的值。

  • PRODUCT(×):构建两个关系的笛卡尔积。设 R 是元数为 k1 的表,S 是元数为 k2 的表。 R × S 是所有这样的 k1 + k2 元组的集合:其前 k1 个分量构成 R 中的一个元组,后 k2 个分量构成 S 中的一个元组。

  • UNION(∪):构建两个表的集合论并集。给定表 R 和 S(两者的元数必须 相同),并集 R ∪ S 是属于 R 或 S 或两者 的元组的集合。

  • INTERSECT(∩):构建两个表的集合论交集。给定表 R 和 S,R ∩ S 是既属于 R 又属于 S 的元组的集合。我们同样要求 R 和 S 具有相同的元数。

  • DIFFERENCE(− 或 [setmn ]):构建两个表的集合差。设 R 和 S 仍是两张元数 相同的表。R - S 是 属于 R 但不属于 S 的 元组的集合。

  • JOIN(∏):通过公共属性连接两张表。设 R 是具有属性 A、 B 和 C 的表, S 是具有属性 C、 D 和 E 的表。两个关系 有一个公共属性,即属性 C。 R ∏ S = πR.A,R.B,R.C,S.D,S.E(σR.C=S.C(R × S)). 我们在这里做了什么?首先计算笛卡尔积 R × S。 然后选出公共属性 C 的值相等的那些元组 (σR.C = S.C)。 现在我们得到一张包含两次属性 C 的表, 再通过投影去掉重复的列来纠正这一点。

    例 59.2. 一个内连接

    让我们看一看执行连接所需的各个步骤所产生的表。给定下面两张表:

             R   A | B | C      S   C | D | E
                ---+---+---        ---+---+---
                 1 | 2 | 3          3 | a | b
                 4 | 5 | 6          6 | c | d
                 7 | 8 | 9
             
    

    首先计算笛卡尔积 R × S, 得到:

           R x S   A | B | R.C | S.C | D | E
                  ---+---+-----+-----+---+---
                   1 | 2 |  3  |  3  | a | b
                   1 | 2 |  3  |  6  | c | d
                   4 | 5 |  6  |  3  | a | b
                   4 | 5 |  6  |  6  | c | d
                   7 | 8 |  9  |  3  | a | b
                   7 | 8 |  9  |  6  | c | d
            
    

    经过选择 σR.C=S.C(R × S) 后,得到:

                   A | B | R.C | S.C | D | E
                  ---+---+-----+-----+---+---
                   1 | 2 |  3  |  3  | a | b
                   4 | 5 |  6  |  6  | c | d
            
    

    为去掉重复的列 S.C, 我们用下面的操作把它投影出去: πR.A,R.B,R.C,S.D,S.E(σR.C=S.C(R × S)) 并得到:

                       A | B | C | D | E
                      ---+---+---+---+---
                       1 | 2 | 3 | a | b
                       4 | 5 | 6 | c | d
            
    
  • DIVIDE(÷):设 R 是具有属性 A、B、C 和 D 的表,设 S 是具有属性 C 和 D 的表。我们把除法定义为: R ÷ S = {t [mid ] ∀ ts ∈ S ∃ tr ∈ R 使得 tr(A,B)=t∧tr(C,D)=ts} 其中 tr(x,y) 表示表 R 中仅由分量 x 和 y 组成的一个元组。注意元组 t 仅由关系 R 的分量 A 和 B 组成。

    给定下面的表

              R   A | B | C | D        S   C | D
                 ---+---+---+---          ---+---
                  a | b | c | d            c | d
                  a | b | e | f            e | f
                  b | c | e | f
                  e | d | c | d
                  e | d | e | f
                  a | b | d | e
            
    

    R ÷ S 的结果推导为

                             A | B
                            ---+---
                             a | b
                             e | d
            
    

关于关系代数更详细的描述和定义,参见 [ Ullman, 1988 ] 或 [ Date, 1994 ]。

例 59.3. 一个使用关系代数的查询

回想一下,我们阐述所有这些关系操作符,是为了能够从数据库中检索数据。 让我们回到上一节(关系数据模型中的操作)中的 例子:有人想知道销售零件 Screw 的所有供应商的名称。 使用关系代数,这个问题可以用下面的操作来回答:

πSUPPLIER.SNAME(σPART.PNAME='Screw'(SUPPLIER ∏ SELLS ∏ PART))
      

我们把这样的操作称为查询。如果对示例表 (供应商与零件数据库)求值上述查询, 将得到以下结果:

                             SNAME
                            -------
                             Smith
                             Adams
      

59.3.2. 关系演算 #

关系演算基于一阶逻辑。关系演算有两种变体:

  • 域关系演算(DRC),其中 变量代表元组的分量(属性)。

  • 元组关系演算(TRC),其中 变量代表元组。

我们只讨论元组关系演算,因为它是大多数关系语言的底层基础。关于 DRC(以及 TRC)的详细讨论,参见 [ Date, 1994 ] 或 [ Ullman, 1988 ]。

59.3.3. 元组关系演算

TRC 中使用的查询具有如下形式: x(A) [mid ] F(x) 其中 x 是元组变量,A 是属性集,F 是公式。得到的关系由满足 F(t) 的所有元组 t(A) 组成。

如果我们想用 TRC 回答例子 一个使用关系代数的查询 中的问题, 可以表述如下查询:

     {x(SNAME) [mid   ] x ∈ SUPPLIER ∧ \nonumber
                       ∃ y ∈ SELLS ∃ z ∈ PART (y(SNO)=x(SNO) ∧ \nonumber
                        z(PNO)=y(PNO) ∧ \nonumber
                        z(PNAME)='Screw')} \nonumber
     

对 供应商与零件数据库 中的表求值该查询, 得到的结果与 一个使用关系代数的查询 中的相同。

59.3.4. 关系代数与关系演算 #

关系代数和关系演算具有相同的表达能力;即所有能用关系代数表达的查询也都能用关系演算表达,反之亦然。这一点最早由 E. F. Codd 于 1972 年证明。该证明基于一个算法(“Codd 归约算法”),通过它可以把关系演算的任意表达式归约为语义等价的关系代数表达式。更详细的讨论参见 [ Date, 1994 ] 和 [ Ullman, 1988 ]。

有时人们说,基于关系演算的语言比基于关系代数的语言"层次更高"或"更具声明性",因为代数(部分地)指定了操作的顺序,而演算把确定最有效求值顺序的工作留给了编译器或解释器。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。