22.子查询
SQL语句允许将一个查询语句做为一个结果集供其他SQL语句使用,就像使用普通的表一样,被当作结果集的查询语句被称为子查询。可以将子查询看成一张暂态的数据表,这张表在查询开始时被创造,在查询结束时被删除。
select语句可以嵌套在其他语句中,比如select,insert,update以及delete等,这些被嵌套的select语句就称为子查询,可以这么说当一个查询依赖于另外一个查询结果时就可以使用子查询。子查询有两种类型,一种是只返回一个单值的子查询,这时它可以用在一个单值可以使用的地方,这时子查询可以看作是一个拥有返回值的函数;另外一种是返回一列值的子查询,这时子查询可以看作是一个在内存中临时存在的数据表。
create table T_Reader --读者信息 ( FId int not null,--Id FName varchar(50),--读者姓名 FYearOfBirth int,--出生年份 FCity varchar(50),--所在城市 FProvince varchar(50),--省份 FYearOfJoin int--读者入会年份 ) create table T_Book --书籍 ( FId int not null,--主键 FName varchar(50),--书名 FYearPublished int,--出版年份 FCategoryId int--分类编号 ) create table T_Category --分类信息 ( FId int not null,--编号 FName varchar(50)--分类名 ) create table T_ReaderFavorite--读者和读者喜爱的类别之间的对应关系 ( FCategoryId int,--分类编号 FReaderId int--读者编号 ) --读者 insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(1,'Tom',1979,'TangShan','Hebei',2003); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(2,'Sam',1981,'LangFang','Hebei',2001); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(3,'Jerry',1966,'DongGuan','GuangDong',1995); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(4,'Lily',1972,'JiaXing','ZheJiang',2005); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(5,'Marry',1985,'BeiJing','BeiJing',1999); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(6,'Kelly',1977,'ZhuZhou','HuNan',1995); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(7,'Tim',1982,'YongZhou','HuNan',2001); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(8,'King',1979,'JiNan','ShanDong',1997); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(9,'John',1979,'QingDao','ShanDong',2003); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(10,'Lucy',1978,'LuoYang','HeNan',1996); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(11,'July',1983,'ZhuMaDian','HeNan',1999); insert into T_Reader(FId,FName,FYearOfBirth,FCity,FProvince,FYearOfJoin) values(12,'Fige',1981,'JinCheng','ShanXi',2003); --书籍 insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(1,'About J2EE',2005,4); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(2,'Learning Hibernate',2003,4); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(3,'Two Cites',1999,1); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(4,'Jane Eyre',2001,1); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(5,'Oliver Twist',2002,1); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(6,'History of China',1982,2); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(7,'History of England',1860,2); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(8,'History of America',1700,2); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(9,'History of The World',2008,2); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(10,'Atom',1930,3); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(11,'RELATIVITY',1945,3); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(12,'Computer',1970,3); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(13,'Astronomy',1971,3); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(14,'How To Singing',1771,5); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(15,'DaoDeJing',2001,6); insert into T_Book(FId,FName,FYearPublished,FCategoryId) values(16,'Obedience to Authority',1995,6); --分类 insert into T_Category(FId,FName) values(1,'Story'); insert into T_Category(FId,FName) values(2,'History'); insert into T_Category(FId,FName) values(3,'Theory'); insert into T_Category(FId,FName) values(4,'Technology'); insert into T_Category(FId,FName) values(5,'Art'); insert into T_Category(FId,FName) values(6,'Philosophy'); --对应关系 insert into T_ReaderFavorite(FCategoryId,FReaderId) values(1,1); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(5,2); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(2,3); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(3,4); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(5,5); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(1,6); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(1,7); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(4,8); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(6,9); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(5,10); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(2,11); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(2,12); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(1,12); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(3,1); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(1,3); insert into T_ReaderFavorite(FCategoryId,FReaderId) values(4,4);
1.单值查询
单值子查询的语法和普通的select语句没有什么不同,唯一的限制就是子查询的返回值必须只有一行记录,而且只能有一个列。这样的子查询又被称为标量子查询,标量子查
询可以用在select语句的列表中、表达式中、where语句中等很多场合。如果一个子查询返回值不止一行记录或者有多个列的话都不能当作标量子查询使用,否则会出错。
正确:
select 1 as f1,2 as f2,(select min(FYearPublished) from T_Book) as f3,(select max(FYearPublished) from T_Book)as f4
错误:
select 1 as f1,2 as f2,(select FYearPublished from T_Book) as f3 --多行
select 1 as f1,2 as f2,(select max(FYearPublished),min(FYearPublished) from T_Book) --多列
其实标量子查询完全可以返回随当前查询记录而变化的值。这里的子查询是依赖于外部查询中的T_Category.FId字段的,这个子查询是无法单独执行的。
select FId,FName, ( select max(FYearPublished) from T_Book where T_Book.FCategoryId=T_Category.FId ) as MaxYear from T_Category
在where搜索中也可以用标量子查询来随查询记录的变化返回相应的值。下面检索没一种数据类别中出版年份最早的书籍的名称,如果有多本书籍在同一年出版,则均显示它们的名字。要求检索结果中显示出:类型的名字、书的名字、和它的出版年份。
在这个SQL语句中,T_Category表和T_Book表首先进行内部连接,然后使用where子句中使用子查询来进行数据的过滤。这个子查询是一个相关子查询,它返回外部查询中当前图书类别中的图书的最早出版年份。在外部查询的where子句中,T_Book的FYearPublished与子查询的返回值进行比较,这样就可以得到每种书籍类型中的出版最早的书籍了。
--通过内连接查询分类表和图书表 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= ( --根据外部当前的分类表的Id去图书表中获取最小的日期 --然后这个日期就作为外部查询的删选条件 select min(T_Book.FYearPublished) from T_Book where T_Book.FCategoryId=T_Category.FId )
2.列值子查询
列值子查询可以返回一个多行多列的结果集。这样的子查询又被称为表子查询,表子查询可以看作一个临时的表,表子查询可以用在select语句的from子句中、insert语句、连接、in子句等很多场合。
--将T_Reader表和表子查询做交叉连接。 --其中“select * from T_Book where FYearPublished < 1800”做为表子查询,并取别名 --这与使用一个普通的数据表没有什么区别。 select T_Reader.FName,t2.FYearPublished,t2.FName from T_Reader,(select * from T_Book where FYearPublished < 1800) t2
表子查询可以看作一张临时的表,所以引用子查询中列的时候必须使用子查询中定义的列名,也就是如果子查询中为列定义了别名,那么在引用的时候也要使用别名。
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
3.In运算符
select * from T_Reader where FYearOfJoin in (select FYearPublished from T_Book)
4.Any于Some
some的用法和功能和any一模一样。和in运算符不同,any必须和其他的比较运算符共同使用,而且必须将比较运算符放在any关键字之前,所比较的值需要匹配子查询中的任意一个值,这也就是any在英文中所表示的意义,只有满足一个条件就可以了。=any”等价于in 运算符,而“<>any”则等价于not in运算符。 除了等于运算符,any运算符还可以和大于(>)、小于(<)、大于等于(>=)、小于等于(<=)等比较运算符共同使用。
select * from T_Reader where FYearOfJoin =any (select FYearPublished from T_Book)
和in运算符不同,any运算符不能与固定的集合相匹配,比如下面的SQL语句是错误的:
select * from T_Book where FYearPublished<any(2001,2003,2005)
不过这个限制并不会妨碍功能的实现,因为没有对固定的集合进行any匹配的必要,因为待匹配的集合是固定的,所以上面的SQL语句完全可以用下面的SQL语句来代替:
select * from T_Book where FYearPublished<2005
5.All运算符
ALL运算符要求比较的值需要匹配子查询中的所有值。ALL运算符同样不能单独使用,必须和比较运算符共同使用。
下面的SQL语句用来检索在所有会员入会之前出版的图书:
select * from T_Book where FYearPublished<ALL ( select FYearOfJoin from T_Reader )
另外需要注意的就是,当使用ALL运算符的时候,如果带匹配的集合为空,也就是子查询没有返回任何数据的时候,不论与什么比较运算符搭配使用ALL的返回值将永远是true。
6.Exists运算符
可以认为EXISTS就是用来测试子查询的结果是否为空,如果结果集为空则匹配结果为false,否则匹配结果为true。下面的SQL语句用来检索存在1950年以前出版的图书的图书类别:
select * from T_Category where exists ( --判断当前目录Id下面的书籍是否存在,空为false,不为空true select * from T_Book where T_Book.FCategoryId = T_Category.FId and T_Book.FYearPublished<1950 )
7.子查询在insert语句中的应用
除了insert……values……这种用法外,insert语句还支持另外一种语法,那就是insert……select……,采用这种使用方式可以将select语句返回的结果集直接插入到目标表中。
insert into T_ReaderFavorite2(FCategoryId,FReaderId) select FCategoryId, (case with FReaderId<=10 then FReaderId else FReaderId- FCategoryId end ) from T_ReaderFavorite