SELECT(嵌套查询、EXISTS、集合查询、基于派生表的查询)

嵌套查询

带有EXISTS谓词的子查询

EXISTS谓词 —— 存在量词 \exists

带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”

  • 若内层查询为,外层WHERE子句返回false
  • 若内层查询非空,外层WHERE子句返回true

因为带有EXISTS谓词的子查询不返回任何数据,指定列名没有意义,所以目标列表达式全用*代替

NOT EXISTS(与EXISTS正好相反)

  • 若内层查询非空,外层WHERE子句返回false
  • 若内层查询为,外层WHERE子句返回true

【例3.60】查询所有选修了1号课程的学生姓名

选修一号课程 —— SC表
学生姓名 —— Student表

SELECT Sname
FROM Student
WHERE EXISTS
     (SELECT *
     FROM SC
     WHERE Sno=Student.Sno AND Cno='1');

这是个相关子查询。
执行过程:
①在外层Student中取出一个元组的Sno
②进入内层EXISTS判断WHERE子句是否成立,即SC表中是否存在这样的元组
③存在,返true;为空,返false
④若外层的WHERE子句为真,就取出该元组的Sname
⑤重复上述步骤,直到将Student中的元组取完
在这里插入图片描述
【例3.61】查询没有选修1号课程的学生姓名

这个跟上题正好相反,用NOT EXISTS

SELECT Sname
FROM Student
WHERE NOT EXISTS
      (SELECT *
      FROM SC
      WHERE Sno=Student.Sno AND Cno='1');

在这里插入图片描述
不同形式之间的查询可以替换

  • 一些带有EXISTS和NOT EXISTS谓语的子查询不能被其他形式的子查询所替换
  • 所有带IN谓词、比较运算符、ANY或ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换

EXISTS好万能。。。果然要学到最好的总是要付出一些代价,要征服EXISTS!!!

【例3.55】查询与“刘晨”在同系的学生

之前使用的是IN谓词或者 =
用EXISTS等价替换:

SELECT Sno,Sname,Sdept
FROM Student S1
WHERE EXISTS
      (SELECT *
      FROM Student S2
      WHERE S1.Sdept=S2.Sdept AND S2.Sname='刘晨');

在这里插入图片描述

依次取S1表中元组的Sdept,看是否和S2中刘晨的Sdept相等,相等就是满足条件,内层查询结果就不是空,外层WHERE子句就是真,就取该元组的Sno,Sname,Sdept。

!!!用EXISTS和NOT EXISTS实现全称量词 \forall

SQL中没有全称量词,所以利用离散数学中的逻辑转化,将全称量词 \forall 用存在量词 \exist 表示。

( x ) P ¬ ( x ( ¬ P ) ) (\forall x)P \equiv \neg (\exists x(\neg P))

双重否定!

可以先将自然语言做转换,再用量词表示。

【例3.62】查询选修了全部课程的学生姓名。

选修了全部课程的学生 \equiv 没有课程是该学生没有选修的

SELECT Sname
FROM Student
WHERE NOT EXISTS
      (SELECT *
      FROM Course
      WHERE NOT EXISTS
            (SELECT *
            FROM SC
            WHERE SC.Cno = Course.Cno
             AND Student.Sno=SC.Sno)
      );

双重否定 —— NOT EXISTS嵌套

这里修改了一下表,让结果有数据“李勇”。

执行过程
进入第一层:取Student表中的一个Sno值‘201215121’
进入第二层:取Course表中的一个Cno值‘1’
进入第三层:判断SC表中是否有 Sno=‘201215121’ 并且Cno=‘1’这样的元组,经过NOT EXISTS的取反,存在F,不存在就是T—— 结果F
返回第二层:取第二个Cno值‘2’
进入第三层:判断是否存在 Sno=‘201215121’ 并且Cno=‘2’这样的元组,结果取反 —— 结果F

