3.4数据查询
明确在那个表中查,要查什么内容,最后显示什么内容
语句格式
SELECT [ALL|DISTINCT] <目标列表达式>[,<目标列表达式>] …
FROM <表名或视图名>[,<表名或视图名> ]…|(SELECT 语句)
[AS]<别名>
[ WHERE <条件表达式> ]
[ GROUP BY <列名1> [ HAVING <条件表达式> ] ]
[ ORDER BY <列名2> [ ASC|DESC ] ];
注意:
①语句中字母不分大小写
②[ ]中内容可有可无
③符号都是英文状态下的半角字符
SELECT子句:指定要显示的属性列
FROM子句:指定查询对象(基本表或视图)
WHERE子句:指定查询条件
GROUP BY子句:对查询结果按指定列的值分组,该属性列值相等的元组为一个组。通常会在每组中作用聚集函数。
HAVING短语:只有满足指定条件的组才予以输出
ORDER BY子句:对查询结果表按指定列值的升序或降序排序
3.4.1 单表查询
查询仅涉及一个表
- 选择表中的若干列
(1)查询指定列
[例3.16] 查询全体学生的学号与姓名。
SELECT Sno,Sname FROM Student;
[例3.17] 查询全体学生的姓名、学号、所在系。
SELECT Sname,Sno,Sdept FROM Student;
(2)查询全部列
功能:选出所有属性列:
格式:①在SELECT关键字后面列出所有列名 ; ②将<目标列表达式>指定为 *
[例3.18] 查询全体学生的详细记录
SELECT Sno,Sname,Ssex,Sage,Sdept FROM Student;
或
SELECT * FROM Student;
(3)查询经过计算的值
功能:选出表中指定的属性列,并经过计算后输出
格式:SELECT子句的<目标列表达式>不仅可以为表中的属性列,也可以是表达式(算数表达式、字符串常量、函数、列别名(Sno=学号))
[例3.19] 查全体学生的姓名及其出生年份。
SELECT Sname,2014-Sage /*假设当时为2014年*/
FROM Student;
输出结果:
Sname 2014-Sage
李勇 1994
刘晨 1995
王敏 1996
张立 1995
[例3.20] 查询全体学生的姓名、出生年份和所在的院系,要求用小写字母表示系名。
SELECT Sname,'Year of Birth: ',2014-Sage,LOWER(Sdept)
FROM Student;
输出结果:
Sname 'Year of Birth:' 2014-Sage LOWER(Sdept)
李勇 Year of Birth: 1994 cs
刘晨 Year of Birth: 1995 cs
王敏 Year of Birth: 1996 ma
张立 Year of Birth: 1995 is
①lower()函数功能将字符编程小写
②’Year of Birth:'是字符串常量,写下后,原样输出
eg:使用列别名改变查询结果的列标题:
SELECT Sname NAME,'Year of Birth:' BIRTH,
2014-Sage BIRTHDAY,LOWER(Sdept) DEPARTMENT
FROM Student;
输出结果:
NAME BIRTH BIRTHDAY DEPARTMENT
李勇 Year of Birth: 1994 cs
刘晨 Year of Birth: 1995 cs
王敏 Year of Birth: 1996 ma
张立 Year of Birth: 1995 is
- 选择表中的若干元组
(1)消除取值重复的行
如果没有指定DISTINCT关键词,则缺省为ALL
[例3.21] 查询选修了课程的学生学号。
SELECT Sno FROM SC;
等价于:
SELECT ALL Sno FROM SC;
执行上面的SELECT语句后,结果为:
Sno
201215121
201215121
201215121
201215122
201215122
eg:指定DISTINCT关键词,去掉表中重复的行
SELECT DISTINCT Sno FROM SC;
执行结果:
Sno
201215121
201215122
(2)查询满足条件的元组
1.三段论:①最终显示的什么 ,SELECT后面;②查询的目标在那个表中,FROM后面;③查询条件,WHERE后面
2.涉及到多表查询时,有时WHERE条件中会涉及到其他的表
3.字符需要用‘’括住,;表示SELECT查询语句已经完成
4.涉及到多个列时,列之间用,分隔
① 比较大小
[例3.22] 查询计算机科学系全体学生的名单。
SELECT Sname
FROM Student
WHERE Sdept=‘CS’;
[例3.23] 查询所有年龄在20岁以下的学生姓名及其年龄。
SELECT Sname,Sage
FROM Student
WHERE Sage < 20;
数据库里数字不等于字符,查询时不需要加‘’
[例3.24] 查询考试成绩有不及格的学生的学号。
SELECT DISTINCT Sno
FROM SC
WHERE Grade<60;
② 确定范围
谓词: BETWEEN … AND …
NOT BETWEEN … AND …
[例3.25] 查询年龄在20~23岁(包括20岁和23岁)之间的学生的姓名、系别和年龄
SELECT Sname, Sdept, Sage
FROM Student
WHERE Sage BETWEEN 20 AND 23;
[例3.26] 查询年龄不在20~23岁之间的学生姓名、系别和年龄
SELECT Sname, Sdept, Sage
FROM Student
WHERE Sage NOT BETWEEN 20 AND 23;
SELECT、FROM……等语句写不写在一行都可以
③ 确定集合
谓词:IN <值表>, NOT IN <值表>
[例3.27] 查询计算机科学系(CS)、数学系(MA)和信息系(IS)学生的姓名和性别。
SELECT Sname, Ssex
FROM Student
WHERE Sdept IN ('CS','MA’,'IS' );
集合之间元素用,分隔
[例3.28] 查询既不是计算机科学系、数学系,也不是信息系的学生的姓名和性别。
SELECT Sname, Ssex
FROM Student
WHERE Sdept NOT IN ('IS','MA’,'CS' );
④ 字符匹配(模糊查询)
谓词: [NOT] LIKE ‘<匹配串>’ [ESCAPE ‘ <换码字符>’]
- <匹配串>可以是一个完整的字符串,也可以含有通配符%和 (常用只有%和)
- ① % (百分号) 代表任意长度(长度可以为0)的字符串 : 例如a%b表示以a开头,以b结尾的任意长度的字符串
②_ (下横线) 代表任意单个字符。例如a_b表示以a开头,以b结尾的长度为3的任意字符串
(1) 匹配串为固定字符串
[例3.29] 查询学号为201215121的学生的详细情况。
SELECT *
FROM Student
WHERE Sno LIKE ‘201215121';
等价于:
知道确切情况,一般用这种方法二不用上面的方法
SELECT *
FROM Student
WHERE Sno = ' 201215121 ';
(2)匹配串为含通配符的字符串
[例3.30] 查询所有姓刘学生的姓名、学号和性别。
SELECT Sname, Sno, Ssex
FROM Student
WHERE Sname LIKE '刘%';
[例3.31] 查询姓"欧阳"且全名为三个汉字的学生的姓名。
SELECT Sname
FROM Student
WHERE Sname LIKE '欧阳__';
[例3.32] 查询名字中第2个字为"阳"字的学生的姓名和学号。
SELECT Sname,Sno
FROM Student
WHERE Sname LIKE '__阳%';
[例3.33] 查询所有不姓刘的学生姓名、学号和性别。
SELECT Sname, Sno, Ssex
FROM Student
WHERE Sname NOT LIKE '刘%';
(3)使用换码字符将通配符转义为普通字符
[例3.34] 查询DB_Design课程的课程号和学分。
SELECT Cno,Ccredit
FROM Course
WHERE Cname LIKE 'DB\_Design'
ESCAPE '\ ' ;
[例3.35] 查询以"DB_"开头,且倒数第3个字符为 i的课程的详细情况。
SELECT *
FROM Course
WHERE Cname LIKE 'DB\_%i_ _' ESCAPE '\ ' ;
ESCAPE ‘\’ 表示“ \” 为换码字符,后面符号维持不变
⑤ 涉及空值的查询
谓词: IS NULL 或 IS NOT NULL
“IS” 不能用 “=” 代替
[例3.36] 某些学生选修课程后没有参加考试,所以有选课记录,但没 有考试成绩。查询缺少成绩的学生的学号和相应的课程号。
SELECT Sno,Cno
FROM SC
WHERE Grade IS NULL
[例3.37] 查所有有成绩的学生学号和课程号。
SELECT Sno,Cno
FROM SC
WHERE Grade IS NOT NULL;
⑥多重条件查询
逻辑运算符:AND和 OR来连接多个查询条件
AND的优先级高于OR
可以用括号改变优先级
[例3.38] 查询计算机系年龄在20岁以下的学生姓名。
SELECT Sname
FROM Student
WHERE Sdept= 'CS' AND Sage<20;
[例3.27] 查询计算机科学系(CS)、数学系(MA)和信息系(IS)学生的姓名和性别。
SELECT Sname, Ssex
FROM Student
WHERE Sdept IN ('CS ','MA ','IS')
可改写为:
SELECT Sname, Ssex
FROM Student
WHERE Sdept= ' CS' OR Sdept= ' MA' OR Sdept= 'IS ';
3.ORDER BY子句(排序)
(1)可以按一个或多个属性列排序
(2)升序:ASC(由小到大) ; 降序:DESC ; 缺省值为升序
(3)对于空值,默认最大,升序时在最后显示,降序时在最先显示。
[例3.39] 查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列。
SELECT Sno, Grade
FROM SC
WHERE Cno= ' 3 '
ORDER BY Grade DESC;
[例3.40] 查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列。
SELECT *
FROM Student
ORDER BY Sdept, Sage DESC;
按那些条件排序,顺序写在ORDER BY之后,用,分隔
4.聚集函数
统计元组个数 | COUNT(*) |
---|---|
统计一列中值的个数 | COUNT([DISTINCT / ALL] <列名>) |
计算一列值的总和(此列必须为数值型) | SUM([DISTINCT / ALL] <列名>) |
计算一列值的平均值(此列必须为数值型) | AVG([DISTINCT / ALL] <列名>) |
求一列中的最大值 | MAX([DISTINCT / ALL] <列名>) |
求一列中的最小值 | MIN([DISTINCT / ALL] <列名>) |
默认值为ALL
使用在SELECT后面和GROUP BY的HAVING子句中
DISTINCT是去掉重复值
[例3.41] 查询学生总人数。
SELECT COUNT(*)
FROM Student;
[例3.42] 查询选修了课程的学生人数。
SELECT COUNT(DISTINCT Sno)
FROM SC;
[例3.43] 计算1号课程的学生平均成绩。
SELECT AVG(Grade)
FROM SC
WHERE Cno= ' 1 ';
[例3.44] 查询选修1号课程的学生最高分数。
SELECT MAX(Grade)
FROM SC
WHERE Cno='1';
[例3.45 ] 查询学生201215012选修课程的总学分数。
SELECT SUM(Ccredit)
FROM SC,Course
WHERE Sno='201215012' AND SC.Cno=Course.Cno;
SC.Cno=Course.Cno做了一个SC表和Course表的等值连接
5.GROUP BY子句
细化聚集函数的作用对象
(1)如果未对查询结果分组,聚集函数将作用于整个查询结果
(2) 对查询结果分组后,聚集函数将分别作用于每个组
(3)按指定的一列或多列值分组,值相等的为一组
eg: 都是计算机系的分为一组。按学号分,001号选修课程为一组
[例3.46] 求各个课程号及相应的选课人数。
SELECT Cno,COUNT(Sno)
FROM SC
GROUP BY Cno;
查询结果可能为:
Cno COUNT(Sno)
1 22
2 34
3 44
4 33
5 48
- 用GROUP BY分组,SELECT后面只能写两种情况:①GROUP BY后面出现的列 ; ②聚集函数
- GROUP BY分组后,必须用HAVING指定筛选条件,不能用WHERE
[例3.47] 查询选修了3门以上课程的学生学号。
SELECT Sno
FROM SC
GROUP BY Sno
HAVING COUNT(*) >3;
[例3.48 ] 查询平均成绩大于等于90分的学生学号和平均成绩
下面的语句是不对的:
SELECT Sno, AVG(Grade)
FROM SC
WHERE AVG(Grade)>=90
GROUP BY Sno;
因为WHERE子句中是不能用聚集函数作为条件表达式,正确的查询语句应该是:
SELECT Sno, AVG(Grade)
FROM SC
GROUP BY Sno
HAVING AVG(Grade)>=90;
HAVING短语与WHERE子句的区别:
①作用对象不同
WHERE子句作用于所有的查询结果,即基表或视图,从中选择满足条件的元组
HAVING短语作用于组,从中选择满足条件的组。
②WHERE子句中是不能用聚集函数作为条件表达式
3.4.2 连接查询
连接查询:同时涉及两个以上的表的查询
- 等值与非等值连接查询
连接条件或连接谓词:WHERE子句中用来连接两个表的条件
一般格式:
[<表名1>.]<列名1> <比较运算符> [<表名2>.]<列名2>
Student.Sno=SC.Sno
[<表名1>.]<列名1> BETWEEN [<表名2>.]<列名2> AND [<表名2>.]<列名3>
注意:
- 连接运算符为“=”是等值连接,其他为非等值连接
- 连接字段:连接谓词中的列(eg:Sno)名称,连接条件中的各连接字段类型必须是可比的,但名字不必相同
[例 3.49] 查询每个学生及其选修课程的情况
多个表必须将表也写出来
SELECT Student.*, SC.*
FROM Student, SC
WHERE Student.Sno = SC.Sno;
- 自然连接:将[例 3.49]的重复列去掉
- Sname,Ssex,Sage,Sdept是Student表中的信息,所以不需要在前面写上表,但最好写上,这样程序的阅读性强
[例 3.50] 对[例 3.49]用自然连接完成。
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student,SC
WHERE Student.Sno = SC.Sno;
连接操作的执行过程
(1)嵌套循环法(NESTED-LOOP)(了解)
① 首先在表1中找到第一个元组,然后从头开始扫描表2,逐一查找满足连接件的元组,找到后就将表1中的第一个元组与该元组拼接起来,形成结果表中一个元组。
② 表2全部查找完后,再找表1中第二个元组,然后再从头开始扫描表2,逐一查找满足连接条件的元组,找到后就将表1中的第二个元组与该元组拼接起来,形成结果表中一个元组。
③ 重复上述操作,直到表1中的全部元组都处理完毕
2.自身连接
自身连接:一个表与其自己进行连接
说明:①需要给表起别名以示区别
②由于所有属性名都是同名属性,因此必须使用别名前缀
[例 3.52] 查询每一门课的间接先修课(即先修课的先修课)
SELECT FIRST.Cno, SECOND.Cpno
FROM Course FIRST, Course SECOND
WHERE FIRST.Cpno = SECOND.Cno;
3.外连接
外连接与普通连接的区别:
①普通连接操作只输出满足连接条件的元组
②外连接操作以指定表为连接主体,将主体表中不满足连接条件的元组一并输出
③左外连接:列出左边关系中所有的元组 , LEFT OUT JOIN SC ON
④右外连接:列出右边关系中所有的元组 , RIGHT OUT JOIN SC ON
[例 3. 53] 改写[例 3.49]
[例 3.49] 查询每个学生及其选修课程的情况
SELECT Student.*, SC.*
FROM Student, SC
WHERE Student.Sno = SC.Sno;
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student LEFT OUT JOIN SC ON
(Student.Sno=SC.Sno);
4.多表连接
多表连接:两个以上的表进行连接
[例3.54] 查询每个学生的学号、姓名、选修的课程名及成绩
SELECT Student.Sno, Sname, Cname, Grade
FROM Student, SC, Course /*多表连接*/
WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;
3.4.3 嵌套查询
一个SELECT-FROM-WHERE语句称为一个查询块
嵌套查询概述:将一个查询块嵌套在另一个查询块的WHERE子句或HAVING短语的条件中的查询称为嵌套查询
SELECT Sname /*外层查询/父查询*/
FROM Student
WHERE Sno IN
( SELECT Sno /*内层查询/子查询*/
FROM SC
WHERE Cno= ' 2 ');
说明:
- 上层的查询块称为外层查询或父查询
- 下层查询块称为内层查询或子查询
- SQL语言允许多层嵌套查询:即一个子查询中还可以嵌套其他子查询
- 子查询的限制:不能使用ORDER BY子句
- 带有IN谓词的子查询
[例 3.55] 查询与“刘晨”在同一个系学习的学生。
(1) 此查询要求可以分步来完成(好理解)
① 确定“刘晨”所在系名
SELECT Sdept
FROM Student
WHERE Sname= ' 刘晨 ';
结果为: CS
② 查找所有在CS系学习的学生。
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept= ' CS ';
结果如下:
③ 将第一步查询嵌入到第二步查询的条件中
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept IN
(SELECT Sdept
FROM Student
WHERE Sname= ' 刘晨 ');
(2) 用自身连接完成[例 3.55]查询要求
SELECT S1.Sno, S1.Sname,S1.Sdept
FROM Student S1,Student S2
WHERE S1.Sdept = S2.Sdept AND
S2.Sname = '刘晨';
[例 3.56] 查询选修了课程名为“信息系统”的学生学号和姓名
(1)嵌套查询
SELECT Sno,Sname ③ 最后在Student关系中
FROM Student 取出Sno和Sname
WHERE Sno IN
(SELECT Sno ② 然后在SC关系中找出选
FROM SC 修了3号课程的学生学号
WHERE Cno IN
(SELECT Cno ① 首先在Course关系中找出
FROM Course “信息系统”的课程号,为3号
WHERE Cname= '信息系统'
)
);
(2)用连接查询实现[例 3.56] :
SELECT Sno,Sname
FROM Student,SC,Course
WHERE Student.Sno = SC.Sno AND
SC.Cno = Course.Cno AND
Course.Cname='信息系统';
不相关子查询:子查询的查询条件不依赖于父查询
相关子查询:子查询的查询条件依赖于父查询
2.带有比较运算符的子查询
当能确切知道内层查询返回单值时,可用比较运算符(>,<,=,>=,<=,!=或< >)。
在 [例 3.55] 中,由于一个学生只可能在一个系学习,则可以用 = 代替 IN :
SELECT Sno,Sname,Sdept
FROM Student
WHERE Sdept =
(SELECT Sdept
FROM Student
WHERE Sname= '刘晨');
[例 3.57 ] 找出每个学生超过他选修课程平均成绩的课程号。
先查找平均成绩,Grade在SC表中,之所以用有y.Sno=x.Sno这个条件,因为执行要分组
SELECT Sno, Cno
FROM SC x
WHERE Grade >=(SELECT AVG(Grade)
FROM SC y
WHERE y.Sno=x.Sno);
可能的执行过程
①从外层查询中取出SC的一个元组x,将元组x的Sno值(201215121)传送给内层查询。
SELECT AVG(Grade)
FROM SC y
WHERE y.Sno='201215121‘;
②执行内层查询,得到值88(近似值),用该值代替内层查询,得到外层查询:
SELECT Sno,Cno
FROM SC x
WHERE Grade >=88;
③执行这个查询,得到
(201215121,1)
(201215121,3)
然后外层查询取出下一个元组重复做上述①至③步骤,直到外层的SC元组全部处理完毕。结果为:
(201215121,1)
(201215121,3)
(201215122,2)
- 带有ANY(SOME)或ALL谓词的子查询
ANY表示任意一个值,ALL表示所有值
[例 3.58] 查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄
SELECT Sname,Sage
FROM Student
WHERE Sage < ANY (SELECT Sage
FROM Student
WHERE Sdept= ' CS ')
AND Sdept <> ‘CS ' ; /*父查询块中的条件 */
结果:
执行过程:
(1)首先处理子查询,找出CS系中所有学生的年龄,构成一个集合(20,19)
(2)处理父查询,找所有不是CS系且年龄小于 20 或 19的学生
用聚集函数实现[例 3.58]
SELECT Sname,Sage
FROM Student
WHERE Sage < (SELECT MAX(Sage)
FROM Student
WHERE Sdept= 'CS ')
AND Sdept <> ' CS ';
[例 3.59] 查询非计算机科学系中比计算机科学系所有学生年龄都小的学生姓名及年龄。
(1)用ALL谓词
SELECT Sname,Sage
FROM Student
WHERE Sage < ALL
(SELECT Sage
FROM Student
WHERE Sdept= ' CS ')
AND Sdept <> ' CS ’;
(2)用聚集函数
SELECT Sname,Sage
FROM Student
WHERE Sage < (SELECT MIN(Sage)
FROM Student
WHERE Sdept= ' CS ')
AND Sdept <>' CS ';
说明: 表3.7 ANY(或SOME),ALL谓词与聚集函数、IN谓词的等价转换关系
4.带有EXISTS谓词的子查询
(1)EXISTS谓词
①存在量词
②带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。
- 若内层查询结果非空,则外层的WHERE子句返回真值
- 若内层查询结果为空,则外层的WHERE子句返回假值
③由EXISTS引出的子查询,其目标列表达式通常都用 * ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。
[例 3.60] 查询所有选修了1号课程的学生姓名。
思路分析:
- 本查询涉及Student和SC关系
- 在Student中依次取每个元组的Sno值,用此值去检查SC表
- 若SC中存在这样的元组,其Sno值等于此Student.Sno值,并且其Cno= ‘1’,则取此Student.Sname送入结果表
连接查询:以学号相等的条件将两个表连在一起,再查询(理解容易)
SELECT Sname
FROM Student
WHERE Student.Sno=SC.Sno AND SC.Cno=‘1’
嵌套查询:先在Student表中每一条记录带入SC表中查询,直到最后一条记录,如果有这个条件,就返回True,否则返回False(效率高)
SELECT Sname
FROM Student
WHERE EXISTS
(SELECT *
FROM SC
WHERE Sno=Student.Sno AND Cno= ' 1 ');
[例 3.61] 查询没有选修1号课程的学生姓名。
SELECT Sname
FROM Student
WHERE NOT EXISTS
(SELECT *
FROM SC
WHERE Sno = Student.Sno AND Cno='1');
(2)NOT EXISTS谓词
- 若内层查询结果非空,则外层的WHERE子句返回假值
- 若内层查询结果为空,则外层的WHERE子句返回真值
(3)不同形式的查询间的替换
① 一些带EXISTS或NOT EXISTS谓词的子查询不能被其他形式的子查询等价替换
② 所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换
(4)用EXISTS/NOT EXISTS实现全称量词(难点)
① SQL语言中没有全称量词 (For all)
② 可以把带有全称量词的谓词转换为等价的带有存在量词的谓词:
[例 3.55] 查询与“刘晨”在同一个系学习的学生。 可以用带EXISTS谓词的子查询替换:
用到同一个表,但在不同的位置,需起一个列别名
S2.Sdept = S1.Sdept的意思是将S1的系名带到S2中
SELECT Sno,Sname,Sdept
FROM Student S1
WHERE EXISTS(SELECT *
FROM Student S2
WHERE S2.Sdept = S1.Sdept AND
S2.Sname = '刘晨');
[例 3.62] 查询选修了全部课程的学生姓名。
在最内层的语句中,限定Sno和Cno这两个条件组合,两次取反
SELECT Sname
FROM Student
WHERE NOT EXISTS
(SELECT *
FROM Course
WHERE NOT EXISTS
(SELECT *
FROM SC
WHERE Sno= Student.Sno
AND Cno= Course.Cno
)
);
(5) 用EXISTS/NOT EXISTS实现逻辑蕴含(难点)
SQL语言中没有蕴涵(Implication)逻辑运算,可以利用谓词演算将逻辑蕴涵谓词等价转换为:
p:我来过北京 q:我来过中国
[例 3.63] 查询至少选修了学生201215122选修的全部课程的学生号码。
解题思路: 用逻辑蕴涵表达:查询学号为x的学生,对所有的课程y,只要201215122学生选修了课程y,则x也选修了y。 形式化表示:
用P表示谓词 “学生201215122选修了课程y” 用q表示谓词 “学生x选修了课程y” 则上述查询为:
用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 SCZ.Sno=SCX.Sno AND
SCZ.Cno=SCY.Cno));