多行子查询通过多行比较操作符来实现,其返回值为多行。常用的多行比较操作符包括:IN 和 NOT IN、ALL 和 ANY/SOME、EXISTS 和 NOT EXISTS 操作符,本文主要介绍这几种多行比较操作符的应用。
IN 子查询是指在外层查询和子查询之间用 IN 进行连接,判断某个属性列是否在子查询的结果中,其返回的结果中可以包含零个或者多个值。在 IN 子句中,子查询和输入多个运算符的数据的区别在于,使用多个运算符输入时,一般都会输入两个或者两个以上的数值,而使用子查询时,不能确定其返回结果的数量。但是,即使子查询返回的结果为空,语句也能正常运行。
由于在子查询中,查询的结果往往是一个集合,所以 IN 子查询是子查询中最常用的。IN 子查询语句的操作步骤可以分成两步:
【示例01】获取 tb_book 图书表和 tb_book_author 作者表中具有相同部门 (tb_author_department) 的作者所写的图书信息。SQL 语句如下:
SELECT * FROM tb_book
WHERE book_sort IN
(SELECT tb_author_department FROM tb_book_author WHERE tb_book.book_sort=tb_book_author.tb_author_department)
ORDER BY tb_book.book_price;
查询结果如下图所示:
子查询还可以用在外层查询的 NOT IN 子句中,以产生 NOT IN 使用的清单。如果外层查询中用来比较的数据被查询出与子查询产生的结果集中的所有值都不匹配,那么 NOT IN 子句返回 TRUE,然后将该记录指定列的值输入到最终的结果集中。
【示例02】查询在 tb_book 图书表中列出,而在 tb_book_author 作者表中没有列出的作者所在部门的图书信息。SQL 语句如下:
SELECT * FROM tb_book
WHERE book_sort NOT IN(SELECT tb_author_department FROM tb_book_author);
查询结果如下图所示:
小结:tb_book_author 作者表中的作者部门有 PHP 和 VC,通过与 tb_book图书表的对比,发现 ASP 和 SQL 数据库这两个部门在 tb_book 图书表中列出,而在 tb_book_author 作者表中没有列出。使用 NOT IN 子查询的查询速度很慢,在对 SQL 语句的性能有所要求的时候,就要使用性能更好的语句来替代 NOT IN。例如,可以使用外连接的方式替换 NOT IN 以提高语句的执行速度。一般来说,使用 NOT IN 和子查询的语句结构更容易理解和编写,所以只在对 SQL 语句的性能有要求时,才使用外连接来替换 NOT IN 和子查询联用的结果。
【练习1】在 course 和 grade 表中,查询没有学生参加考试的课程信息。
【练习2】在 student 表中,查询 课程成绩
大于 98 分的学生信息。
EXISTS 子查询的功能是判断子查询的返回结果中是否有数据行。如果子查询返回的结果是空集,则判断为不存在,即 EXISTS 失败,NOT EXISTS 成功。如果子查询返回至少一行的数据记录,则判断存在,即 EXISTS 成功,NOT EXISTS 失败。由于 EXISTS 子查询中不需要返回具体值,所以该子查询的选择列表常用 SELECT *
格式,其外层的 WHERE 子句中也不需要指定列名。
EXISTS 通常都和相关子查询一起使用。在使用相关子查询时,对外表中的每一行,子查询都要运行一遍,该行的值也要在子查询的 WHERE 子句中被使用。这样,通过 EXISTS 子句就能将外层表中的各行数据依次与子查询处理的内层表中的数据进行存在性比较,得到需要的结果。关键字 EXISTS 引入子查询的语法特点如下:
*
组成的。因为只是测试满足子查询的数据行的存在性,所以在子查询的 SELECT 列表清单中加入列名没有实际意义。1、比较使用 EXISTS 和 IN 的查询
EXISTS 子查询和 IN 操作符可以实现同一个功能,但是两者之间是有区别的。第 1 个查询使用 EXISTS,而第 2 个查询使用 IN。注意两个查询返回相同的信息。
(1) 使用谓词 EXISTS 的 SQL 语句如下:
SELECT DISTINCT goods_name
FROM goods
WHERE EXISTS(
SELECT * FROM brand
WHERE cat_id = goods.cat_id AND name='索尼/SONY');
(2) 使用谓词 IN 的 SQL 语句如下:
SELECT DISTINCT goods_name
FROM goods
WHERE cat_id IN(
SELECT cat_id FROM brand
WHERE cat_id = goods.cat_id AND name='索尼/SONY');
这两种方法返回相同的查询结果,如下图所示:
2、比较使用 EXISTS 和 ”=ANY“ 的查询
通过 orderform 订单表与 user_address 用户地址表,查找两个表中相同国家的收货人姓名。通过两种查询方法:
(1) 使用 EXISTS 的 SQL 语句如下:
SELECT consignee,country FROM orderform
WHERE EXISTS(SELECT country FROM user_address WHERE orderform.country=user_address.country);
(2) 使用 “=ANY” 的 SQL 语句如下:
SELECT consignee,country FROM orderform
WHERE country = ANY(SELECT country FROM user_address);
这两种方法返回相同的查询结果。
【示例03】获取 tb_book 图书表和 tb_book_author 作者表中,具有相同部门的作者所写的图书信息。SQL 语句如下:
SELECT * FROM tb_book
WHERE EXISTS(SELECT * FROM tb_book_author WHERE tb_book.book_sort=tb_book_author.tb_author_department)
ORDER BY tb_book.book_price;
查询结果如下图所示:
一些带 EXISTS 或者 NOT EXISTS 的子查询不能被其他形式的子查询等价交换,但所有带 IN、ANY、ALL 和比较运算符的子查询都能用带 EXISTS 的子查询等价替换。
3、NOT EXISTS 子查询实现两个表的差集
与 EXISTS 相对应的是 NOT EXISTS。使用 NOT EXISTS 后,如果子查询的结果为空,则外层的 WHERE 子句返回 TRUE。
【示例04】获取 tb_book 图书表与 tb_book_author 作者表中相同部门以外的图书信息。SQL 语句如下:
SELECT * FROM tb_book
WHERE NOT EXISTS(
SELECT tb_author_department
FROM tb_book_author
WHERE tb_book.book_sort=tb_book_author.tb_author_department)
ORDER BY tb_book.book_price;
【练习3】查询没参加考试的学生信息。
【练习4】查询报名人数大于平均报名人数的课程信息。
【练习5】查询学生选课表(StuCou) 中报名状态为 "报名"的课程名。
【练习6】查询已经报名选修课程的学生信息,要求显示学号和姓名。
ALL、SOME 和 ANY 是量词,允许将比较运算符左边的单值与生成单列但多行结果集的子查询(在比较运算符的右边)相比较。如果 WHERE 子句中的子查询生成多个值的单列结果,将终止以下形式的查询:
SELECT <column name list>
FROM <table>
WHERE <expression>{
=|<>|>|>=|<|<=}<subquery>
下面通过几个例子来了解如何使用量词实现多行子查询。
(1) 在 goods 商品信息表中查询同类商品中,售价高于平均售价的商品信息。SQL 语句如下:
SELECT cat_id,goods_name,shop_price
FROM goods
WHERE shop_price < (
SELECT AVG(shop_price) FROM goods GROUP BY cat_id);
执行 SQL 语句,如下图所示:
如果通过使用 3 个量词(ALL、SOME、ANY或其他) 之一引入子查询,而将查询语句改写如下:
SELECT <column name list>
FROM <table>
WHERE <expression>{
=|<>|>|>=|<|<=} {
SOME|ANY|ALL} <subquery>
如果子查询的单列结果表有不止一行的数据,此时将不会终止查询。而是将
(2) 改写上面的 SQL 语句,加入量词 SOME,再次执行查询语句,就不会出现错误信息。SQL 语句如下:
SELECT cat_id,goods_name,shop_price
FROM goods
WHERE shop_price < SOME(
SELECT AVG(shop_price) FROM goods GROUP BY cat_id);
查询结果如下图所示:
如果使用 ALL 量词,
ALL 操作符比较子查询返回列表中的每一个值。>ALL
为大于最大的;而 =ALL
则没有返回值,因为在等于子查询的情况下,返回列表中的所有值是不符合逻辑的。
ALL 允许将比较运算符前面的单值与比较运算符后面的子查询返回值的集合中的每一个值相比较。另外,仅当所有(ALL)的比较运算符左边的单值与子查询返回值的集合中的每一个值的比较都求值为 TRUE 时,比较判式(以及 WHERE子句)才求值为 TRUE。
【示例05】获取所有商品售价低于本身品牌的平均售价的商品信息。SQL 语句如下:
SELECT cat_id,goods_name,shop_price
FROM goods
WHERE shop_price < ALL(
SELECT AVG(shop_price) FROM goods GROUP BY cat_id);
ANY 操作符比较子查询返回列表中的每一个值。>ANY
为大于最小的;而 =ANY
为等于 IN。ANY 允许将比较运算符前面的单值与比较运算符后面的子查询返回值的集合中的每一个值相比较。另外,仅当所有(ANY) 的比较运算符左边的单值与子查询返回值的集合中的一个值的比较求值为 TRUE 时,比较判式(以及WHERE子句)的求值就为 TRUE。
【示例06】获取商品的售价比平均售价高的商品信息,并按商品品牌进行划分。SQL 语句如下:
SELECT cat_id,goods_name,shop_price
FROM goods
WHERE shop_price > ANY(
SELECT AVG(shop_price) FROM goods GROUP BY cat_id);
查询结果如下图所示:
量词 SOME 和 ANY 是同义的,它们都允许将比较运算符前面的单值与比较运算符后面的子查询返回的结果集中的每个值进行比较。如果比较运算符前面的单值与比较运算符后面的子查询返回的结果集中的每个值之间的任何 (ANY) 比较求值为 TRUE,那么判式(以及 WHERE 子句)求值为 TRUE。
总结:本章主要介绍了多行子查询。多行子查询通过多行比较操作符来实现,比较常用的多行比较操作符包括:IN 和 NOT IN、ALL 和 ANY/SOME、 EXISTS 和 NOT EXISTS 操作符。