SQL实现关系除法

1 关系除法概念

首先,SQL运算其实就是集合的运算,大体也就是投影(select),筛选(where),分组(group by,分组后的聚合和having筛选我们统一划分到分组),集合的加(union,也可以叫集合并运算),减(not in,not exists,minus等,也叫结合补预算,差运算),乘(inner join、left join等各种连接,也可以归结到结合交运算),除(貌似没怎么弄过)。

那么问题来了,什么是关系除法,网上搜一下,概念解释有很多。本博客我们给除法运算下一个通俗的解释。我们看上面的不同集合的运算方法,交并补,都有了,还差一个子集运算,对,集合除法描述的问题就是子集运算,也就是包含关系。

严谨的关系除法(我自定义的严谨)有两种实现方式,双重的not exists(not in是否可行还请自行验证),和用集合减法进行转换

2 环境准备

我们基于选课业务构造如下环境

数据脚本:

create table student(stu_name varchar2(20),subject_id varchar2(10));

create table subject(subject_id varchar2(10),subject_name varchar2(20),teacher varchar2(20));

insert into student values('狗蛋','M');

insert into student values('狗蛋','C');

insert into student values('狗蛋','P');

insert into student values('翠花','M');

insert into student values('翠花','C');

insert into student values('小红','M');

insert into subject values('M','数学','张老师');

insert into subject values('C','语文','张老师');

insert into subject values('M','数学','李老师');

insert into subject values('C','语文','李老师');

insert into subject values('P','物理','李老师');

insert into subject values('M','数学','孙老师');

insert into subject values('P','物理','孙老师');

commit;

3 SQL实现关系除法

3.1 SQL实现除法1

我们需要求同时选了数学和语文的学生姓名,即狗蛋和翠花。

这里的同时其实描述的就是包含关系,即,语文和数学包含在学生的选课项目里面。

select distinct  st1.stu_name

from student st1

where not exists (select 1

                  from subject sj1     --

                  where sj1.SUBJECT_ID in ('M','C')

                                and not exists (select 1   

                                                   from student st2

                                                               where st1.STU_NAME=st2.STU_NAME  --

                                                                 and sj1.SUBJECT_ID=st2.SUBJECT_ID ));  --

可以看到执行计划是Filter嵌套Filter,性能好坏不能一概而论,只能说这种执行计划比较危险。

我们简单说一下运算方法(辅助理解,和运算顺序无关):

①处,我们先获得数学和语文的结果集;

②处,外层st1的学生姓名传给内层的st2学生姓名,获得学生选课的结果集

③处,①处取得的结果集和②处取得结果集用课程编号进行关联,此时,要求①处取得的结果集包含在②的结果集内,最外层的not exists才会返回数据。

 

上面的实现方式我感觉还是比较抽象的,我们现在先考虑用减运算改一下实现方式。

select distinct  st1.stu_name

from student st1

where not exists (select sj1.SUBJECT_ID

                  from subject sj1

                              where sj1.SUBJECT_ID in ('M','C')  --①

                              minus   --③

                              select st2.SUBJECT_ID

                              from student st2

                              where st2.STU_NAME=st1.STU_NAME);  --②

虽然少了一层Filter,性能貌似也没有好太多,还请大神在评论区留下更好的实现方法。

我们依然简单说一下运算方法,还是辅助理解,与运算顺序无关。

①处,我们获得语文和数学的结果集;

②处,内层学生表和外层学生表用学生姓名关联,获得学生选课信息结果集

③处,①和②取得的结果集进行减法运算,如果无数据返回,外层查询返回数据。

相比之下,这种写法更好理解。

3.2 SQL实现除法2

需求加强一下,我们要求同时选了语文和数学的学生及同时能教语文和数学的老师。

结果如下

同时选课+同时教课,不废话,上SQL。

with tmp1 as (   --tmp1求同时选课的学生

select st1.*

from student st1

where not exists (select 1

                  from subject sj1    

                  where sj1.SUBJECT_ID in ('M','C')

                                and not exists (select 1  

                                                   from student st2

                                                               where st1.STU_NAME=st2.STU_NAME 

                                                                 and sj1.SUBJECT_ID=st2.SUBJECT_ID ))) 

,tmp2 as (  --tmp2求同时教课的老师

select sj1.*

from subject sj1

where not exists (select 1

                  from subject sj2

                              where sj2.subject_id in ('M','C')

                                and not exists (select 1

                                                   from subject sj3

                                                               where sj1.TEACHER=sj3.TEACHER

                                                              and sj2.subject_id = sj3.subject_id)))

select distinct t1.STU_NAME,t2.TEACHER

from tmp1 t1

    ,tmp2 t2

where t1.SUBJECT_ID=t2.SUBJECT_ID;

仔细看,并不复杂,我们只是把同时选课和同时教课分别抽取了我with视图,如果有更好的写法,还请留言区评论。

发布了51 篇原创文章 · 获赞 4 · 访问量 4240

猜你喜欢

转载自blog.csdn.net/songjian1104/article/details/100594315