
关联子查询,非关联子查询
子查询虽然是一种嵌套查询的形式,不过依然可以一句子查询是否执行多次,从而将子查询划分为关联子查询和非关联子查询。
子查询从数据表中查询了数据结果,如果这个数据结果只执行一次,然后这个数据结果作为主查询条件进行执行,那么这样的子查询叫做非关联子查询。
如果子查询执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查询,然后再将结果返回给外部,这种嵌套的执行方式就称为关联子查询。
以NBA球员数据库为例,文件中一共包括5张表,player表为球员,team为球队表,team_score为球队比赛表,player_score为球员比赛成绩表,height_grades为球员身高对应的等级表。
player表,37个球员
team表,3支球队。
team_score表,一共记录两场比赛
player_score表,记录了一场比赛中球员的表现,包含19个字段
注:数据表字段比较多的时候,为了防止混淆字段,最好的方式就是做个说明文档,用实例举例。
例:shoot_attempts是总出手次数(这里的总出手次数=二分球出手次数+三分球出手次数,不包含罚球的次数)。
示例
- 获取球员的最高身高,最高身高是多少
SQL: SELECT player_name, height FROM player WHERE height = (SELECT max(height) FROM player)
运行结果
通过SELECT max(height) FROM player可以得到最高身高这个数值,结果为2.16,然后再通过player这个表,看谁具有这个身高,再进行输出,这样的子查询就是非关联子查询。
如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了条件关联,因此没执行一次外部查询,子查询都要重新计算一次,这样的子查询就称关联子查询。
示例
- 想要查找每个球队中大于平均身高的球员有哪些,显示他们的姓名、身高以及所在球队ID
SELECT player_name, height, team_id FROM player AS a WHERE height > (SELECT avg(height) FROM player AS b WHERE a.team_id = b.team_id)
运行结果:(18条记录)
这里将player表复制成了表a和表b,每次计算的时候,需要将表a中的team_id传入从句,作为已知值。因为每次表a中的team_id可能是不同的,所以是关联子查询。如果是非关联子查询,那么从句计算的结果是固定的才可以。
EXISTS子查询
关联子查询通常会和EXISTS一起使用,EXISTS子查询用来判断条件是否满足,满足的话为True,不满足为False。
示例
- 查看出场过的球员有哪些,并显示他们的姓名、球员ID和球队ID(是否出场通过player_score)这张表中的球员出场表现来统计,如果球员在player_score中有过出场记录则代表他出场过)
SQL:SELECT player_id, team_id, player_name FROM player WHERE EXISTS (SELECT player_id FROM player_score WHERE player.player_id = player_score.player_id)
运行结果(19条记录)
NOT EXISTS是不存在的意思
SQL: SELECT player_id, team_id, player_name FROM player WHERE NOT EXISTS (SELECT player_id FROM player_score WHERE player.player_id = player_score.player_id)
运行结果:(18条记录)
集合比较子查询
上面例子,采用IN子查询来进行操作
SELECT player_id, team_id, player_name FROM player WHERE player_id in (SELECT player_id FROM player_score WHERE player.player_id = player_score.player_id)
运行结果和EXISTS相同
IN和EXISTS的选择
抽象示例
SELECT * FROM A WHERE cc IN (SELECT cc FROM B)
SELECT * FROM A WHERE EXIST (SELECT cc FROM B WHERE B.cc=A.cc)
实际查询过程中,对cc列建立索引的情况下,还需要判断表A和表B的大小。在这个例子当中,表A指的是player表,表B指的是player_score表,如果表A表比表B大,那么IN查询的效率要比EXIST子查询效率高,因为这时B表中如果对cc列进行了索引,那么IN子查询的效率就会比较高。
如果表A比表B小,那么使用EXISTS子查询效率会更高。
使用ANY和ALL需要使用比较符
示例
- 比印第安那步行者(对应的team_id为1002)中任意一个球员身高高的球员信息,并且输出他们的球员表ID、球员姓名和球员身高。
SQL: SELECT player_id, player_name, height FROM player WHERE height > ANY (SELECT height FROM player WHERE team_id = 1002)
运行结果
- 查询比印第安纳步行者中所有球员身高都高的球员的信息,并且输出球员ID、球员姓名和球员身高
SQL: SELECT player_id, player_name, height FROM player WHERE height > ALL (SELECT height FROM player WHERE team_id = 1002)
运行结果
将子查询作为计算字段
示例
- 查询相同team_id在player表中所有球员的数量是多少
SQL: SELECT team_name, (SELECT count(*) FROM player WHERE player.team_id = team.team_id) AS player_num FROM team
运行结果:(3条记录)
总结