MySQL 子查詢(一)


  源自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);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM