源自MySQL 5.7 官方手冊 13.2.10 Subquery Syntax
〇、MySQL子查詢介紹
子查詢指的是嵌套在某個語句中的SELECT語句。
MySQL支持標准SQL所要求的所有子查詢形式和操作,此外還進行了一些擴展。
下面就是一個有子查詢的示例:
SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);
在此示例中,SELECT * FROM t1 ...是外部查詢(或外部語句),而(SELECT column1 FROM t2)是子查詢。子查詢嵌套在外部查詢中,實際上可以將子查詢嵌套在其他子查詢中,達到相當深的程度。子查詢必須始終出現在括號內。
子查詢的優點:
- 允許結構化的查詢,以便可以隔離語句的每個部分;
- 可以替代復雜的連接和聯合;
- 相比連接和聯合,有更高的可讀性。實際上,正是子查詢這個創新給了人們靈感把SQL叫做結構化查詢語言。
子查詢的語法要點
下面是一個示例語句,顯示了SQL標准指定的並在MySQL中支持的子查詢語法的要點:
DELETE FROM t1 WHERE s11 > ANY (SELECT COUNT(*) /* no hint */ FROM t2 WHERE NOT EXISTS (SELECT * FROM t3 WHERE ROW(5*t2.s1,77)= (SELECT 50,11*s1 FROM t4 UNION SELECT 50,77 FROM (SELECT * FROM t5) AS t5)));
一個子查詢可以返回一個標量(單個值),單個行,單個列或一個表(一個或多個列的一行或多行)。他們分別叫做標量子查詢、列子查詢、行子查詢以及表子查詢。
返回特定類型結果的子查詢通常只能在某些上下文中使用,接下來的章節會闡述。
子查詢可以應用在大部分語句中,MySQL對此限制很少。
子查詢可以包含很多普通SELECT語句中的關鍵字:DISTINCT, GROUP BY, ORDER BY, LIMIT, joins, index hints, UNION constructs, comments, functions等。
一個子查詢的外接語句可以為:SELECT, INSERT, UPDATE, DELETE, SET, or DO。
但是在MySQL中,不能在修改一個表的同時在子查詢中對同一個表進行SELECT操作。這適用於DELETE,INSERT,REPLACE,UPDATE等語句,還有LOAD DATA((因為子查詢可以在SET子句中使用))。
關於優化器怎么處理子查詢的知識,see Section 8.2.2,“Optimizing Subqueries, Derived Tables, and View References”。
有關子查詢使用限制的討論,包括某些形式的子查詢語法的性能問題,see Section C.4, “Restrictions on Subqueries”。
一、將子查詢作為標量操作數
在此查詢最簡單的形式中,子查詢是一個返回單個值的標量子查詢(a scalar subquery)。標量子查詢是一個簡單的操作數,您幾乎可以在將它使用在任何單個列值或字面值合法的地方。你可以期望它具有一般操作數都擁有的特征:數據類型,長度,可以為NULL的指示,等等。
示例:
CREATE TABLE t1 (s1 INT, s2 CHAR(5) NOT NULL); INSERT INTO t1 VALUES(100, 'abcde');
SELECT (SELECT s2 FROM t1);
這個查詢中的子查詢返回單個值——“abcde”,數據類型為CHAR,長度為5,字符集和排序規則等於CREATE TABLE時生效的默認值,以及一個關於該列值可以為NULL的提示。
如果子查詢的結果為空集,那么單值子查詢所取回的值的NULL性並不會直接被復制,因為此時子查詢的結果就為NULL。如上面的子查詢,如果t1為空表,那么子查詢的結果將為NULL,即使表t1中的S2列含有NOT NULL約束。
很少有一個標量子查詢不能被使用的情況。如果一個語句只允許一個字面量值,那此時你無法使用一個子查詢。例如,LIMIT要求整數類型的字面值參數,LOAD DATA要求一個代表文件路徑的字面量的字符串值。此時你就不能使用子查詢來提供這些值。
當你在接下來章節的示例中看到相當簡潔的子查詢時,可以聯想下在自己的代碼中的子查詢使用更加多樣化和復雜的構造。
假設現在有兩個表:
CREATE TABLE t1 (s1 INT); INSERT INTO t1 VALUES (1); CREATE TABLE t2 (s1 INT); INSERT INTO t2 VALUES (2);
然后之心一個SELECT:
SELECT (SELECT s1 FROM t2) FROM t1; +---------------------+ | (SELECT s1 FROM t2) | +---------------------+ | 2 | +---------------------+ 1 row in set (0.00 sec)
結果為2,因為在表t2中有一行數據,s1列值為2。
標量子查詢可以是表達式的一部分,但記得加括號,即使子查詢只是作為操作數為函數提供參數。
SELECT UPPER((SELECT s1 FROM t1)) FROM t2; +----------------------------+ | UPPER((SELECT s1 FROM t1)) | +----------------------------+ | 1 | +----------------------------+ 1 row in set (0.01 sec)
二、使用子查詢進行比較
子查詢最常見的用法是:
non_subquery_operand comparison_operator (subquery)
compare_operator是以下運算符之一:
= > < >= <= <> != <=>
例如:
... WHERE 'a' = (SELECT column1 FROM t1)
MySQL也允許這種結構:
non_subquery_operand LIKE (subquery)
在曾經某個時間,子查詢的唯一合法位置是在比較的右側,您可能仍然會發現一些堅持這一點的舊DBMS。
下面是一個常見形式子查詢比較的示例,您無法對連接執行此操作。它找到表t1中column1值等於表t2中最大值的所有行:
SELECT * FROM t1 WHERE column1 = (SELECT MAX(column2) FROM t2);
這是另一個例子,連接也是不可行的,因為它涉及聚合其中一個表。它查找表t1中的所有行,其中包含在給定列中出現兩次的值:
SELECT * FROM t1 AS t WHERE 2 = (SELECT COUNT(*) FROM t1 WHERE t1.id = t.id);
若是為了將子查詢與標量進行比較,子查詢必須返回標量。
若是為了將子查詢與行構造函數進行比較,子查詢必須是行子查詢,該子查詢返回與行構造函數具有相同數量值的行。See Section 13.2.10.5, “Row Subqueries”.
三、帶有ANY,IN或SOME的子查詢
語法:
operand comparison_operator ANY (subquery) operand IN (subquery) operand comparison_operator SOME (subquery)
compare_operator是以下運算符之一:
= > < >= <= <> !=
ANY關鍵字,必須緊跟比較運算符后,意味着“如果操作數與子查詢中多返回的列中的任意值的比較為TRUE,那就返回TRUE”。例如:
SELECT s1 FROM t1 WHERE s1 > ANY (SELECT s1 FROM t2);
假設表t1中有一行包含(10)。如果表t2包含(21,14,7),則表達式為TRUE,因為t2中的值7小於10。
如果表t2包含(20,10),或者表t2為空,則表達式為FALSE。
如果表t2包含(NULL,NULL,NULL),則表達式結果是未知的(即NULL)。
與子查詢一起使用時,單詞IN是= ANY的別名。因此,這兩個陳述是相同的:
SELECT s1 FROM t1 WHERE s1 = ANY (SELECT s1 FROM t2); SELECT s1 FROM t1 WHERE s1 IN (SELECT s1 FROM t2);
與表達式列表一起使用時,IN和= ANY不是同義詞。 IN可以采用表達式列表,但是= ANY不能。See Section 12.3.2, “Comparison Functions and Operators”.
NOT IN不是<> ANY的別名,而是<> ALL的別名。See Section 13.2.10.4, “Subqueries with ALL”.
SOME這個詞是ANY的別名。因此,這兩個陳述是相同的:
SELECT s1 FROM t1 WHERE s1 <> ANY (SELECT s1 FROM t2); SELECT s1 FROM t1 WHERE s1 <> SOME (SELECT s1 FROM t2);
SOME這個詞很少用到,但是這個例子說明了為什么它可能有用。
對於大多數人來說,英語短語“a is not equal to any b”意味着“沒有b等於a”。但這不是SQL語法的含義。在SQL中,該短語意味着“有一些b與a不相等。”使用<> SOME有助於確保每個人都理解查詢的真正含義。
四、帶有ALL的子查詢
operand comparison_operator ALL (subquery)
ALL關鍵字必須緊跟在比較操作符后,意思是“只有操作數與子查詢返回的列中的所有值進行比較都為true,則這個比較表達式也返回true”。例如:
SELECT s1 FROM t1 WHERE s1 > ALL (SELECT s1 FROM t2);
假設表t1中有一行包含(10)。如果表t2包含(-5,0,+ 5),則表達式為TRUE,因為10大於t2中的所有三個值。如果表t2包含(12,6,NULL,-100),則表達式為FALSE,因為表t2中的單個值12大於10。如果表t2包含(0,NULL,1),則表達式是未知的(即NULL)。
而如果表t2為空,則表達式為TRUE。因此,當表t2為空時,以下表達式為TRUE:
SELECT * FROM t1 WHERE 1 > ALL (SELECT s1 FROM t2);
但是當表t2為空時,以下表達式為NULL:
SELECT * FROM t1 WHERE 1 > (SELECT s1 FROM t2);
此外,當表t2為空時,以下表達式為NULL:
SELECT * FROM t1 WHERE 1 > ALL (SELECT MAX(s1) FROM t2);
通常,包含NULL值和空表的表是“邊緣情況”。
所以在編寫子查詢時,請始終考慮是否考慮了這兩種可能性。
NOT IN是<> ALL的別名。因此,這兩個陳述是相同的:
SELECT s1 FROM t1 WHERE s1 <> ALL (SELECT s1 FROM t2); SELECT s1 FROM t1 WHERE s1 NOT IN (SELECT s1 FROM t2);
