SQL之高级查询篇

1. CASE函数

CASE函数是一种多分支函数,它可以根据条件列表的值返回多个可能的结果表达式中的一个。

1.1 简单CASE函数

CASE input_expression
    WHEN when_expression THEN result_expression
    [...n]
    [ELSE else_expression]
END
  • 1
  • 2
  • 3
  • 4
  • 5
  • input_expression:所计算的表达式,可以是一个变量名、字段名、函数或子查询。
  • when_expression :要与input _expression进行比较的简单表达式。简单表达式中不可包含比较运算法,只需给出被比较的表达式或值。
  • else_expression : 比较结果均不为TRUE时返回的表达式。
(查询选了JAVA课程的学生的学号、姓名、所在系、成绩,
若所在系为“计算机系”,则显示“CS”;若所在系为“信息管理系”,则显示“IM”;若所在系为“通信工程系”,则显示“COM”)
SELECT S.Sno 学号,Sname 姓名,
    CASE Dept
        WHEN '计算机系' THEN 'CS'
        WHEN '信息管理系' THEN 'IM'
        WHEN '通信工程系' THEN 'COM'
    END AS 所在系,Grade 成绩
    FROM Student S JOIN SC ON S.Sno = SC.Sno 
    JOIN Course C ON C.Cno = SC.Cno
    WHERE Cname = 'Java'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

1.2 搜索CASE函数

简单 CASE函数只能将input_expression与一个单值进行比较,如果需要跟一个范围内的值进行比较,就需要搜索CASE函数

CASE 
    WHEN Boolean_expression THEN result_expression
    [...n]
    [ELSE else_expression]
END
  • 1
  • 2
  • 3
  • 4
  • 5
  • Boolean_expression :比较表达式,可以包含比较运算符,直接将两者进行比较。
上述例子也可以用搜索CASE函数:
SELECT S.Sno 学号,Sname 姓名,
    CASE 
        WHEN Dept = '计算机系' THEN 'CS'
        WHEN Dept = '信息管理系' THEN 'IM'
        WHEN Dept = '通信工程系' THEN 'COM'
    END AS 所在系,Grade 成绩
    FROM Student S JOIN SC ON S.Sno = SC.Sno 
    JOIN Course C ON C.Cno = SC.Cno
    WHERE Cname = 'Java'

(查询C001课程的考试情况,列出学号和成绩,然后根据成绩划分等级)
SELECT S.Sno 学号,Sname 姓名,
    CASE 
        WHEN Grade >= 90 THEN '优'
        WHEN Grade BETWEEN 80 AND 99 THEN '良'
        WHEN Grade BETWEEN 70 AND 79 THEN '中'
        WHEN Grade BETWEEN 60 AND 69 THEN '及格'
    END AS 成绩
    FROM  SC ON WHERE Cno = 'C001'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21




2. 子查询

如果一个SELECT语句嵌套在另一个SELECT、INSERT、UPDATE或DELETE语句中,则称为子查询或内层查询;而包含子查询的语句称为主查询。

子查询通常用于满足下列需求之一:

  • 把一个查询分解成一系列的逻辑步骤
  • 提供一个列表作为WHERE子句和IN、EXISTS、ANY、ALL的目标对象
  • 提供由外层查询中每一条记录驱动的查询

子查询通常有几种形式:

  • WHERE 列名 [NOT] IN (子查询)
  • WHERE 列名 比较运算符 (子查询)
  • WHERE EXISTS(子查询)

2.1 使用基于集合测试的嵌套子查询

使用嵌套子查询进行基于集合的测试时,子查询返回的是一个值列表,外层查询通过运算符 IN 或 NOT IN,对子查询返回的结果集进行比较。

SELECT <查询列表> FROM ...
WHERE <列名> [NOT] IN (
SELECT <列名> FROM)
  • 1
  • 2
  • 3

包含这种子查询形式的查询语句是分步骤实现的,即先执行子查询,然后在子查询的结果基础上执行外层查询(先内后外)。子查询返回的结果是一个集合,外层查询就是在这个集合上使用IN运算符进行比较。

(查询与刘晨在同一系的学生)
SELECT Sno,Sname,Dept FROM Student WHERE Dept IN
    (SELECT Dept From Student Where Sname = '刘晨')

(查询选修了JAVA课程学生的姓名)
SELECT Sname FROM Stduent WHERE Sno IN(
    SELECT Sno FROM SC WHERE Cno IN(
        SELECT Cno FROM Course WHERE Cname = 'JAVA'))

第二个例子也可以用了连接查询来做:
SELECT Sname From Student S JOIN SC ON S.Sno = SC.Sno JOIN Course C ON C.Cno = SC.Cno WHERE Cname = 'JAVA'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

上述的例子可以看出子查询连接查询可以互通,但在某些情况下是不可以的:

(统计选了JAVA 课程的学生的姓名和所在系)
子查询:
SELECT Sno 学号,COUNT(*) 选课门数,AVG(Grade) 平均成绩
    FROM SC WHERE Sno IN(
      SELECT Sno FROM SC JOIN Course C 
        ON C.Cno = SC.Cno
          WHERE Cname = 'JAVA')
     GROUP BY Sno
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个查询不能完全用连接查询 实现,因为这个查询的语义是要先找出选了JAVA课程的学生,然后再计算这些学生的选课门数和平均成绩。

