热门关键字:  项目经理  分析师  CEO  中间件  报名费





嵌套查询

来源: 作者: 时间:2008-03-16 点击:

在SQL语言中,一个SELECT-FROM-WHERE语句称为一个查询块。将一个查询块嵌套在另一个查询块的WHERE子句或HAVING短语的条件中的查询称为嵌套查询或子查询。例如:

SELECT Sname
FROM Student
WHERE Sno IN
      SELECT Sno
      FROM SC
      WHERE Cno='2';

  说明:
 

在这个例子中,下层查询块 SELECT Sno FROM SC WHERE Cno='2'是嵌套在上层查询块 SELECT Sname FROM Student WHERE Sno IN 的WHERE条件中的。上层的查询块又称为外层查询或父查询或主查询,下层查询块又称为内层查询或子查询。SQL语言允许多层嵌套查询。即一个子查询中还可以嵌套其它子查询。需要特别指出的是,子查询的SELECT语句中不能使用ORDER BY子句,ORDER BY子句永远只能对最终查询结果排序。

嵌套查询的求解方法是由里向外处理。即每个子查询在其上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。  

嵌套查询使得可以用一系列简单查询构成复杂的查询,从而明显地增强了SQL的查询能力。以层层嵌套的方式来构造程序正是 SQL(Structurred Query Language)中“结构化”的含义所在。

 
一、 带有IN谓词的子查询
  带有IN谓词的子查询是指父查询与子查询之间用IN进行连接,判断某个属性列值是否在子查询的结果中。由于在嵌套查询中,子查询的结果往往是一个集合,所以谓词IN是嵌套查询中最经常使用的谓词。
例37 查询与“刘晨”在同一个系学习的学生
  详细信息…
 

查询与“刘晨”在同一个系学习的学生,可以首先确定“刘晨”所在系名,然后再查找所有在该系学习的学生。所以可以分步来完成此查询:   
①确定“刘晨”所在系名

SELECT Sdept
FROM Student
WHERE Sname='刘晨';

结果为:

  Sdept
--------
   IS  

②查找所有在IS系学习的学生。

SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept='IS';

结果为:

   Sno     Sname    Sdept
------- ------- -------
  95001    刘晨      IS
  95004    张立      IS

分步写查询毕竟比较麻烦,上述查询实际上可以用子查询来实现,即将第一步查询嵌入到第二步查询中,用以构造第二步查询的条件。SQL语句如下:

SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept IN  
      SELECT Sdept
      FROM Student
      WHERE Sname='刘晨';  

本例中的查询也可以用我们前面学过的表的自身连接查询来完成:

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

可见,实现同一个查询可以多种方法,当然不同的方法其执行效率可能会有差别,甚至会差别很大。

例38 查询选修了课程名为'信息系统'的学生学号和姓名
  详细信息…
 

SELECT Sno, Sname
FROM Student
WHERE Sno IN
      SELECT Sno
      FROM SC
      WHERE Cno IN
            SELECT Cno
            FROM Course
            WHERE Cname='信息系统'

    Sno       Sname
------- --------
  95001     李勇
  95002     刘晨

本查询同样可以用连接查询实现:

SELECT Sno, Sname
FROM Student, SC, Course
WHERE Student.Sno=SC.Sno AND SC.Cno=Course.Cno AND Course.Cname='信息系统';

例37和例38中的各个子查询都只执行一次,其结果用于父查询,子查询的查询条件不依赖于父查询,这类子查询称为不相关子查询。不相关子查询是最简单的一类子查询。

二、 带有比较运算符的子查询
  带有比较运算符的子查询是指父查询与子查询之间用比较运算符进行连接。当用户能确切知道内层查询返回的是单值时,可以用> < = >=<=!=<>等比较运算符。
  例如:
  详细信息…
 

在例37中,由于一个学生只可能在一个系学习,也就是说内查询刘晨所在系的结果是一个唯一值,因此该查询也可以用比较运算符来实现,其SQL语句如下:

SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept =  
      SELECT Sdept
      FROM Student
      WHERE Sname='刘晨';
需要注意的是,子查询一定要跟在比较符之后,下列写法是错误的:
SELECT Sno, Sname, Sdept
FROM Student
WHERE (SELECT Sdept
            FROM Student
            WHERE Sname='刘晨') = Sdept;

例38中信息系统的课程号是唯一的,但选修该课程的学生并不只一个,所以例38也可以用=运算符和IN谓词共同完成:

SELECT Sno, Sname
FROM Student
WHERE Sno IN
      SELECT Sno
      FROM SC
      WHERE Cno =
            SELECT Cno
            FROM Course
            WHERE Cname='信息系统'

三、 带有ANY或ALL谓词的子查询
  子查询返回单值时可以用比较运算符。而使用ANY或ALL谓词时则必须同时使用比较运算符。其语义为:

