一,子查詢定義:
子查詢就是嵌套在主查詢中的查詢。
子查詢可以嵌套在主查詢中所有位置,包括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 即為相關條件。其他的例子均只對一張表進行操作,為非相關子查詢。
需要注意的是相關子查詢主查詢執行一回,子查詢就執行一回,十分耗費時間,尤其是當數據多的時候。
