数据库--子查询

将一个查询语句做为一个结果集供其他SQL语句使用,就像使用普通的表一样,被当作结果集的查询语句被称为子查询。

子查询大大简化了复杂SQL语句的编写,不过使用不当也容易造成性能问题。

子查询的语法与普通的SELECT 语句语法相同,所有可以在普通SELECT 语句中使用的特性都可以在子查询中使用,比如WHERE 子句过滤、UNION 运算符、HAVING 子句、GROUPBY 子句、ORDER BY 子句,甚至在子查询中还可以包含子查询。同时,不仅可以在SELECT语句中使用子查询,还可以在UPDATE、DELETE 等语句中使用子查询。

子查询有两种类型,

一种是只返回一个单值的子查询,这时它可以用在一个单值可以使用的地方,这时子查询可以看作是一个拥有返回值的函数;

另外一种是返回一列值的子查询,这时子查询可以看作是一个在内存中临时存在的数据表。

单值子查询
单值子查询的语法和普通的SELECT 语句没有什么不同,唯一的限制就是子查询的返回值必须只有一行记录,而且只能有一个列,又被称为标量子查询。

如果一个子查询返回值不止一行记录或者有多个列的话都不能当作标量子查询使用,否则会出错。

例:

SELECT 1 AS f1,2,(SELECT MIN(FYearPublished) FROM T_Book),

(SELECT MAX(FYearPublished) FROM T_Book) AS f4

列值子查询
与标量子查询不同,列值子查询可以返回一个多行多列的结果集,又被称为表子查询。

例:

SELECT T_Reader.FName,t2.FYear,t2.FName ,t2.F3
FROM T_Reader,
(SELECT FYearPublished AS FYear,FName,1+2 as F3 FROM T_Book WHERE FYearPublished < 1800) t2

SELECT列表中的标量子查询

SELECT FId,FName,
(
SELECTMAX(FYearPublished)
FROM T_Book
WHERE T_Book. FCategoryId= T_Category.FId
)
FROM T_Category

这里的子查询是依赖于外部查询中的T_Category.FId字段的,这个子查询是无法单独执行的

引用了外部查询中字段的子查询被称为相关子查询。

WHERE 子句中的标量子查询

标量子查询不仅可以用在SELECT 语句的列表中,它还可以用在WHERE 子句中,而且实际应用中子查询很多的时候都是用在WHERE子句中的。

要检索喜欢“Story”的读者主键列表:

SELECT FReaderId FROM T_ReaderFavorite
WHERE FCategoryId=
(
SELECT FId FROM T_Category
WHERE FName='Story'
)

要检索每一种书籍类别中出版年份最早的书籍的名称,如果有两本或者多本书籍在同一年出版,则均显示它们的名字:

SELECT T_Category.FId, T_Book. FName,T_Book.FYearPublished
FROM T_Category
INNER JOIN T_Book ON T_Category.FId=T_Book.FCategoryId
WHERE T_Book.FYearPublished=
(
SELECT MIN(T_Book.FYearPublished)
FROM T_Book
WHERE T_Book.FCategoryId=T_Category.FId
)

所有在SELECT列表中的字段如果没有包含在聚合函数中,则必须放到GROUP BY 子句中

集合运算符与子查询

如果子查询是多行多列的表子查询,那么可以将其看成一个临时的数据表使用,而如果子查询
是多行单列的表子查询,这样的子查询的结果集其实是一个集合,SQL 提供了对这样的集
合进行操作的运算符,包括IN、ANY、ALL以及EXISTS等。

IN 运算符

检索在2001、2003 和2005年出版的所有图书:

SELECT * FROM T_Book
WHERE FYearPublished IN(2001,2003,2005)

需要检索所有图书出版年份内入会的读者信息:

SELECT * FROM T_Reader
WHERE FYearOfJoin IN
(
select FYearPublished FROM T_Book
)

ANY和SOME 运算符

在 SQL中ANY 和SOME 是同义词,SOME的用法和功能和ANY一模一样。

ANY必须和其他的比较运算符共同使用,而且必须将比较运算符放在ANY 关键字之前

检索所有图书出版年份内入会的读者信息:
SELECT * FROM T_Reader
WHERE FYearOfJoin =ANY
(
select FYearPublished FROM T_Book
)

也就是说“=ANY”等价于IN 运算符,而“<>ANY”则等价于NOT IN 运算符。

ANY 运算符还可以和大于(>)、小于(<)、大于等于(>=)、小于等于(<=)等比较运算符共同使用。

注:和IN 运算符不同,ANY 运算符不能与固定的集合相匹配。

ALL运算符

ALL运算符要求比较的值需要匹配子查询中的所有值。ALL运算符同样不能单独使用,必须和比较运算符共同使用。

