以初学者的角度理解:SQL实现关系除法

相信各位在学习SQL的时候,由于没有一家SQL语言提供除法命令而只能自己写一个。而网上大多就是四步骤加一个模板:

select distinct	A.X
from A A1
where not exists(
select B.Y
from B
where not exists(
select *
from A A2
where A1.X = A2.X
and A2.Y = B.Y
)
)

那四个步骤又写的过于抽象~,看得一头雾水。因此笔者希望从一个初学者的角度,讲解一下关系除法的实现过程,帮助大家理解。

例子

我们举一个实例来讲解~

我们用一张SC表和Course表,其中:

SC表:

Course表:

而我们想要做的是:

查询选修了全部课程的学生姓名。

很明显我们需要做除法:

select distinct	SC1.Sno
from SC SC1
where not exists(
select Course.Cno
from Course
where not exists(
select *
from SC SC2
where SC1.Sno = SC2.Sno
and SC2.Cno = Course.Cno
)
)

接下来讲解这段语句的整个过程~

具体讲解

我们先看看这两句SQL

select distinct	SC1.Sno
from SC SC1

这一段,产生的结果应该是:

接下来到下一句:

where			not exists()

这句话的意思很简单,就是我即将进行括号中写好的查询语句,如果查询结果为空,返回true,否则返回false

这时候,我们开始where语句,此时SC1.Sno的值会被挨个放进where中进行处理。

此时:

接下来进入not exists的部分。

select			Course.Cno
from Course

跟上面一样,先看看它执行出来什么效果:

接下来到下一句:

where			not exists()

此时:

接下来进入下一个not exists的部分。

select			*
from SC SC2
where SC1.Sno = SC2.Sno
and SC2.Cno = Course.Cno

我们的SC表是这样子的:

我们看看当select完之后,where在干嘛:

where			SC1.Sno = SC2.Sno
and SC2.Cno = Course.Cno

这句话的意思是:

SC1.Sno与本关系中的Sno相等且Course.Cno与本关系中Cno的值相等

那回头看看,我们进行的步骤,我们发现到这一步的时候,SC1.Sno = 20110001,而Course.Cno = 001。换言之,我们要找到符合这两个条件的元组。

不难找到:

因此

select			*
from SC SC2
where SC1.Sno = SC2.Sno
and SC2.Cno = Course.Cno

返回结果集:

20110001 001 89

这时候,我们看回上一层结构:

select			Course.Cno
from Course
where not exists(...)

我们成功返回了not exists部分的结果集,因此where处得到的结果是false,因此Course.Cno = 001不被加入这层结构产生的结果集。所以指针往下,Course.Cno的数据变更:

Coures.Cno = 002

于是我们再次进入该语句:

select			*
from SC SC2
where SC1.Sno = SC2.Sno
and SC2.Cno = Course.Cno

此时SC1.Sno = 20110001,而Course.Cno = 002

返回结果集:

20110001 002 78

这时候,我们看回上一层结构:

select			Course.Cno
from Course
where not exists(...)

我们成功返回了not exists部分的结果集,因此where处得到的结果是false,因此Course.Cno = 002不被加入这层结构产生的结果集。所以指针往下,Course.Cno的数据变更:

Coures.Cno = 003

于是我们再次进入该语句:

select			*
from SC SC2
where SC1.Sno = SC2.Sno
and SC2.Cno = Course.Cno

此时SC1.Sno = 20110001,而Course.Cno = 003

返回结果集:

20110001 003 89

这时候,我们看回上一层结构:

select			Course.Cno
from Course
where not exists(...)

我们成功返回了not exists部分的结果集,因此where处得到的结果是false,因此Course.Cno = 002不被加入这层结构产生的结果集。所以指针往下,Course.Cno的数据变更:

Coures.Cno = 004

重复上述的操作,不再赘述。

我们发现,由于20110001这位同学刚刚好都把课选了。每次我们都能从SC表中找到数据,因此

select			Course.Cno
from Course
where not exists(...)

该段语句最终的结果是一个空集,没有一个数据被放进了结果集。

这时候我们再回到上一层结构:

select distinct	SC1.Sno
from SC SC1
where not exists(...)

该处的not exists中的查询语句返回的是空集,所以它返回的是true

也就是说,where处得到的结果是true

也就是说,Sno = 20110001 被加入结果集。

我们继续分析,想要Sno不被加入结果集:

select			Course.Cno
from Course
where not exists(...)

这段语句就需要返回结果集。

也就是说这段语句not exists部分至少有一个返回true

那么这段语句:

select			*
from SC SC2
where SC1.Sno = SC2.Sno
and SC2.Cno = Course.Cno

只要元祖没有被找到(即有一门课该学生没有选),它就返回空集。

接下来一系列的连锁反应:

select			Course.Cno
from Course
where not exists(...)

返回一个非空结果集

select distinct	SC1.Sno
from SC SC1
where not exists(...)

由于返回的是true,所以该学生的学号不被加入结果集。

总结一下

select distinct	A.X
from A A1
where not exists(
select B.Y
from B
where not exists(
select *
from A A2
where A1.X = A2.X
and A2.Y = B.Y
)
)

实际上,直观来看:

