关系代数中的除操作怎么用SQL语句表示?

除运算是什么?(如果已经会了的请直接跳过)

要理解除操作,我们首先要引入“象集”这个概念。

象集

其实象集很简单,就跟我们学过的函数对应关系差不多,只不过函数是“一对一”或者“多对一”,而象集恰好相反,是“一对多”。
引入一个例子看看。假设我们有一张学生选课表SC(sno, cno, grade),其中sno的学生的学号,cno是课程号,grade是分数。这张表记录了学生选修某门课的成绩。

sno cno grade
2020001 001 93
2020001 002 95
2020001 003 90
2020002 001 98
2020002 003 87
2020003 002 75

(备注:一共只有三门选修课:“001”,“002”,“003”)
我们不给出“象集”的任何定义,先直接求出象集,求完之后,你也就明白了,注意前面说的一对多的关系。
2020001的象集{(001,93),(002,95), (003,90)}
2020002的象集{(001,98),(003,987)}
2020003的象集{(002,75)}

除运算

有了象集的概念后,我们现在来理解“除运算”。假设我们有两个关系R(X,Y)和关系S(Y,Z),那么
R÷S = { tr[X] | tr∈R ∧ ∏Y(S)⊆Yx } 其中Yx为x在R中的象集。
是不是懵逼了?什么鬼东西?
其实啊,这都是纸老虎,没那么难!我们只需要从列(属性)和行(元组)来理解就可以了。
列(属性):R ÷ S得到的新的关系(表),它的列是从R中的列中去掉R和S相交的列。
行(元组):R中每一个x对应的象集应该包含S的投影
这就是“除运算”,这么说可能还是有些抽象,我们继续用上面给出的学生选课例子来研究,为了方便我们暂时先将SC的grade属性去掉,然后我们再加一个课程关系C(cno,cname)
SC:

sno cno
2020001 001
2020001 002
2020001 003
2020002 001
2020002 003
2020003 002

C:

cno cname
001 操作系统
002 计算机网络
003 数据结构

我们通过这两个表求SC÷C,我们从列和行两个步骤来进行求解。

第一步(列):SC和C公共的列是cno,所以R÷S必然只剩下R中的sno列,故列先被确定下来了
SC÷C

sno
2020001
2020001
2020001
2020002
2020002
2020003

第二步(行):
C的投影为(001,002,003),而SC中sno的象集中包含(001,002,003)的只有2020001,故,行也被确定下来了
SC÷C

sno
2020001

聪明的你可能已经发现,SC÷C表示的含义是“查询选修了全部课程的学生”,其实除操作非常适合用于求“至少使用了…的全部”之类的查询

落地到SQL语句的实现

我们刚刚讨论了除操作的关系代数,那么怎么用SQL语句来表示呢?事实上,sql语言并没有定义除操作,甚至连全称量词都没有,但是有存在量词EXISTS 和 NOT EXISTS,所以通常的思路就是将全程量词转变为存在量词来实现除操作。这种也是大多数教材普遍的讲法,但是我们今天不用这种方法,因为那种方法有些绕,使得我们很容易看懂别人写的,但是让自己写却写不出来。所以我们用另一种非常好理解的方式:从除操作的定义出发来解决,不过这要求我们知道什么是右外连接,不用慌,活着就是为了折腾,我都为你准备好了。

外连接 & 右外连接

什么是“外连接”呢?这恐怕得从“连接(严格来讲应该是自然连接)”说起!我们还是使用上面的SC表和C表,注意表的变化!
SC:

sno cno
2020001 001
2020001 002
2020001 003

C:

cno cname
001 操作系统
002 计算机网络
003 数据结构
004 计算机组成原理

当我们对两张表做连接的时候,我们会将SC和C中cno相等的元组拼接起来,于是得到新的关系
SC∞C:

sno cno cname
2020001 001 操作系统
2020001 002 计算机网络
2020001 003 数据结构

细心的你一定发现了,计算机组成原理这么课因为没人选,所以连接运算时就丢失了,这就是所谓的“悬浮元组”。为了让悬浮元组也出现,于是人们就折腾出了外连接、左外连接、右外连接,我们这里直接说右外连接吧。
当C中的元组的cno与SC中的元组的cno没有对应关系的时候,我们不丢弃这个选组,仍然将其显示出来,用NULL填充没有匹配的字段,也就是下面这样:
SC∝C:

sno cno cname
2020001 001 操作系统
2020001 002 计算机网络
2020001 003 数据结构
NULL 004 计算机组成原理

了解了右外连接后我们就可以利用它这个性质来做除运算了。我们再次使用上面的例子(注意SC和C的变化)
SC:

sno cno grade
2020001 001 93
2020001 002 95
2020001 003 90
2020002 001 98
2020002 003 87
2020003 002 75

C:

cno cname
001 操作系统
002 计算机网络
003 数据结构

现在我们求选修了全部课程的学生的学号
我们先写关系代数:
① 全部课程的课程号:∏cno(C)
②除:SC ÷ ∏cno(C)
③投影取学号:∏sno(SC ÷ ∏cno(C))
关系代数很容易写出,但是SQL语句稍微有些复杂,现在我们就用外连接来实现。
SQL语句

SELECT DISTINCT sno
FROM SC a
WHERE NOT EXISTS(
	SELECT * FROM 
	(SELECT sno, cno FROM SC b WHERE a.sno = b.sno) x
	RIGHT OUTER JOIN 
	(SELECT cno FROM C) y
	ON x.cno = y.cno
	WHERE x.sno IS NULL
);

莫慌,我来解释一下这条SQL语句。拿数据说话,注意我调整了一下
SC:

sno cno grade
2020002 002 75
2020001 001 93
2020001 002 95
2020001 003 90

C:

cno cname
001 操作系统
002 计算机网络
003 数据结构

外层循环先取出一条记录 SELECT DISTINCT sno FROM SC a :(2020002, 002, 75)
我们拿到这个学生的学号2020002,然后找出他选修的所有课程:SELECT sno, cno FROM SC b WHERE a.sno = b.sno:只有一门课002
然后用002与所有课程做外连接,得到如下的结果:

sno cno
NULL 001
2020002 002
NULL 003

显然,有两个sno为空了,所以NOT EXISTS不通过,2020002不是选修了全部课程的学生

以同样的方式对2020001操作得到下面的结果

sno cno
2020001 001
2020001 002
2020001 003

sno没有NULL,所以2020001是选修了全部课程的学生,将其学号输出。

发布了17 篇原创文章 · 获赞 6 · 访问量 638

猜你喜欢

转载自blog.csdn.net/weixin_36979214/article/details/104747820