select语句多表联合查询(三)下

版权声明:未经博主允许请勿转载 https://blog.csdn.net/hlz_12345/article/details/84974218

(所有表都在select语句多表联合查询(一)中)

示例四:列出没学过赵四老师讲授课程的所有学生的姓名

首先需要的表有Teacher,Course,SC,Student。其次,分析并简化问题,先找出学过赵四老师讲授课程的学生,把它过滤掉剩下的就是没学过的。然后就开始构思了,最外层为select Sname from Student where.......,其内层为select XX from Teacher,Course,SC,Student where Teacher.T# = Course.T# and Course.C# = SC.C# and SC.S# = Student.S# and Teacher.Tname = '赵四';然后我们select的是什么呢?这就要考虑我们要通过什么将Student表和剩余的那些表连接起来了,明显是S#对吧,于是答案就出来了。

select Sname from Student where S# not in 
(select Student.S# from Teacher,Course,SC,Student where Teacher.T# = Course.T# and Course.C# = SC.C# and SC.S# = Student.S# and Teacher.Tname = '赵四');

通过这道例题可以知道not in就是在当前表中去掉那些具体属性值在集合里的元组,留下不在集合里的.目前来讲,我们大致理解了in是怎样的操作,not in 是怎样的操作,但具体何时用因为我接触的例题太少暂时无法归纳。

相关子查询

因为慕课里的老师也只是一笔带过,所以没有例题,这里做个笔记。外层查询的表里的属性可以进入到内层子查询中。

θsome 与 θall 子查询

θ代表运算符,如>,<,=,<>等,这样一个语法跟in有点类似,不过它是拿一个元素跟一个集合进行比较。θsome是说一个元素跟集合里的每个元素进行比较,若有一个元素满足了条件,整体就满足,留下该元组。θall是说一个元素跟集合里的每个元素进行比较,若集合里的所有元素都满足条件,则留下该元组,否则剔除。

示例一:找出‘001’号课成绩不是最高的所有学生的学号。

既然不是最高,那就是用θsome。有课号,学号和成绩,所以选SC表。我们要在SC表找这样一位同学,只要在所有学生成绩的集合中能找到一个成绩比他高的就可以留下这位同学了,这说明他不是最高的。那么这个集合就是select score from SC;要表达集合中任意一个成绩比他高就是score < some(select score from SC);

select S# from SC where score < some(select score from SC);

示例二:找出所有课程都不及格的学生姓名。

这道题是用相关子查询做的,因为有点不是那么好理解,我先放出代码。

select Sname from Student where 60 > all(select Score from SC where SC.S# =Student.S#);

首先我们来看集合(子查询结果)里面是什么东西,是一个来自SC表的学号与Student表相同的成绩的集合,如果我不加上这个where条件,集合有变化嘛?(试想一下,如果我不写这个where条件就是SC表里所有的成绩,无论加不加这个条件,在student表里都一定能找到SC表里的任意一个学号,这样的一个限制条件本应该是没有意义的),于是我对这段语句做了些修改:

select Sname from Student where 60 > all(select Score from SC);

然后执行出来的结果是nothing。事实证明这两句还真不一样。说明了什么,说明这个where条件是有意义的,是的我想就是相关子查询在这里起了作用。可能就像C语言里的for嵌套循环一样,程序先从student表第一个元组开始,拿元组里的S#去SC表里从SC表的第一个元组开始到最后一个找与它相同的,一旦找到放入到集合中。内层循环过一遍后,集合里就都是与当前student.S#相同的score,再跳到外层循环进行判断是否所有score都小于60,如果是,将student表当前元组的Sname留下,外层查询继续拿下一个S#进入内层循环进行比对。程序类似:

for(type i = Student[0];i<=Student[end-1];i++){ //从student表的第一个元组到最后一个元组

    for(type j= SC[0];j<=SC[end-1];j++)  //从SC表的第一个元组到最后一个元组

        if(i.S# == j.S#)          //如果找到相同的S#
            put_into_set(j.Score);     //把SC表里的Score存储到集合中
    for_each_elem(set)   //对于集合中的每个元素(假设这个函数可以遍历所有元素)
    {
        if(curr_elem > 60){    //如果当前元素大于60
            clearset();       //清理集合中的元素
            continue; }     //跳过下面的语句直接进入下一次的外层循环
    }
    put_into_select(i.Sname);   //将Sname放入结果表中
    clearset();
}

如果我的猜想是正确的,那么我们可以做这样一件事情:我们可以获得每一个学生的所有选修课程的成绩,我们就可以求得他的平均值,如果他的平均值大于60,我们就选入结果。

select Sname from Student where 60< (select avg(Score) from SC where Student.S# = SC.S#);

经过我的试验,确实就是按照我的想法找的,相关子查询会在每次内循环跑完一遍后将满足要求的元组(也有可能是具体的属性值)放在一起,你也可以对这个临时的集合进行一些函数操作(比如我这里的avg),然后他会跳出到外循环进行外层的比较啊乱七八糟的操作。

这个相关子查询还是挺重要的,理解了后会对后面的exists子查询有所帮助。

总结与归纳:

1.in后面的子查询结果必定是一个属性值的集合,且这个属性必须与In前面的那个属性一致。即:属性名 in (select 属性名 from...)

2.能用in写的大部分也都能用θ等值连接写

3.我们可以把in理解为 指定的属性值要在一个集合里,而not in就是不在这个集合里,而这个集合可能就是一些属性值或者子查询的结果

4.θsome就是指定的属性值与集合里任一元素比较,若有一个元素满足了比较条件,整体就满足,留下该元组。θall同样是与前面一样的比较,但要所有的元素均满足这个条件才能留下该元组

5.关于相关子查询,就是要看成一个嵌套的for循环,多想想它的过程便会明白

 

 

 

猜你喜欢

转载自blog.csdn.net/hlz_12345/article/details/84974218