返回第二层:取最后一个Cno值‘7’
进入第三层:判断是否存在 Sno=‘201215121’ 并且Cno=‘7’这样的元组,结果取反 —— 结果F
得到第二层的结果是:F \vee F \vee F… \vee F = F
返回第一层的结果:T(NOT EXISTS再次取反)
结果为T,所以将Sno='201215121’学生的学生姓名取出放入结果表中。
进入第一层:取第二个Sno值‘201215122’
再次重复以上的操作。
在这里插入图片描述
刚开始这里我一直搞不懂为什么第二层的结果都是逻辑或 \vee 的结果,后来慢慢地想通了。

逻辑或 \vee ,表示只要有一个为真就是真,也就是只要表中有一个满足条件的也就是存在,表非空,那就是真。

!!!用EXISTS和NOT EXISTS实现逻辑蕴涵 \rightarrow

SQL中没有逻辑蕴涵运算,也是可以用谓词演算等价转换:

p q ¬ p q p \rightarrow q \equiv \neg p \vee q

蕴涵的符号是一个箭头 \rightarrow ,就是可以推出的意思,如果p,那么q;强调一种包含关系。

【例3.63】查询至少选修了学生201215122选修的全部课程的学生学号。

所要查询的学生选修的课程包含了201215122学生所选的课程,用蕴涵。

蕴涵表达:查询学号为x的学生,对所有课程y,只要201215122选修了y,则x也选了y。

形式化表示:
p:学生201215122选修了课程y
q:学生x选修了课程y
上述查询就是: ( y ) p q (\forall y) p \rightarrow q

用上述式子等价转换:
¬ y ( p ¬ q ) \neg \exist y (p \wedge \neg q)
其中运用了德摩根律 ¬ ( x ) P ( x ) x ( ¬ P ( x ) ) \neg (\forall x)P(x) \equiv \exist x(\neg P(x))

转义后的表达:
不存在一门课程y,201215122选了,但是x没有选

用NOT EXISTS

SELECT DISTINCT Sno
FROM SC SCX
WHERE NOT EXISTS
      (SELECT *
      FROM SC SCY
      WHERE SCY.Sno = '201215122' 
         AND NOT EXISTS
            (SELECT *
            FROM SC SCZ
            WHERE Sno = SCX.Sno
              AND Cno = SCY.Cno)
       );

上题中将201215121设置成了选修了所有课程,所以结果中有。
在这里插入图片描述在这里插入图片描述
执行过程:
进入第一层:在SCX表中选取一个Sno值(‘201215001’)
进入第二层:在SCY中取第一个Cno值‘2’
(我的表中第一个Cno为2)
进入第三层:判断是否有Sno='201215001’和Cno='2’这样的元组,NOT EXISTS取反,存在为F,不存在为T——结果F
相当于第三层内的语句为:

SELECT *
FROM SC SCZ
WHERE Sno = '201215001' AND Cno = '2';

在这里插入图片描述
不是空,所以是T,NOT EXISTS取反后是F

返回第二层:是否有Sno = ‘201215122’ AND F(逻辑与操作),结果是空,为F,再次取第二个Cno的值‘3’
在这里插入图片描述
进入第三层:
相当于

SELECT *
FROM SC SCZ
WHERE Sno = '201215001' AND Cno = '3';

存在这样的元组,NOT EXISTS取反,结果为F
返回第二层:此时WHERE子句后为
Sno=‘201215122’ AND F —— F
得到第二层的结果
F F = F F\vee F= F
得到第一层结果:T
所以201215001学生满足条件
进入第一层:使用DISTINCT,跳过相同的值,取下一个Sno,201215121

重复上述操作

刚开始忘记了写SCY.Sno = ‘201215122’的条件,结果里只有‘201215121’这一个值。不加学号的条件的话,就是选修了所有SC表中出现的课程的学生学号。

这里可以简单理解为

SCY是先经过Sno = ‘201215122’这个条件的选择后再选Cno的值。就像是先产生一个中间关系,在与内层中的SCZ做连接。
(不知道这样理解对不对,但是感觉这样子会好理解很多)

集合查询

并——UNION
交——INTERSECT
差——EXCEPT

前提:

  • 参加集合操作的各查询结果列数必须相同
  • 对应项的数据类型必须相同

【例3.64】查询CS系中学生及年龄不大于19岁的学生

