MySQL中的子查詢
是在MySQL中經常使用到的一個操作,不僅僅是用在DQL語句中,在DDL語句、DML語句中也都會常用到子查詢。
子查詢的定義:
子查詢是將一個查詢語句嵌套在另一個查詢語句中;
在特定情況下,一個查詢語句的條件需要另一個查詢語句來獲取,內層查詢(inner query)語句的查詢結果,可以為外層查詢(outer query)語句提供查詢條件。
特點(規范):
①子查詢必須放在小括號中
②子查詢一般放在比較操作符的右邊,以增強代碼可讀性
③子查詢(小括號里的內容)可出現在幾乎所有的SELECT子句中(如:SELECT子句、FROM子句、WHERE子句、ORDER BY子句、HAVING子句……)
(相關、不相關)子查詢分類:
①標量子查詢(scalar subquery):返回1行1列一個值
②行子查詢(row subquery):返回的結果集是 1 行 N 列
③列子查詢(column subquery):返回的結果集是 N 行 1列
④表子查詢(table subquery):返回的結果集是 N 行 N 列
可以使用的操作符:= > < >= <= <> ANY IN SOME ALL EXISTS
注意:一個子查詢會返回一個標量(就一個值)、一個行、一個列或一個表,這些子查詢稱之為標量、行、列和表子查詢
1、如果子查詢返回一個標量值(就一個值),那么外部查詢就可以使用:=、>、<、>=、<=和<>符號進行比較判斷;
2、如果子查詢返回的不是一個標量值,而外部查詢使用了比較符和子查詢的結果進行了比較,那么就會拋出異常。
環境搭建:employee雇員表、department部門表、TENNIS數據庫
mysql> select * from department; +------+-----------+--------------+---------------------+
| d_id | d_name | function | address |
+------+-----------+--------------+---------------------+
| 1001 | 科技部 | 研發產品 | 3號樓5層 |
| 1002 | 生產部 | 生產產品 | 5號樓1層 |
| 1003 | 銷售部 | 策划銷售 | 1號樓銷售大廳 |
+------+-----------+--------------+---------------------+
3 rows in set (0.02 sec) mysql> select * from employee; +------+------+--------+------+------+--------------------+
| num | d_id | name | age | sex | homeaddr |
+------+------+--------+------+------+--------------------+
| 1 | 1001 | 張三 | 26 | 男 | 北京市海淀區 |
| 2 | 1001 | 李四 | 24 | 女 | 上海市黃浦區 |
| 3 | 1002 | 王五 | 25 | 男 | 江西省贛州市 |
| 4 | 1004 | Aric | 15 | 男 | England |
+------+------+--------+------+------+--------------------+
4 rows in set (0.00 sec)
mysql> use TENNIS Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show tables; +-------------------+
| Tables_in_TENNIS |
+-------------------+
| COMMITTEE_MEMBERS |
| MATCHES |
| PENALTIES |
| PLAYERS |
| TEAMS |
+-------------------+
5 rows in set (0.00 sec)
一、不相關子查詢
不相關,主查詢和子查詢是不相關的關系。也就是意味着在子查詢中沒有使用到外部查詢的表中的任何列。
先執行子查詢,然后執行外部查詢
1、標量子查詢(scalar subquery):返回1行1列一個值
因為是標量子查詢,結果是一個值,所以可用來進行算數運算。
可以使用 = > < >= <= <> 操作符對子查詢的結果進行比較:
mysql> select num,name -> from employee -> where d_id=( -> select d_id -> from department -> where d_name='科技部'); +------+--------+
| num | name |
+------+--------+
| 1 | 張三 |
| 2 | 李四 |
+------+--------+
mysql> select num,name -> from employee -> where d_id=( -> select d_id -> from department -> where d_name='財務部'); Empty set (0.00 sec)
注意:如果子查詢返回空值,可能導致外部查詢的where條件也為空,從而外部查詢的結果集為空。
mysql> SELECT playerno,town,sex -> FROM PLAYERS -> WHERE (town,sex) = ((SELECT town FROM PLAYERS WHERE playerno=7), -> (SELECT sex FROM PLAYERS WHERE playerno=44)); +----------+-----------+-----+
| playerno | town | sex |
+----------+-----------+-----+
| 2 | Stratford | M |
| 6 | Stratford | M |
| 7 | Stratford | M |
| 39 | Stratford | M |
| 57 | Stratford | M |
| 83 | Stratford | M |
| 100 | Stratford | M |
+----------+-----------+-----+
7 rows in set (0.01 sec)
注意: (列,列,…)叫做行表達式,比較時是比較列的組合。
2、行子查詢(row subquery):返回的結果集是 1 行 N 列
使用行表達式進行比較,可以使用 = > < >= <= <> in操作符
mysql> select d_id from department; +------+
| d_id |
+------+
| 1001 |
| 1002 |
| 1003 |
+------+
mysql> select * from employee -> where d_id in
-> (select d_id from department); +------+------+--------+------+------+--------------------+
| num | d_id | name | age | sex | homeaddr |
+------+------+--------+------+------+--------------------+
| 1 | 1001 | 張三 | 26 | 男 | 北京市海淀區 |
| 2 | 1001 | 李四 | 24 | 女 | 上海市黃浦區 |
| 3 | 1002 | 王五 | 25 | 男 | 江西省贛州市 |
+------+------+--------+------+------+--------------------+
解析:此處首先查詢出department表中所有d_id字段的信息,並將結果作為條件,接着查詢employee表中以d_id為條件的所有字段信息;NOT IN的效果與上面剛好相反。
3、列子查詢(column subquery):返回的結果集是 N 行 1列
必須使用 IN、ANY 和 ALL 操作符對子查詢返回的結果進行比較
注意:ANY 和 ALL 操作符不能單獨使用,其前面必須加上單行比較操作符= > < >= <= <>
1)帶ANY關鍵字的子查詢:ANY關鍵字表示滿足其中任一條件
mysql> select * from employee -> where d_id !=any
-> (select d_id from department); +------+------+--------+------+------+--------------------+
| num | d_id | name | age | sex | homeaddr |
+------+------+--------+------+------+--------------------+
| 1 | 1001 | 張三 | 26 | 男 | 北京市海淀區 |
| 2 | 1001 | 李四 | 24 | 女 | 上海市黃浦區 |
| 3 | 1002 | 王五 | 25 | 男 | 江西省贛州市 |
| 4 | 1004 | Aric | 15 | 男 | England |
+------+------+--------+------+------+--------------------+
2)帶ALL關鍵字的子查詢:ALL關鍵字表示滿足其中所有條件
mysql> select * from employee -> where d_id >=all
-> (select d_id from department); +------+------+------+------+------+----------+
| num | d_id | name | age | sex | homeaddr |
+------+------+------+------+------+----------+
| 4 | 1004 | Aric | 15 | 男 | England |
+------+------+------+------+------+----------+
注意:如果子查詢的結果集中有null值,使用>ALL 和not in操作符時,必須去掉子查詢結果集中的null值,否則查詢結果錯誤
mysql> select * from department -> where d_id >all #>all背后執行and操作 -> (select d_id from employee); Empty set (0.01 sec)
結果為空:子查詢的結果集中包含null值(子查詢結果集中沒有主查詢里的1004行,則為空)。
4、表子查詢(table subquery):返回的結果集是 N 行 N 列
必須使用 IN、ANY 和 ALL 操作符對子查詢返回的結果進行比較
示例:在committee_members表中,得到任職日期和卸任日期與具有Secretary職位的一行相同的所有行
mysql> select *
-> from COMMITTEE_MEMBERS -> where (begin_date,end_date) in
-> ( -> select begin_date,end_date -> from COMMITTEE_MEMBERS -> where position='Secretary'
-> ); +----------+------------+------------+-----------+
| PLAYERNO | BEGIN_DATE | END_DATE | POSITION |
+----------+------------+------------+-----------+
| 6 | 1990-01-01 | 1990-12-31 | Secretary |
| 8 | 1990-01-01 | 1990-12-31 | Treasurer |
| 8 | 1991-01-01 | 1991-12-31 | Secretary |
| 27 | 1990-01-01 | 1990-12-31 | Member |
| 27 | 1991-01-01 | 1991-12-31 | Treasurer |
| 57 | 1992-01-01 | 1992-12-31 | Secretary |
| 112 | 1992-01-01 | 1992-12-31 | Member |
+----------+------------+------------+-----------+
7 rows in set (0.05 sec)
二、相關子查詢(correlated subquery)
在子查詢中使用到了外部查詢的表中的任何列。
先執行外部查詢,然后執行子查詢
相關子查詢的執行步驟:
①先執行外部查詢,得到的行叫做候選行
②使用某個候選行來執行子查詢
③使用子查詢的返回值來決定該候選行是出現在最終的結果集中還是被丟棄
④重復以上步驟2和3,將所有的候選行處理完畢,得到最終的結果
示例:得到項目是‘研發產品’的雇員的編號
mysql> select num -> from employee -> where '研發產品'=( -> select function
-> from department -> where d_id=employee.d_id); +------+
| num |
+------+
| 1 |
| 2 |
+------+
解析:
1)主查詢得到候選行,一行一行的拿去執行子查詢;
2)主查詢表employee的候選行的d_id和子查詢的d_id匹配,返回值進行where過濾;
3)符合,加入最終結果集;
4)不符合,將候選行丟棄,接着進行處理下一個候選行。
帶EXISTS關鍵字的相關子查詢(EXISTS存在)
專門判斷子查詢的結果集是否不為空:
非空空返回true
空返回false
當返回的值為true時,外層查詢語句將進行查詢,否則不進行查詢
mysql> select * from employee -> where exists
-> (select d_name from department where d_id=1004); Empty set (0.00 sec)
此處內層循環並沒有查詢到滿足條件的結果,因此返回false,外層查詢不執行
EXISTS關鍵字可以與其他的查詢條件一起使用,條件表達式與EXISTS關鍵字之間用AND或者OR來連接
mysql> select * from employee -> where age>24 and exists -> (select d_name from department where d_id=1003); +------+------+--------+------+------+--------------------+ | num | d_id | name | age | sex | homeaddr | +------+------+--------+------+------+--------------------+ | 1 | 1001 | 張三 | 26 | 男 | 北京市海淀區 | | 3 | 1002 | 王五 | 25 | 男 | 江西省贛州市 | +------+------+--------+------+------+--------------------+