select distinct	A.X
from A A1
where not exists()

这一段相当于按X分组。

select			B.Y
from B
where not exists(
select *
from A A2
where A1.X = A2.X
and A2.Y = B.Y
)

这段就是分组来看,去确定在一组中,是不是有某一个数据没有没有在B.Y中,如果有,返回没有在B.Y中的结果

如果的确有一个数据没在其中,由于它是嵌套在not exists中的,只要它返回没有在B.Y中的结果不是空集,就说明该组不合要求,不加入结果集。如果是空集,说明符合要求,加入结果集。

瞎说一下

我知道非常绕,静下心按流程走一走,还是看不懂可以留言喔~

以初学者的角度理解:SQL实现关系除法的更多相关文章

  1. 转:如何学习SQL(第二部分:从关系角度理解SQL)

    转自:http://blog.163.com/mig3719@126/blog/static/285720652010950825538/ 6. 从关系角度理解SQL 6.1. 关系和表 众所周知,我 ...

  2. 0419如何利用关系角度看待SQL

    转自http://www.open-open.com/solution/view/1389339225820 十步完全理解SQL   1. SQL 是一种声明式语言 首先要把这个概念记在脑中:“声明” ...

  3. 【转载】十步完全理解SQL

    很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程序语言.甚至是函数语言(尽管有些人认为 SQL 语言也是一种函数式语言) ...

  4. 转载文章----十步完全理解SQL

    转载地址:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程 ...

  5. 十步完全理解SQL

    转载于:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程序 ...

  6. [转]十步完全理解SQL

    原文地址:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程 ...

  7. 十步完全理解 SQL(转载)

    英文出处:Lukas Eder. 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程序语言.甚至是函数语言(尽管有些人认为 ...

  8. 经典:十步完全理解 SQL

    经典:十步完全理解 SQL   来源:伯乐在线 链接:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完 ...

  9. 简单十步让你全面理解SQL

    很多程序员认为SQL是一头难以驯服的野兽.它是为数不多的声明性语言之一,也因为这样,其展示了完全不同于其他的表现形式.命令式语言. 面向对象语言甚至函数式编程语言(虽然有些人觉得SQL 还是有些类似功 ...

随机推荐

  1. CVPR2020:点云分析中三维图形卷积网络中可变形核的学习

    CVPR2020:点云分析中三维图形卷积网络中可变形核的学习 Convolution in the Cloud: Learning Deformable Kernels in 3D Graph Con ...

  2. 3,java数据结构和算法:约瑟夫环出队顺序, 单向环形链表的应用

    什么是约瑟夫环? 就是数小孩游戏: 直接上代码: 要实现这个,只需要理清思路就好了 孩子节点: class Boy{ int no;//当前孩子的编码 Boy next; // 下一节点 public ...

  3. RabbitMQ由浅入深入门全总结(二)

    写在最前面 距离上一次发文章已经很久了,其实这段时间一直也没有停笔,只不过在忙着找工作还有学校结课的事情,重新弄了一下博客,后面也会陆陆续续会把文章最近更新出来~ 这篇文章有点长,就分了两篇 PS:那 ...

  4. 一文带你了解 Redis 的发布与订阅的底层原理

    01.前言 发布订阅系统在我们日常的工作中经常会使用到,这种场景大部分情况我们都是使用消息队列的,常用的消息队列有 Kafka,RocketMQ,RabbitMQ,每一种消息队列都有其特性,关于 Ka ...

  5. 高速数字逻辑电平(8)之LVDS差分信号深度详解

    原文地址点击这里: LVDS(Low-Voltage Differential Signaling ,低电压差分信号)是美国国家半导体(National Semiconductor, NS,现TI)于 ...

  6. VLAN协议与三层交换机 (Access/Trubk/Hrbrid)

    VLAN协议与三层交换机(Access/Trunk/Hybrid) 目录 一.VLAN概述 1.1.分割广播域 1.2.VLAN的优势 1.3.静态VLAN 二.Trunk的作用 2.1.概念 2.2 ...

  7. mapboxgl绘制3D线

    最近遇到个需求,使用mapboxgl绘制行政区划图层,要求把行政区划拔高做出立体效果,以便突出显示. 拿到这个需求后,感觉很简单呀,只需要用fill-extrusion方式绘制就可以啦,实现出来是这个 ...

  8. Unix、Linux 软件包管理快速入门对照:apt、brew、pkg、yum

    请访问原文链接:https://sysin.org/blog/apt-brew-pkg-yum/,查看最新版.原创作品,转载请保留出处. 作者:gc(at)sysin.org,主页:www.sysin ...

  9. 07 修改JumpServer网页信息

    1.7.修改JumpServer网页信息 注意:在修改相关配置文件之前要先进行备份,防止文件修改错误无法恢复. 1.Luna图标: /opt/luna/static/imgs/logo.png 2.j ...

  10. 尝试用面向对象思维理解Vue组件

    什么是组件 用面向对象的思维去理解Vue组件,可以将所有的事物都抽象为对象,而类或者说是组件,都具有属性和操作. 如抽取人类为组件,其基本的属性有姓名.年龄.国籍:基本的方法有吃饭.睡觉.跑步等. & ...