一,子查询定义:
子查询就是嵌套在主查询中的查询。
子查询可以嵌套在主查询中所有位置,包括SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY。
但并不是每个位置嵌套子查询都是有意义并实用的。
二,子查询的返回:
一个子查询会返回一个标量(单一值)、一个行、一个列或一个表(一行或多行及一列或多列)。这些子查询被称为标量、列、行和表子查询
1,单行单列,聚合(标量):
返回的结果集为单个的子查询,叫做单行子查询。单行比较符有: =、 >、>=、<、<=、!=
2,单行多列:
SELECT * FROM t1 WHERE (1,2) = (SELECT column1, column2 FROM t2);
SELECT * FROM t1 WHERE ROW(1,2) = (SELECT column1, column2 FROM t2);
如果在表t2的一个行中,column1=1并且column2=2,则查询结果均为TRUE。
表达式(1,2)和ROW(1,2)有时被称为行构造符。两者是等同的,在其它的语境中,也是合法的。例如,以下两个语句在语义上是等同的(但是目前只有第二个语句可以被优化):
SELECT * FROM t1 WHERE (column1,column2) = (1,1);
SELECT * FROM t1 WHERE column1 = 1 AND column2 = 1;
3,多行单列与多行多列:
返回的结果集为多个的子查询,为多行子查询,多行子比较符有 IN(等于列中任意一个)、ANY(和子查询返回的某个值比较),ALL(和子查询返回的所有值比较)
NOT IN是<> ALL的别名
IN是=ANY的别名
三,EXISTS和NOT EXISTS
该语法可以理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(TRUE 或 FALSE)来决定主查询的数据结果是否得以保留。请参考
https://blog.csdn.net/qq_27571221/article/details/53090467
四,关联子查询
相关联的子查询是一个包含对表的引用的子查询。该表也显示在外部查询中。例如:
SELECT * FROM t1 WHERE column1 = ANY (SELECT column1 FROM t2 WHERE t2.column2 = t1.column2);
注意,即使子查询的FROM子句不提及表t1,该子查询也会包含一个对t1中一列的引用。所以,MySQL看上去位于子查询的外部,并在外部查询中查找t1。
假设表t1包含一行,在此行中column1=5并且column2=6;同时,表t2包含一行,在此行中column1=5并且column2=7。简单的表达式... WHERE column1 = ANY (SELECT column1 FROM t2)会为TRUE。但是在本例中,在子查询中的WHERE子句为FALSE(因为(5,6)不等于(5,7)),所以子查询总体上为FALSE。
范围划分规则:MySQL从内到外进行评估。例如:
SELECT column1 FROM t1 AS x WHERE x.column1 = (SELECT column1 FROM t2 AS x WHERE x.column1 = (SELECT column1 FROM t3 WHERE x.column2 = t3.column1));
在本语句中,x.column2必须是表t2中的列,因为SELECT column1 FROM t2 AS x ...对t2进行了重命名。它不是表t1中的列,因为SELECT column1 FROM t1 ...是一个更靠外的外部查询。
对于HAVING或ORDER BY子句中的子查询,MySQL也会在外部选择清单中寻找列名称。
对于特定的情况,相关联的子查询被优化。例如:
val IN (SELECT key_val FROM tbl_name WHERE correlated_condition)
否则,这些子查询效率不高,可能速度会慢。把查询作为联合进行改写可能会改进效率。
相关联的子查询不能从外部查询中引用总计函数的结果。
五,在FROM中嵌套
from下的子查询 相当于返回一张表,并且要强制取名
在SELECT语句的FROM子句中,子查询是合法的。实际的语法是:
SELECT ... FROM (subquery) [AS] name ...
[AS] name子句是强制性的,因为FROM子句中的每个表必须有一个名称。在子查询选择列表中的任何列都必须有唯一的名称。您可以在本手册中的其它地方找到对本语法的说明。在该处,所用的词语是“导出表”。
为了进行详细说明,假设您有如下一个表:
CREATE TABLE t1 (s1 INT, s2 CHAR(5), s3 FLOAT);
下面使用了示例表,解释了在FROM子句中如何使用子查询:
INSERT INTO t1 VALUES (1,'1',1.0); INSERT INTO t1 VALUES (2,'2',2.0); SELECT sb1,sb2,sb3 FROM (SELECT s1 AS sb1, s2 AS sb2, s3*2 AS sb3 FROM t1) AS sb WHERE sb1 > 1; //结果:2, '2', 4.0。
下面是另一个例子:假设您想了解一个分类后的表的一组和的平均值。采用如下操作:
SELECT AVG(SUM(column1)) FROM t1 GROUP BY column1;
不过,本查询提供所需的信息:
SELECT AVG(sum_column1) FROM (SELECT SUM(column1) AS sum_column1 FROM t1 GROUP BY column1) AS t1;
注意,在子查询中使用的列名称(sum_column1)被整理到外部查询中。
FROM子句中的子查询可以返回标量、列、行或表。FROM子句中的子查询不能为有关联的子查询。
即使对EXPLAIN语句(即建立临时导出表),FROM子句中的子查询也会被执行。这是因为在优化过程中,上一级的查询需要有关所有表的信息。
六,在SELECT中嵌套:
select中的子查询如果是相关子查询,貌似可以和join关联查询转换
现有表两张:一张学生表、一张班表。id相关联
学生信息和班级名称位于不同的表中,要在同一张表中查出学生的学号、姓名、班级名称:
SELECT s.student_id,s.student_name,(SELECT class_name FROM t_class c WHERE c.class_id=s.class_id) FROM t_student s GROUP BY s.student_id;
首先这条SQL语句用到了别名,写法为在FORM的表名后加上某个字符比如FROM t_student s,这样在之后调用t_student的某一列时就可以用s.student_id来强调此列来源于对应别名的那张表。(这是一个相关子查询)
别名在子查询及联接查询中的应用有着很好效果,当两张表有相同列名或者为了加强可读性,给表加上不同的别名,就能很好的区分哪些列属于哪张表。
还有种情况就是在子查询或联接查询时,主查询及子查询均为对同一张表进行操作,为主、子查询中的表加上不同的别名能够很好的区分哪些列的操作是在主查询中进行的,哪些列的操作是在子查询中进行的,下文会有实例说明。
接下来回到上面的SQL语句中,可以看出本条子查询的嵌套是在SELECT位置(括号括起来的部分),它与学号、学生姓名以逗号分隔开并列在SELECT位置,也就是说它是我们想要查出的一列,
子查询中查出的是,班级表中的班级id与学生表中的班级id相同的行,注意 WHERE c.class_id=s.class_id 这里就是别名用法的一个很好的体现,区分开了两张表中同样列名的列。
七,在WHERE中嵌套:
现要查出C语言成绩最高的学生的信息:
SELECT * FROM t_student WHERE student_subject='C语言' AND student_score>=
ALL (SELECT student_score FROM t_student WHERE student_subject='C语言') ;
这里出现了一个ALL,其为子查询运算符
分类:
–ALL运算符
和子查询的结果逐一比较,必须全部满足时表达式的值才为真。
–ANY运算符
和子查询的结果逐一比较,其中一条记录满足条件则表达式的值就为真。
–EXISTS/NOT EXISTS运算符
EXISTS判断子查询是否存在数据,如果存在则表达式为真,反之为假。NOT EXISTS相反。
在子查询或相关查询中,要求出某个列的最大值,通常都是用ALL来比较,大意为比其他行都要大的值即为最大值。
要查出C语言成绩比李四高的学生的信息:
SELECT * FROM t_student WHERE student_subject='C语言' AND student_score >
(SELECT student_score FROM t_student WHERE student_name='李四' AND student_subject='C语言');
通过上面两例,应该可以明白子查询在WHERE中嵌套的作用。通过子查询中返回的列值来作为比较对象,在WHERE中运用不同的比较运算符来对其进行比较,从而得到结果。
现在我们回到最开始的问题,怎么查出每门课最高成绩的学生的信息:(这个也是相关子查询)
SELECT * FROM t_student s1 WHERE s1.student_score >=
ALL(SELECT s2.student_score FROM t_student s2 WHERE s1.`student_subject`=s2.student_subject);
这里就是上文提到的别名的第二种用法,主、子查询对同一张表操作,区分开位于内外表中相同的列名。
八,子查询的分类:
–相关子查询
执行依赖于外部查询的数据。(就是table1在该select外面,而select里面table1.xx=table2.xx)
外部查询返回 一行 ,子查询就执行一次。(对于该select,里面的子select会重复很多次执行)
–非相关子查询
独立于外部查询的子查询。
子查询总共执行一次,执行完毕后后将值传递给外部查询。
上文提到的例子中,第一个例子求学生对应班级名的即为相关子查询,其中WHERE c.class_id=s.class_id 即为相关条件。其他的例子均只对一张表进行操作,为非相关子查询。
需要注意的是相关子查询主查询执行一回,子查询就执行一回,十分耗费时间,尤其是当数据多的时候。