MySQL的多表查询
文章目錄
- MySQL的多表查詢
- 什么是多表聯合查詢
- 交叉連接(CROSS JOIN)
- 笛卡爾積
- 交叉連接
- 內連接
- 外連接
- 左連接
- 右連接
- 分組查詢
- GROUP BY單獨使用
- GROUP BY 與 GROUP_CONCAT()
- GROUP BY 與聚合函數
- GROUP BY 與 WITH ROLLUP
- 子查詢
MySQL的多表查詢
什么是多表聯合查詢
多表查詢就是同時查詢兩個或兩個以上的表。
在 MySQL 中,多表查詢主要有交叉連接、內連接、外連接、分組查詢與子查詢等5種。
交叉連接(CROSS JOIN)
笛卡爾積
交叉連接(CROSS JOIN):有兩種,顯式的和隱式的2種,一般用來返回連接表的笛卡爾積。
笛卡爾積(Cartesian product)是指兩個集合 X 和 Y 的乘積。
例如,有 A 和 B 兩個集合,它們的值如下:
A = {1,2} B = {3,4,5}集合 A×B 和 B×A 的結果集分別表示為:
A×B={(1,3), (1,4), (1,5), (2,3), (2,4), (2,5) }; B×A={(3,1), (3,2), (4,1), (4,2), (5,1), (5,2) };以上 A×B 和 B×A 的結果就叫做兩個集合的笛卡爾積。
并且,從以上結果我們可以看出:
- 兩個集合相乘,不滿足交換率,即 A×B≠B×A。
- A 集合和 B 集合的笛卡爾積是 A 集合的元素個數 × B 集合的元素個數。
多表查詢遵循的算法就是以上提到的笛卡爾積,表與表之間的連接可以看成是在做乘法運算。
在實際應用中,應避免使用笛卡爾積,因為笛卡爾積中容易存在大量的不合理數據,簡單來說就是容易導致查詢結果重復、混亂。
交叉連接
交叉連接的語法格式如下:
SELECT <字段名> FROM <表1> CROSS JOIN <表2> [WHERE子句];或
SELECT <字段名> FROM <表1>, <表2> [WHERE子句];語法說明如下:
- 字段名:需要查詢的字段名稱。
- <表1><表2>:需要交叉連接的表名。
- WHERE 子句:用來設置交叉連接的查詢條件。
注意:多個表交叉連接時,在 FROM 后連續使用 CROSS JOIN 或,即可。以上兩種語法的返回結果是相同的,但是第一種語法才是官方建議的標準寫法。
交叉連接可以查詢兩個或兩個以上的表,為了更好的理解,我們就講解兩個表的交叉連接查詢。
例 1
查詢學生信息表和科目信息表,并得到一個笛卡爾積。
為了方便觀察學生信息表和科目表交叉連接后的運行結果,我們先分別查詢出這兩個表的數據,再進行交叉連接查詢。
1)查詢 tb_students_info 表中的數據,SQL 語句和運行結果如下:
mysql> create database school; Query OK, 1 row affected (0.01 sec)mysql> use school; Database changed mysql> create table tb_students_info(id int not null primary key auto_increment,name varchar(30),age tinyint,sex varchar(6),height int,course_id int); Query OK, 0 rows affected (0.01 sec)mysql> desc tb_students_info; +-----------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(30) | YES | | NULL | | | age | tinyint(4) | YES | | NULL | | | sex | varchar(6) | YES | | NULL | | | height | int(11) | YES | | NULL | | | course_id | int(11) | YES | | NULL | | +-----------+-------------+------+-----+---------+----------------+ 6 rows in set (0.01 sec) mysql> insert tb_students_info(name,age,sex,height,course_id) values('tom',22,'male',172,1),('mary',25,'female',170,4),('lily',23,'male',170,2),('jim',26,'male',176,1),('henry',24,'male',180,3),('sean',39,'male',178,3),('lisi',25,'female',176,2),('lisa',24,'male',181,2),('green',22,'female',188,1),('jerry',29,'male',175,3); Query OK, 10 rows affected (0.00 sec) Records: 10 Duplicates: 0 Warnings: 0mysql> SELECT * FROM tb_students_info; +----+-------+------+--------+--------+-----------+ | id | name | age | sex | height | course_id | +----+-------+------+--------+--------+-----------+ | 1 | tom | 22 | male | 172 | 1 | | 2 | mary | 25 | female | 170 | 4 | | 3 | lily | 23 | male | 170 | 2 | | 4 | jim | 26 | male | 176 | 1 | | 5 | henry | 24 | male | 180 | 3 | | 6 | sean | 39 | male | 178 | 3 | | 7 | lisi | 25 | female | 176 | 2 | | 8 | lisa | 24 | male | 181 | 2 | | 9 | green | 22 | female | 188 | 1 | | 10 | jerry | 29 | male | 175 | 3 | +----+-------+------+--------+--------+-----------+ 10 rows in set (0.00 sec)2)查詢 tb_course 表中的數據,SQL 語句和運行結果如下:
mysql> create table tb_course(id int not null primary key auto_increment,course_name varchar(30));Query OK, 0 rows affected (0.07 sec)mysql> desc tb_course; +-------------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | course_name | varchar(30) | YES | | NULL | | +-------------+-------------+------+-----+---------+----------------+ 2 rows in set (0.00 sec)mysql> insert tb_course(course_name) values('Java'),('MySQL'),('Python'),('Go'),('C++'); Query OK, 5 rows affected (0.00 sec) Records: 5 Duplicates: 0 Warnings: 0mysql> SELECT * FROM tb_course; +----+-------------+ | id | course_name | +----+-------------+ | 1 | Java | | 2 | MySQL | | 3 | Python | | 4 | Go | | 5 | C++ | +----+-------------+ 5 rows in set (0.00 sec)3)使用 CROSS JOIN 查詢出兩張表中的笛卡爾積,SQL 語句和運行結果如下:
mysql> SELECT * FROM tb_course CROSS JOIN tb_students_info; +----+-------------+----+-------+------+--------+--------+-----------+ | id | course_name | id | name | age | sex | height | course_id | +----+-------------+----+-------+------+--------+--------+-----------+ | 1 | Java | 1 | tom | 22 | male | 172 | 1 | | 2 | MySQL | 1 | tom | 22 | male | 172 | 1 | | 3 | Python | 1 | tom | 22 | male | 172 | 1 | | 4 | Go | 1 | tom | 22 | male | 172 | 1 | | 5 | C++ | 1 | tom | 22 | male | 172 | 1 | | 1 | Java | 2 | mary | 25 | female | 170 | 4 | | 2 | MySQL | 2 | mary | 25 | female | 170 | 4 | | 3 | Python | 2 | mary | 25 | female | 170 | 4 | | 4 | Go | 2 | mary | 25 | female | 170 | 4 | | 5 | C++ | 2 | mary | 25 | female | 170 | 4 | | 1 | Java | 3 | lily | 23 | male | 170 | 2 | | 2 | MySQL | 3 | lily | 23 | male | 170 | 2 | | 3 | Python | 3 | lily | 23 | male | 170 | 2 | | 4 | Go | 3 | lily | 23 | male | 170 | 2 | | 5 | C++ | 3 | lily | 23 | male | 170 | 2 | | 1 | Java | 4 | jim | 26 | male | 176 | 1 | | 2 | MySQL | 4 | jim | 26 | male | 176 | 1 | | 3 | Python | 4 | jim | 26 | male | 176 | 1 | | 4 | Go | 4 | jim | 26 | male | 176 | 1 | | 5 | C++ | 4 | jim | 26 | male | 176 | 1 | | 1 | Java | 5 | henry | 24 | male | 180 | 3 | | 2 | MySQL | 5 | henry | 24 | male | 180 | 3 | | 3 | Python | 5 | henry | 24 | male | 180 | 3 | | 4 | Go | 5 | henry | 24 | male | 180 | 3 | | 5 | C++ | 5 | henry | 24 | male | 180 | 3 | | 1 | Java | 6 | sean | 39 | male | 178 | 3 | | 2 | MySQL | 6 | sean | 39 | male | 178 | 3 | | 3 | Python | 6 | sean | 39 | male | 178 | 3 | | 4 | Go | 6 | sean | 39 | male | 178 | 3 | | 5 | C++ | 6 | sean | 39 | male | 178 | 3 | | 1 | Java | 7 | lisi | 25 | female | 176 | 2 | | 2 | MySQL | 7 | lisi | 25 | female | 176 | 2 | | 3 | Python | 7 | lisi | 25 | female | 176 | 2 | | 4 | Go | 7 | lisi | 25 | female | 176 | 2 | | 5 | C++ | 7 | lisi | 25 | female | 176 | 2 | | 1 | Java | 8 | lisa | 24 | male | 181 | 2 | | 2 | MySQL | 8 | lisa | 24 | male | 181 | 2 | | 3 | Python | 8 | lisa | 24 | male | 181 | 2 | | 4 | Go | 8 | lisa | 24 | male | 181 | 2 | | 5 | C++ | 8 | lisa | 24 | male | 181 | 2 | | 1 | Java | 9 | green | 22 | female | 188 | 1 | | 2 | MySQL | 9 | green | 22 | female | 188 | 1 | | 3 | Python | 9 | green | 22 | female | 188 | 1 | | 4 | Go | 9 | green | 22 | female | 188 | 1 | | 5 | C++ | 9 | green | 22 | female | 188 | 1 | | 1 | Java | 10 | jerry | 29 | male | 175 | 3 | | 2 | MySQL | 10 | jerry | 29 | male | 175 | 3 | | 3 | Python | 10 | jerry | 29 | male | 175 | 3 | | 4 | Go | 10 | jerry | 29 | male | 175 | 3 | | 5 | C++ | 10 | jerry | 29 | male | 175 | 3 | +----+-------------+----+-------+------+--------+--------+-----------+ 50 rows in set (0.00 sec)由運行結果可以看出,兩張表交叉連接查詢后,返回了50 條記錄。可以想象,當表中的數據較多時,得到的運行結果會非常長,而且得到的運行結果也沒太大的意義。所以,通過交叉連接的方式進行多表查詢的這種方法并不常用,我們應該盡量避免這種查詢。
例 2
查詢 tb_course 表中的 id 字段和 tb_students_info 表中的 interst_name字段相等的內容, SQL 語句和運行結果如下:
mysql> SELECT * FROM tb_course CROSS JOIN tb_students_info WHERE tb_students_info.interst_id = tb_course.id; +----+--------------+----+-----------+------+--------+------------+ | id | interst_name | id | name | age | sex | interst_id | +----+--------------+----+-----------+------+--------+------------+ | 1 | sleep | 1 | tom | 22 | male | 1 | | 4 | ski | 2 | mary | 25 | female | 4 | | 2 | pubg | 3 | sean | 23 | male | 2 | | 1 | sleep | 4 | susan | 26 | male | 1 | | 3 | surf | 5 | qiuyi | 27 | female | 3 | | 3 | surf | 6 | jim | 24 | male | 3 | | 3 | surf | 7 | wangwu | 39 | male | 3 | | 2 | pubg | 8 | zhangshan | 25 | female | 2 | | 2 | pubg | 9 | lily | 24 | male | 2 | | 1 | sleep | 10 | lisa | 22 | female | 1 | +----+--------------+----+-----------+------+--------+------------+ 10 rows in set (0.00 sec)如果在交叉連接時使用 WHERE 子句,MySQL 會先生成兩個表的笛卡爾積,然后再選擇滿足 WHERE 條件的記錄。因此,表的數量較多時,交叉連接會非常非常慢。一般情況下不建議使用交叉連接。
在 MySQL 中,多表查詢一般使用內連接和外連接,它們的效率要高于交叉連接。
內連接
內連接(INNER JOIN)主要通過設置連接條件的方式,來移除查詢結果中某些數據行的交叉連接。簡單來說,就是利用條件表達式來消除交叉連接的某些數據行。
內連接使用 INNER JOIN 關鍵字連接兩張表,并使用 ON 子句來設置連接條件。如果沒有連接條件,INNER JOIN 和 CROSS JOIN 在語法上是等同的,兩者可以互換。
內連接的語法格式如下:
SELECT <字段名> FROM <表1> INNER JOIN <表2> [ON子句];語法說明如下。
- 字段名:需要查詢的字段名稱。
- <表1><表2>:需要內連接的表名。
- INNER JOIN :內連接中可以省略 INNER 關鍵字,只用關鍵字 JOIN。
- ON 子句:用來設置內連接的連接條件。
INNER JOIN 也可以使用 WHERE 子句指定連接條件,但是 INNER JOIN … ON 語法是官方的標準寫法,而且 WHERE 子句在某些時候會影響查詢的性能。
多個表內連接時,在 FROM 后連續使用 INNER JOIN 或 JOIN 即可。
內連接可以查詢兩個或兩個以上的表。為了更好的理解,暫時只講解兩個表的連接查詢。
例 1
在 tb_students_info 表和 tb_course 表之間,使用內連接查詢學生姓名和相對應的課程名稱,SQL 語句和運行結果如下。
mysql> SELECT s.name,c.course_name FROM tb_students_info s INNER JOIN tb_course c ON s.course_id = c.id; +-------+-------------+ | name | course_name | +-------+-------------+ | tom | Java | | mary | Go | | lily | MySQL | | jim | Java | | henry | Python | | sean | Python | | lisi | MySQL | | lisa | MySQL | | green | Java | | jerry | Python | +-------+-------------+ 10 rows in set (0.00 sec)在這里的查詢語句中,兩個表之間的關系通過 INNER JOIN指定,連接的條件使用ON子句給出。
注意:當對多個表進行查詢時,要在 SELECT 語句后面指定字段是來源于哪一張表。因此,在多表查詢時,SELECT 語句后面的寫法是表名.列名。另外,如果表名非常長的話,也可以給表設置別名,這樣就可以直接在 SELECT 語句后面寫上表的別名.列名。
外連接
內連接的查詢結果都是符合連接條件的記錄,而外連接會先將連接的表分為基表和參考表,再以基表為依據返回滿足和不滿足條件的記錄。
外連接可以分為左外連接和右外連接2種,下面根據實例分別介紹左外連接和右外連接。
左連接
左外連接又稱為左連接,使用 LEFT OUTER JOIN 關鍵字連接兩個表,并使用 ON 子句來設置連接條件。
左連接的語法格式如下:
SELECT <字段名> FROM <表1> LEFT OUTER JOIN <表2> <ON子句>;語法說明如下:
- 字段名:需要查詢的字段名稱。
- <表1><表2>:需要左連接的表名。
- LEFT OUTER JOIN:左連接中可以省略 OUTER 關鍵字,只使用關鍵字 LEFT JOIN。
- ON 子句:用來設置左連接的連接條件,不能省略。
上述語法中,"表1"為基表,"表2"為參考表。左連接查詢時,可以查詢出"表1"中的所有記錄和"表2"中匹配連接條件的記錄。如果"表1"的某行在"表2"中沒有匹配行,那么在返回結果中,"表2"的字段值均為空值(NULL)。
例 1
在進行左連接查詢之前,我們先查看 tb_course 和 tb_students_info 兩張表中的數據。SQL 語句和運行結果如下:
mysql> insert tb_students_info(name,age,sex,height,course_id) values('liming',22,'male',180,7); Query OK, 1 row affected (0.01 sec)mysql> insert tb_course(course_name) values('HTML'); Query OK, 1 row affected (0.01 sec)mysql> SELECT * FROM tb_students_info; +----+--------+------+--------+--------+-----------+ | id | name | age | sex | height | course_id | +----+--------+------+--------+--------+-----------+ | 1 | tom | 22 | male | 172 | 1 | | 2 | mary | 25 | female | 170 | 4 | | 3 | lily | 23 | male | 170 | 2 | | 4 | jim | 26 | male | 176 | 1 | | 5 | henry | 24 | male | 180 | 3 | | 6 | sean | 39 | male | 178 | 3 | | 7 | lisi | 25 | female | 176 | 2 | | 8 | lisa | 24 | male | 181 | 2 | | 9 | green | 22 | female | 188 | 1 | | 10 | jerry | 29 | male | 175 | 3 | | 11 | liming | 22 | male | 180 | 7 | +----+--------+------+--------+--------+-----------+ 11 rows in set (0.01 sec)mysql> SELECT * FROM tb_course; +----+-------------+ | id | course_name | +----+-------------+ | 1 | Java | | 2 | MySQL | | 3 | Python | | 4 | Go | | 5 | C++ | | 6 | HTML | +----+-------------+ 6 rows in set (0.00 sec)在 tb_students_info 表和 tb_course 表中查詢所有學生姓名和相對應的課程名稱,包括沒有課程的學生,SQL 語句和運行結果如下:
mysql> SELECT s.name,c.course_name FROM tb_students_info s LEFT OUTER JOIN tb_course c ON s.`course_id`=c.`id`; +--------+-------------+ | name | course_name | +--------+-------------+ | tom | Java | | mary | Go | | lily | MySQL | | jim | Java | | henry | Python | | sean | Python | | lisi | MySQL | | lisa | MySQL | | green | Java | | jerry | Python | | liming | NULL | +--------+-------------+ 11 rows in set (0.00 sec)所以該條記錄只取出了 tb_students_info 表中相應的值,而從 tb_course 表中取出的值為 NULL。
右連接
右外連接又稱為右連接,右連接是左連接的反向連接。使用 RIGHT OUTER JOIN 關鍵字連接兩個表,并使用 ON 子句來設置連接條件。
右連接的語法格式如下:
SELECT <字段名> FROM <表1> RIGHT OUTER JOIN <表2> <ON子句>;語法說明如下:
- 字段名:需要查詢的字段名稱。
- <表1><表2>:需要右連接的表名。
- RIGHT OUTER JOIN:右連接中可以省略 OUTER 關鍵字,只使用關鍵字 RIGHT JOIN。
- ON 子句:用來設置右連接的連接條件,不能省略。
與左連接相反,右連接以"表2"為基表,"表1"為參考表。右連接查詢時,可以查詢出"表2"中的所有記錄和"表1"中匹配連接條件的記錄。如果"表2"的某行在"表1"中沒有匹配行,那么在返回結果中,"表1"的字段值均為空值(NULL)。
例 2
在 tb_students_info 表和 tb_course 表中查詢所有課程,包括沒有學生的課程,SQL 語句和運行結果如下:
mysql> SELECT s.name,c.course_name FROM tb_students_info s RIGHT OUTER JOIN tb_course c ON s.`course_id`=c.`id`; +-------+-------------+ | name | course_name | +-------+-------------+ | tom | Java | | mary | Go | | lily | MySQL | | jim | Java | | henry | Python | | sean | Python | | lisi | MySQL | | lisa | MySQL | | green | Java | | jerry | Python | | NULL | C++ | +-------+-------------+ 11 rows in set (0.00 sec)可以看到,結果顯示了 11 條記錄,名稱為 HTML 的課程目前沒有學生,因為對應的tb_students_info 表中并沒有該學生的信息,所以該條記錄只取出了 tb_course 表中相應的值,而從 tb_students_info 表中取出的值為 NULL。
多個表左/右連接時,在 ON 子句后連續使用 LEFT/RIGHT OUTER JOIN 或 LEFT/RIGHT JOIN 即可。
使用外連接查詢時,一定要分清需要查詢的結果,是需要顯示左表的全部記錄還是右表的全部記錄,然后選擇相應的左連接和右連接。
分組查詢
在 MySQL 中,GROUP BY 關鍵字可以根據一個或多個字段對查詢結果進行分組。
使用 GROUP BY 關鍵字的語法格式如下:
GROUP BY <字段名>其中,"字段名"表示需要分組的字段名稱,多個字段時用逗號隔開。
GROUP BY單獨使用
單獨使用 GROUP BY 關鍵字時,查詢結果會只顯示每個分組的第一條記錄。
下面根據 tb_students_info 表中的 sex 字段進行分組查詢,SQL 語句和運行結果如下:
mysql> SELECT `name`,`sex` FROM tb_students_info GROUP BY sex; +------+--------+ | name | sex | +------+--------+ | mary | female | | tom | male | +------+--------+ 2 rows in set (0.01 sec)結果中只顯示了兩條記錄,這兩條記錄的 sex 字段的值分別為“女”和“男”。
GROUP BY 與 GROUP_CONCAT()
GROUP BY 關鍵字可以和 GROUP_CONCAT() 函數一起使用。GROUP_CONCAT() 函數會把每個分組的字段值都顯示出來。
下面根據 tb_students_info 表中的 sex 字段進行分組查詢,使用 GROUP_CONCAT() 函數將每個分組的 name 字段的值都顯示出來。SQL 語句和運行結果如下:
mysql> SELECT `sex`, GROUP_CONCAT(name) FROM tb_students_info GROUP BY sex; +--------+------------------------------------+ | sex | GROUP_CONCAT(name) | +--------+------------------------------------+ | female | mary,lisi,green | | male | tom,lily,jim,henry,sean,lisa,jerry | +--------+------------------------------------+ 2 rows in set (0.00 sec)由結果可以看到,查詢結果分為兩組,sex 字段值為"女"的是一組,值為"男"的是一組,且每組的學生姓名都顯示出來了。
下面根據 tb_students_info 表中的 age 和 sex 字段進行分組查詢。SQL 語句和運行結果如下:
mysql> SELECT age,sex,GROUP_CONCAT(name) FROM tb_students_info GROUP BY age,sex; +------+--------+--------------------+ | age | sex | GROUP_CONCAT(name) | +------+--------+--------------------+ | 22 | female | green | | 22 | male | tom | | 23 | male | lily | | 24 | male | henry,lisa | | 25 | female | mary,lisi | | 26 | male | jim | | 29 | male | jerry | | 39 | male | sean | +------+--------+--------------------+ 8 rows in set (0.00 sec)上面實例在分組過程中,先按照 age 字段進行分組,當 age 字段值相等時,再把 age 字段值相等的記錄按照 sex 字段進行分組。
多個字段分組查詢時,會先按照第一個字段進行分組。如果第一個字段中有相同的值,MySQL 才會按照第二個字段進行分組。如果第一個字段中的數據都是唯一的,那么 MySQL 將不再對第二個字段進行分組。
GROUP BY 與聚合函數
在數據統計時,GROUP BY 關鍵字經常和聚合函數一起使用。
聚合函數包括 COUNT(),SUM(),AVG(),MAX() 和 MIN()。其中,COUNT() 用來統計記錄的條數;SUM() 用來計算字段值的總和;AVG() 用來計算字段值的平均值;MAX() 用來查詢字段的最大值;MIN() 用來查詢字段的最小值。
下面根據 tb_students_info 表的 sex 字段進行分組查詢,使用 COUNT() 函數計算每一組的記錄數。SQL 語句和運行結果如下:
mysql> SELECT sex,COUNT(sex) FROM tb_students_info GROUP BY sex; +--------+------------+ | sex | COUNT(sex) | +--------+------------+ | female | 3 | | male | 7 | +--------+------------+ 2 rows in set (0.00 sec)結果顯示,sex 字段值為"女"的記錄是一組,有 3 條記錄;sex 字段值為"男"的記錄是一組,有 7 條記錄。
GROUP BY 與 WITH ROLLUP
WITH POLLUP 關鍵字用來在所有記錄的最后加上一條記錄,這條記錄是上面所有記錄的總和,即統計記錄數量。
下面根據 tb_students_info 表中的 sex 字段進行分組查詢,并使用 WITH ROLLUP 顯示記錄的總和:
mysql> SELECT sex,GROUP_CONCAT(name) FROM tb_students_info GROUP BY sex WITH ROLLUP; +--------+----------------------------------------------------+ | sex | GROUP_CONCAT(name) | +--------+----------------------------------------------------+ | female | mary,lisi,green | | male | tom,lily,jim,henry,sean,lisa,jerry | | NULL | mary,lisi,green,tom,lily,jim,henry,sean,lisa,jerry | +--------+----------------------------------------------------+ 3 rows in set (0.00 sec)查詢結果顯示,GROUP_CONCAT(name) 顯示了每個分組的 name 字段值。同時,最后一條記錄的 GROUP_CONCAT(name) 字段的值剛好是上面分組 name 字段值的總和。
子查詢
子查詢是 MySQL 中比較常用的查詢方法,通過子查詢可以實現多表查詢。子查詢指將一個查詢語句嵌套在另一個查詢語句中。子查詢可以在 SELECT、UPDATE 和 DELETE 語句中使用,而且可以進行多層嵌套。在實際開發時,子查詢經常出現在 WHERE 子句中。
子查詢在 WHERE 中的語法格式如下:
WHERE <表達式> <操作符> (子查詢)其中,操作符可以是比較運算符和 IN、NOT IN、EXISTS、NOT EXISTS 等關鍵字。
1)IN | NOT IN
當表達式與子查詢返回的結果集中的某個值相等時,返回 TRUE,否則返回 FALSE;若使用關鍵字 NOT,則返回值正好相反。
2)EXISTS | NOT EXISTS
用于判斷子查詢的結果集是否為空,若子查詢的結果集不為空,返回 TRUE,否則返回 FALSE;若使用關鍵字 NOT,則返回的值正好相反。
例 1
使用子查詢在 tb_students_info 表和 tb_course 表中查詢學習 Java 課程的學生姓名,SQL 語句和運行結果如下:
mysql> SELECT name FROM tb_students_info WHERE course_id IN (SELECT id FROM tb_course WHERE course_name = 'Java'); +-------+ | name | +-------+ | tom | | jim | | green | +-------+ 3 rows in set (0.00 sec)上述查詢過程也可以分為以下 2 步執行,實現效果是相同的。
首先單獨執行內查詢,查詢出 tb_course 表中課程為 Java 的 id,SQL 語句和運行結果如下:
mysql> SELECT id FROM tb_course WHERE course_name = 'Java'; +----+ | id | +----+ | 1 | +----+ 1 row in set (0.00 sec)可以看到,符合條件的 id 字段的值為 1。
然后執行外層查詢,在 tb_students_info 表中查詢 course_id 等于 1 的學生姓名。SQL 語句和運行結果如下:
mysql> SELECT name FROM tb_students_info WHERE course_id IN (1); +-------+ | name | +-------+ | tom | | jim | | green | +-------+ 3 rows in set (0.00 sec)習慣上,外層的 SELECT 查詢稱為父查詢,圓括號中嵌入的查詢稱為子查詢(子查詢必須放在圓括號內)。MySQL 在處理上例的 SELECT 語句時,執行流程為:先執行子查詢,再執行父查詢。
例 2
與例 1 類似,在 SELECT 語句中使用 NOT IN 關鍵字,查詢沒有學習 Java 課程的學生姓名,SQL 語句和運行結果如下:
mysql> SELECT name FROM tb_students_info WHERE course_id NOT IN (SELECT id FROM tb_course WHERE course_name = 'Java'); +-------+ | name | +-------+ | mary | | lily | | henry | | sean | | lisi | | lisa | | jerry | +-------+ 7 rows in set (0.01 sec)可以看出,運行結果與上面的例子剛好相反,沒有學習 Java 課程的是除了 Dany 和 Henry 之外的學生。
例 3
使用=運算符,在 tb_course 表和 tb_students_info 表中查詢出所有學習 Python 課程的學生姓名,SQL 語句和運行結果如下:
mysql> SELECT name FROM tb_students_info WHERE course_id = (SELECT id FROM tb_course WHERE course_name = 'Python'); +-------+ | name | +-------+ | henry | | sean | | jerry | +-------+ 3 rows in set (0.00 sec)例 4
使用<>運算符,在 tb_course 表和 tb_students_info 表中查詢出沒有學習 Python 課程的學生姓名,SQL 語句和運行結果如下:
mysql> SELECT name FROM tb_students_info WHERE course_id <> (SELECT id FROM tb_course WHERE course_name = 'Python'); +-------+ | name | +-------+ | tom | | mary | | lily | | jim | | lisi | | lisa | | green | +-------+ 7 rows in set (0.00 sec)可以看出,運行結果與例 3 剛好相反,沒有學習 Python 課程的是除了 Jane 之外的學生。
例 5
查詢 tb_course 表中是否存在 id=1 的課程,如果存在,就查詢出 tb_students_info 表中的記錄,SQL 語句和運行結果如下:
mysql> SELECT * FROM tb_students_info WHERE EXISTS(SELECT course_name FROM tb_course WHERE id=1); +----+-------+------+--------+--------+-----------+ | id | name | age | sex | height | course_id | +----+-------+------+--------+--------+-----------+ | 1 | tom | 22 | male | 172 | 1 | | 2 | mary | 25 | female | 170 | 4 | | 3 | lily | 23 | male | 170 | 2 | | 4 | jim | 26 | male | 176 | 1 | | 5 | henry | 24 | male | 180 | 3 | | 6 | sean | 39 | male | 178 | 3 | | 7 | lisi | 25 | female | 176 | 2 | | 8 | lisa | 24 | male | 181 | 2 | | 9 | green | 22 | female | 188 | 1 | | 10 | jerry | 29 | male | 175 | 3 | +----+-------+------+--------+--------+-----------+ 10 rows in set (0.00 sec)外層查詢語句接收 TRUE 之后對表 tb_students_info 進行查詢,返回所有的記錄。
EXISTS 關鍵字可以和其它查詢條件一起使用,條件表達式與 EXISTS 關鍵字之間用 AND 和 OR 連接。
例 6
查詢 tb_course 表中是否存在 id=1 的課程,如果存在,就查詢出 tb_students_info 表中 age 字段大于 24 的記錄,SQL 語句和運行結果如下:
mysql> SELECT * FROM tb_students_info WHERE age>24 AND EXISTS(SELECT course_name FROM tb_course WHERE id=1); +----+-------+------+--------+--------+-----------+ | id | name | age | sex | height | course_id | +----+-------+------+--------+--------+-----------+ | 2 | mary | 25 | female | 170 | 4 | | 4 | jim | 26 | male | 176 | 1 | | 6 | sean | 39 | male | 178 | 3 | | 7 | lisi | 25 | female | 176 | 2 | | 10 | jerry | 29 | male | 175 | 3 | +----+-------+------+--------+--------+-----------+ 5 rows in set (0.01 sec)內層查詢語句從 tb_course 表中查詢到記錄,返回 TRUE。外層查詢語句開始進行查詢。根據查詢條件,從 tb_students_info 表中查詢 age 大于 24 的記錄。
子查詢的功能也可以通過表連接完成,但是子查詢會使 SQL 語句更容易閱讀和編寫。
一般來說,表連接(內連接和外連接等)都可以用子查詢替換,但反過來卻不一定,有的子查詢不能用表連接來替換。子查詢比較靈活、方便、形式多樣,適合作為查詢的篩選條件,而表連接更適合于查看連接表的數據。
總結
以上是生活随笔為你收集整理的MySQL的多表查询的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Winrunner经验
- 下一篇: 2020年中国人口出生率为8.52‰,首