连接查询:

SELECT Sno 学号,COUNT(*) 选课门数,AVG(Grade) 平均成绩
        FROM SC JOIN Course C ON C.Cno = SC.Cno WHERE Cname = 'JAVA'       GROUP BY Sno
  • 1
  • 2

以上查询是错误的,查出来学生的选课门数都是1 !!! 实际上这个1指的是JAVA这一门课程,,其平均成绩也是JAVA成绩。




【注意:】连接查询和子查询的区别:★★★★★

  • 之所以这样,是因为在执行有连接操作的查询时,系统首先将所有被连接的表连接成一张大表,这张大表中的数据全部满足连接条件的数据。之后再在这张连接后的大表上执行WHERE子句,然后是GROUP BY子句。

执行完WHERE子句之后,连接的大表中的数据就只剩下JAVA这一门课程的情况了,显然不符情况。

  • 对于含有嵌套的子查询的查询,是先执行子查询,然后在子查询的结果基础上再执行外层查询。



【注意:】在子查询中否定和在外查询中否定的区别 ★★★★★

IN 和 != 的搭配 相较于 NOT IN 和 =的搭配是否相同? 
在子查询中否定和在外查询中否定的区别?

(查询没选C001课程的学生的姓名和所在系)

1.多表连接:
SELECT Sname,Dept FROM Student S JOIN SC ON S.Sno= SC。Sno WHERE Cno != 'C001'

2. 嵌套子查询:
1)在子查询中否定
SELECT Sname,Dept FROM Student
    WHERE Sno NOT IN(
      SELECT Sno FROM SC WHERE Cno = 'C001')

2)在外层查询中否定
SELECT Sname,Dept FROM Student
    WHERE Sno IN(
      SELECT Sno FROM SC WHERE Cno != 'C001')
  • 1
  • 2这个例子,连接查询是错误的,嵌套子查询中方法一在子查询中的否定是错误的!嵌套子查询中方法二在外查询中的否定是正确的!

【连接查询】: 
- 上面已经讲过,对于连接查询,所有的条件都是在连接之后的结果表上进行的,而且是逐行进行判断。一旦发现满足要求“Cno != ‘C001’”,则此行满足条件。所以最后的结果既包括没有选C001课程的学生,也包含选了C001同时选了别的课程的学生。

【含有嵌套的子查询】: 
对于含有嵌套的子查询的查询,是先执行子查询,然后在子查询的结果基础上再执行外层查询。而且在子查询中也是逐行判断的,当发现有满足条件的数据时,将此行数据作为外行查询的一个比较条件。

本例要查询的是某个学生所选的全部课程中均不包含C001课程,如果将否定放在子查询中,则查出的学生既包括没有选C001课程的学生,也包含选了C001同时选了别的课程的学生。显然,这个否定的范围不够。

通常情况下,对于这种带有部分否定条件的查询都应该用子查询来实现,而且应该放在外层!




2.2 使用比较测试的嵌套子查询

SELECT <查询列表> FROM...
    WHERE <列名> 比较运算符 (
      SELECT <列名> FROM ...)
  • 1
  • 2
  • 3

使用嵌套子查询进行比较测试时,要求子查询只能返回单个值。外层查询一般通过比较运算符(=、<>、 <= 、>=),将外层查询中某个列的值与子查询返回的值进行比较。

(查询选了C004 课程且成绩高于此课程的平均成绩的学生的学号和成绩)
SELECT Sno, Grade FROM SC
    WHERE Cno = 'C004' AND Grade >(
      SELECT AVG(Grade) FROM SC WHERE Cno = 'C004'

2.2 使用SOME 和 ALL 嵌套子查询

当子查询返回单值时,可以使用比较运算符进行比较,但返回多值时,就需要通过SOME和ALL修饰,同时必须使用比较操作符!

WHERE <列名> 比较运算符 [SOME | ALL](子查询)

(查询其他学期开设的课程中比第一学期开设课程的学分少的课程名、开课学期、学分)
SELECT Cname,Semester,Credit FROM Course
    WHERE Credit < SOME(
      SELECT Credit FROM Course
        WHERE Semester = 1)
AND Semester != 1

该语句实际上等价于:查询比第一学期学分最高的课程的学分小的其它学期的课程名、开课学期和学分:

SELECT Cname,Semester,Credit FROM Course
    WHERE Credit < (
      SELECT MAX(Credit) FROM Course
        WHERE Semester = 1)
AND Semester != 1


(查询至少有一次成绩大于或等于90分的学生的学号及选修的全部课程的课程号和考试成绩)
SELECT Sname,Cno,Grade FROM Student S
    JOIN SC ON S.Sno = SC.Sno
     WHERE S.Sno = SOME (
      SELECT Sno FROM SC 
          WHERE Grade >= 90)

该语句实际上是查询成绩大于或等于90的学生的学号及选修的全部课程的课程号和考试成绩 
,可用子查询来做:

SELECT Sname,Cno,Grade FROM Student S
    JOIN SC ON S.Sno = SC.Sno
     WHERE S.Sno IN (
      SELECT Sno FROM SC 
          WHERE Grade >= 90)

猜你喜欢

转载自blog.csdn.net/qq_36561697/article/details/80713463