及 —— 并

SELECT *
FROM Student
WHERE Sdept = 'CS'
UNION
SELECT *
FROM Student
WHERE Sage <= 19;

在这里插入图片描述
就是表中除了非CS系中大于19岁的学生,其余都包括。

刚开始,写成了下面这个样子,是错误的,UNION不是谓词,是用来连接两个查询块的!!!

SELECT *
FROM Student
WHERE Sdept = 'CS'
    UNION Sage <= 19;
    --错误的!!!

UNION:将多个查询结果合并起来时,系统自动去掉重复元组
UNION ALL:将多个查询结果合并起来时,保留重复元组
(相当于在UNION这缺省值变为 DISTINCT了)

【例3.65】查询选修了1号课程或者2号课程的学生。

SELECT Sno
FROM SC
WHERE Cno = '1'
UNION
SELECT Sno
FROM SC
WHERE Cno='2';

在这里插入图片描述
【例3.66】查询CS系与年龄不大于19岁的学生交集

①集合查询

SELECT *
FROM Student
WHERE Sdept = 'CS'
INTERSECT
SELECT *
FROM Student
WHERE Sage <= 19;

②连接查询

SELECT * FROM Student WHERE Sdept = 'CS' AND Sage<=19;

在这里插入图片描述
【例3.67】查询既选修了课程1又选修了课程2的学生

①集合查询:

SELECT Sno
FROM SC 
WHERE Cno='1'
INTERSECT
SELECT Sno
FROM SC
WHERE Cno='2';

②嵌套查询:

SELECT Sno
FROM SC
WHERE Cno ='1' AND Sno IN
                   (SELECT Sno
                   FROM SC
                   WHERE Cno='2');

在这里插入图片描述
【例3.68】查询CS系学生与年龄不大于19岁学生的差集

SELECT *
FROM Student
WHERE Sdept = 'CS'
EXCEPT
SELECT *
FROM Student
WHERE Sage <= 19;

就是 CS 系中大于19岁的人
在这里插入图片描述
等价于

SELECT *
FROM Student
WHERE Sage > 19 AND Sdept = 'CS';

基于派生表的查询

子查询不仅可以出现在WHERE子句中,还可以出现在FROM子句中。子查询生成的临时派生表成为主查询的查询对象。

【例3.57】找出每个学生超出他选修课程平均成绩的课程号

SELECT Sno,Cno
FROM SC,(SELECT Sno,AVG(Grade)
         FROM SC
         GROUP BY Sno)
         AS Avg_sc(avg_sno,avg_grade)
WHERE SC.Sno = avg_sno AND SC.Grade >= avg_grade;

注意括号的位置!!!子查询块用括号括起来,不包括AS语句。
在这里插入图片描述
如果子查询中没有聚集函数,派生表可以不指定属性列,子查询SELECT子句后面的列名为其缺省属性。

因为有聚集函数的列是没有列名的,要指定。没有聚集函数,就取其原来的名字。

【例】查询所有选修了1号课程的学生姓名

SELECT Sname
FROM Student,(SELECT Sno FROM SC WHERE Cno='1') AS SC1
WHERE Student.Sno = SC1.Sno;

SC1缺省后面的属性列,对应列名仍为Sno

SELECT语句的一般形式

SELECT [ALL|DISTINCT]
<目标列表达式> [别名][,<目标列表达式> [别名]]...
FROM <表名或视图名> [别名]
     [,<表名或视图名> [别名]]...
     |(<SELECT语句>)[AS <别名>]
[WHERE<条件表达式>]
[GROUP BY <列名1>[HAVING <条件表达式>]]
[ORDER BY <列名2>[ASC|DESC]];

其中的(<SELECT语句>)[AS <别名>]就是基于派生表的查询。
不知不觉学了好多呀~

感觉EXISTS还是有些难度,通过分析可以理解例题,但是如果只给出例题,还是不知道如何写出答案那样子的语句,还是需要多练,多分析。

发布了10 篇原创文章 · 获赞 11 · 访问量 3627

猜你喜欢

转载自blog.csdn.net/fu_GAGA/article/details/104982161