检索在所有会员入会之前出版的图书:
SELECT * FROM T_Book
WHERE FYearPublished<ALL
(
SELECT FYearOfJoin FROM T_Reader
)

与ANY 运算符相同,ALL 运算符同样不能与固定的集合相匹配。

注:当使用ALL 运算符的时候,如果带匹配的集合为空,也就是子查询没有返回任何数据的时候,不论与什么比较运算符搭配使用ALL的返回值将永远是true,即都符合条件,使检索出现bug。

EXISTS运算符

EXISTS运算符是单目运算符,它不与列匹配,因此它也不要求待匹配的集合是单列的。

EXISTS运算符用来检查每一行是否匹配子查询,可以认为EXISTS就是用来测试子查询的结果是否为空,如果结果集为空则匹配结果为false,否则匹配结果为true。

例:

SELECT * FROM T_Book
WHERE EXISTS
(
SELECT * FROM T_Reader WHERE FProvince='ShanDong'
)

EXISTS运算符的真正意义只有和相关子查询一起使用才更有意义。

相关子查询中引用外部查询中的这个字段,这样在匹配外部子查询中的每行数据的时候相关子查询就会根据当前行的信息来进行匹配判断了,这样就可以完成非常丰富的功能。

检索存在1950 年以前出版的图书的图书类别:
SELECT * FROM T_Category
WHERE EXISTS
(
SELECT * FROM T_Book
WHERE T_Book. FCategoryId = T_Category.FId
AND T_Book. FYearPublished<1950
)
子查询在INSERT语句中的应用

除了INSERT……VALUES……这种用法外,INSERT 语句还支持另外一种语法,那就是INSERT……SELECT……,采用这种使用方式可以将SELECT语句返回的结果集直接插入到目标表中,因为这一切都是都数据库内部完成的,所以效率非常高。

将T_ReaderFavorite中的输入复制插入到T_ReaderFavorite2表,SQL语句如下:
INSERT INTO T_ReaderFavorite2(FCategoryId,FReaderId)
SELECT FCategoryId,FReaderId FROM T_ReaderFavorite

使用INSERT……SELECT……不仅能够实现简单的将一个表中的数据导出到另外一个表中的功能,还能在将输入插入目标表之前对数据进行处理。

将T_ReaderFavorite表中的数据复制到T_ReaderFavorite2 表中,但是如果T_ReaderFavorite表中的FReaderId 列的值大于10,则将FReaderId 的值减去FCategoryId 的值后再复制到T_ReaderFavorite2 表中:

INSERT INTO T_ReaderFavorite2(FCategoryId,FReaderId)
SELECT FCategoryId,
(CASE
WHEN FReaderId<=10 THEN FReaderId
ELSE FReaderId- FCategoryId
END
)
FROM T_ReaderFavorite

INSERT……SELECT……语句不局限于同结构表间的数据插入,也可以实现异构表见输入的插入。

假设要将所有会员爱好的图书统一增加“小说”,也就是为T_Reader 表中的每个读者都在T_ReaderFavorite表中创建一条FCategoryId等于1 的记录,实现SQL语句如下:

INSERT INTO T_ReaderFavorite(FCategoryId,FReaderId)
SELECT 1,FId FROM T_Reader
WHERE NOT EXISTS
(
SELECT * FROM T_ReaderFavorite
WHERE T_ReaderFavorite. FCategoryId=1
AND T_ReaderFavorite. FReaderId= T_Reader.FId
)
子查询在UPDATE 语句中的应用

例:

UPDATE T_Book
SET FYearPublished= (SELECT MAX(FYearPublished) FROM T_Book)

如果UPDATE 语句拥有WHERE 子句,那么还可以在WHERE 子句中使用子查询,其使用方式与SELECT语句中的子查询基本相同,而且也可以使用相关子查询等高级的特性。

将所有同类书本书超过3 本的图书的出版日期更新为2005:
UPDATE T_Book b1
SET b1.FYearPublished=2005
WHERE
(
SELECT COUNT(*) FROM T_Book b2
WHERE b1. FCategoryId=b2. FCategoryId
)>3

子查询在DELETE 语句中的应用

子查询在DELETE 中唯一可以应用的位置就是WHERE 子句,使用子查询可以完成复杂的数据删除控制。其使用方式与SELECT 语句中的子查询基本相同,而且也可以使用相关子查询等高级的特性。

将所有同类书本书超过3 本的图书删除:
DELETE FROM T_Book b1
WHERE
(
SELECT COUNT(*) FROM T_Book b2
WHERE b1. FCategoryId=b2. FCategoryId
)>3

原文地址:https://www.cnblogs.com/HuXiaoxia/p/3476873.html