3.4.6 子查询

当一个查询是另一个查询的条件时,称之为子查询。主要有4种语法的子查询:

·带有谓词IN的子查询:expression [NOT] IN (sqlstatement)。

·带有EXISTS谓词的子查询:[NOT] EXISTS (sqlstatement)。

·带有比较运算符的子查询:comparison(>,<,=,!=)(sqlstatement)。

·带有ANY(SOME)或ALL谓词的子查询:comparison [ANY|ALL|SOME] (sqlstatement)。

我们还用前面例子中班级表“class”和学生表“studtent”中的数据。

下面用带有谓词IN的子查询来查询“初二(1)班”的学生记录:


osdba=# SELECT * FROM student WHERE class_no in (select no FROM class where class_name = '初二(1)班');
 no | student_name | age | class_no
----+--------------+-----+----------
  1 | 张三         |  14 |        1
  2 | 吴二         |  15 |        1
(2 rows)

上面的查询也可以用带EXISTS谓词的子查询来实现:


osdba=# SELECT * FROM student s WHERE EXISTS (SELECT 1 FROM class c WHERE s.class_no=c.no AND c.class_name = '初二(1)班');
no | student_name | age | class_no
----+--------------+-----+----------
  1 | 张三         |  14 |        1
  2 | 吴二         |  15 |        1
(2 rows)

此查询还可以用带有比较符(这里用的是“=”)的子查询来实现:


osdba=# SELECT * FROM student WHERE class_no = (SELECT no FROM class c WHERE class_name = '初二(1)班');
no | student_name | age | class_no
----+--------------+-----+----------
  1 | 张三         |  14 |        1
  2 | 吴二         |  15 |        1
(2 rows)

此查询还可以用带有ANY(SOME)或ALL谓词的子查询来实现:


osdba=# SELECT * FROM student WHERE class_no = any(SELECT no FROM class c WHERE class_name = '初二(1)班');
no | student_name | age | class_no
----+--------------+-----+----------
  1 | 张三         |  14 |        1
  2 | 吴二         |  15 |        1
(2 rows)

但如果我们要查询两个班级的学生记录,不能使用带有等于“=”比较符的子查询:


osdba=# SELECT * FROM student WHERE no = (SELECT no FROM class c WHERE class_name in ('初二(1)班', '初二(2)班'));
ERROR:  more than one row returned by a subquery used as an expression

上面的查询报错说子查询不能返回多行。这种不能返回多行的子查询也称标量子查询,标量子查询不仅能嵌套在WHERE子句中,也可以嵌套在SELECT的列表中,如我们要查询每个班级学生的最大年龄,则可以用如下SQL语句:


osdba=# SELECT no, class_name, (SELECT max(age) as max_age FROM student s WHERE s.no= c.no) as max_age FROM class c;
 no | class_name | max_age
----+------------+---------
  1 | 初二(1)班  |      14
  2 | 初二(2)班  |      15
  3 | 初二(3)班  |      13
  4 | 初二(4)班  |      15
(4 rows)

查询两个班级的学生记录时用带有ANY(SOME)谓词的子查询就没有问题了,示例如下:


osdba=# SELECT * FROM student WHERE class_no = any(SELECT no FROM class c WHERE class_name in ('初二(1)班', '初二(2)班'));
no | student_name | age | class_no
----+--------------+-----+----------
  1 | 张三         |  14 |        1
  2 | 吴二         |  15 |        1
  3 | 李四         |  13 |        2
  4 | 吴三         |  15 |        2
(4 rows)