> ANY 大于子查询结果中的某个值
< ANY 小于子查询结果中的某个值
>= ANY 大于等于子查询结果中的某个值
<= ANY 小于等于子查询结果中的某个值
<= ANY 小于等于子查询结果中的某个值
= ANY 等于子查询结果中的某个值
!= ANY或<> ANY 不等于子查询结果中的某个值
> ALL 大于子查询结果中的所有值
< ALL 小于子查询结果中的所有值
>= ALL 大于等于子查询结果中的所有值
<= ALL 小于等于子查询结果中的所有值
<= ALL 小于等于子查询结果中的所有值
= ALL 等于子查询结果中的所有值(通常没有实际意义)
!= ALL或<> ALL 不等于子查询结果中的任何一个值
例39 查询其他系中比IS系任一学生年龄小的学生名单
  详细信息…
 

SELECT Sname, Sage
FROM Student
WHERE Sage (SELECT Sage
      FROM Student
      WHERE Sdept="IS" )
      AND Sdept <> 'IS'
      ORDER BY Sage DESC;

本查询实际上也可以用集函数实现。

SELECT Sname, Sage
FROM Student
WHERE Sage >
      (SELECT MAX(Sage)
        FROM Student
        WHERE Sdept='IS')
AND Sdept <> 'IS'
ORDER BY Sage DESC;   

事实上,用集函数实现子查询通常比直接用ANY或ALL查询效率要高。ANY与ALL与集函数的对应关系如表3-4所示。

  表3-4 ANY、ALL谓词与集函数及IN谓词的等价转换关系  
  = <>或!= < <= > >=
───── ───── ───── ───── ───── ───── ─────
ANY IN  -- <MAX <=MAX >MIN >= MIN
ALL -- NOT IN <MIN <=MIN >MAX >= MAX
四、 带有EXISTS谓词的子查询
  EXISTS代表存在量词彐。带有EXISTS谓词的子查询不返回任何实际数据,它只产生逻辑真值“true"或逻辑假值“false"。
例40 查询所有选修了1号课程的学生姓名
  详细信息…
 

查询所有选修了1号课程的学生姓名涉及Student关系和SC关系,我们可以在Student关系中依次取每个元组的Sno值,用此Student.Sno值去检查SC关系,若SC中存在这样的元组,其SC.Sno值等于用来检查的Student.Sno值,并且其SC.Cno='1',则取此Student.Sname送入结果关系。将此想法写成SQL语句就是:

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

使用存在量词EXISTS后,若内层查询结果非空,则外层的WHERE子句返回真值,否则返回假值。

由EXISTS引出的子查询,其目标列表达式通常都用*,因为带EXISTS的子查询只返回真值或假值,给出列名亦无实际意义。

这类查询与我们前面的不相关子查询有一个明显区别,即子查询的查询条件依赖于外层父查询的某个属性值(在本例中是依赖于Student表的Sno值),我们称这类查询为相关子查询(Correlated Subquery)。求解相关子查询不能象求解不相关子查询那样,一次将子查询求解出来,然后求解父查询。相关子查询的内层查询由于与外层查询有关,因此必须反复求值。从概念上讲,相关子查询的一般处理过程是:

首先取外层查询中Student表的第一个元组,根据它与内层查询相关的属性值(即Sno值)处理内层查询,若WHERE子句返回值为真(即内层查询结果非空),则取此元组放入结果表;然后再检查Student表的下一个元组;重复这一过程,直至Student表全部检查完毕为止。

本例中的查询也可以用连接运算来实现,读者可以参照有关的例子,自己给出相应的SQL语句。  与EXISTS谓词相对应的是NOT EXISTS谓词。使用存在量词NOT EXISTS后,若内层查询结果为空,则外层的WHERE子句返回真值,否则返回假值。

例41 查询所有未修1号课程的学生姓名
  详细信息…
 

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

一些带EXISTS或NOT EXISTS谓词的子查询不能被其他形式的子查询等价替换,但所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换。例如带有IN谓词的例37可以用如下带EXISTS谓词的子查询替换:

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

由于带EXISTS量词的相关子查询只关心内层查询是否有返回值,并不需要查具体值,因此其效率并不一定低于不相关子查询,甚至有时是最高效的方法。

SQL语言中没有全称量词∨(For all)。因此必须利用谓词演算将一个带有全称量词的谓词转换为等价的带有存在量词的谓词:。

例42 查询选修了全部课程的学生姓名
  详细信息…
 

由于没有全称量词,我们将题目的意思转换成等价的存在量词的形式:查询这样的学生姓名,没有一门课程是他不选的。该查询涉及三个关系,存放学生姓名的Student表,存放所有课程信息的Course表,存放学生选课信息的SC表。其SQL语句为:

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

SQL语言中也没有蕴函(Implication)逻辑运算。因此也必须利用谓词演算将一个逻辑蕴函的谓词转换为等价的带有存在量词的谓词:

例43 查询至少选修了学生95002选修的全部课程的学生号码。
  详细信息…
 

本题的查询要求可以做如下解释,查询这样的学生,凡是95002选修的课,他都选修了。换句话说,若有一个学号为x的学生,对所有的课程y,只要学号为95002的学生选修了课程y,则x也选修了y;那么就将他的学号选出来。它所表达的语义为:不存在这样的课程y,学生95002选修了y,而学生x没有选。用SQL语言可表示如下:

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

 

上一篇:连接查询
下一篇:集合查询




最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册
企业电子商务

赞助商