以初学者的角度理解:SQL实现关系除法
以初学者的角度理解: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实现关系除法的更多相关文章
- 转:如何学习SQL(第二部分:从关系角度理解SQL)
转自:http://blog.163.com/mig3719@126/blog/static/285720652010950825538/ 6. 从关系角度理解SQL 6.1. 关系和表 众所周知,我 ...
- 0419如何利用关系角度看待SQL
转自http://www.open-open.com/solution/view/1389339225820 十步完全理解SQL 1. SQL 是一种声明式语言 首先要把这个概念记在脑中:“声明” ...
- 【转载】十步完全理解SQL
很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程序语言.甚至是函数语言(尽管有些人认为 SQL 语言也是一种函数式语言) ...
- 转载文章----十步完全理解SQL
转载地址:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程 ...
- 十步完全理解SQL
转载于:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程序 ...
- [转]十步完全理解SQL
原文地址:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程 ...
- 十步完全理解 SQL(转载)
英文出处:Lukas Eder. 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完全不同于我们所熟知的命令行语言.面向对象的程序语言.甚至是函数语言(尽管有些人认为 ...
- 经典:十步完全理解 SQL
经典:十步完全理解 SQL 来源:伯乐在线 链接:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完 ...
- 简单十步让你全面理解SQL
很多程序员认为SQL是一头难以驯服的野兽.它是为数不多的声明性语言之一,也因为这样,其展示了完全不同于其他的表现形式.命令式语言. 面向对象语言甚至函数式编程语言(虽然有些人觉得SQL 还是有些类似功 ...
随机推荐
- Pipe Utilization管道利用率
Pipe Utilization管道利用率 概述 CUDA设备的每个流式多处理器(SM)都具有许多专门用于执行特定任务的硬件单元.在芯片级,这些单元提供执行管道,翘曲调度程序将指令发送到这些管道.例如 ...
- C++标准模板库(STL)——queue常见用法详解
queue的定义 queue<typename> name; queue容器内元素的访问 由于队列本身就是一种先进先出的限制性数据结构,因此在STL中只能通过front()来访问队首元素, ...
- Java中List集合转Map集合报错:Duplicate key
一.问题由来 最近生成环境刚发布了一个版本,本人负责优化的一个功能在进行测试时,报错了一个异常,duplicate key;去百度里面看了一下, 意思很明确就是建重复,而且错误是在Java代码中抛出来 ...
- 【逆向&编程实战】Metasploit中的安卓载荷凭什么吊打SpyNote成为安卓端最强远控
文章作者:MG1937 QQ:3496925334 CNBLOG:ALDYS4 未经许可,禁止转载 前言 说起SpyNote大家自然不陌生,这款恶意远控软件被利用在各种攻击场景中 甚至是最近也捕获到了 ...
- 微信小程序电子签名实现
实现签名方法就是使用canvas <canvas canvas-id="firstCanvas" id='firstCanvas' bindtouchstart=" ...
- 『动善时』JMeter基础 — 50、使用JMeter测试WebService接口
目录 1.什么是WebService 2.WebService和SOAP的关系 3.什么是WSDL 4.测试WebService接口前的准备 (1)如何判断是WebService接口 (2)如何获取W ...
- JAVA并发(8)-ThreadPoolExecutor的讲解
很久前(2020-10-23),就有想法学习线程池并输出博客,但是写着写着感觉看不懂了,就不了了之了.现在重拾起,重新写一下(学习一下). 线程池的优点也是老生常谈的东西了 减少线程创建的开销(任务数 ...
- JVM,我就不信学不会你了
JVM 对 Java 有多重要,对程序员面试有多重要,这些不用多说. 如果你还没意识到学 JVM 的必要性,或者不知道怎么学 JVM,那么看完这篇文章,你就能知道答案了. 曾经的我很不屑于学 JVM, ...
- Redisson 分布式锁源码 09:RedLock 红锁的故事
前言 RedLock 红锁,是分布式锁中必须要了解的一个概念. 所以本文会先介绍什么是 RedLock,当大家对 RedLock 有一个基本的了解.然后再看 Redisson 中是如何实现 RedLo ...
- 前端 | Vue 路由返回恢复页面状态
需求场景:首页搜索内容,点击跳转至详情页,页面后退返回主页,保留搜索结果. 方案:路由参数:路由守卫 需求描述 在使用 Vue 开发前端的时候遇到一个场景:在首页进行一些数据搜索,点击搜索结果进入详情 ...