所謂複雜查詢,指涉及多個表、具有嵌套等複雜結構的查詢。這裏簡要介紹典型的幾種複雜查詢格式。
一、連接查詢
連接是區別關係與非關係系統的最重要的標誌。通過連接運算符可以實現多個表查詢。連接查詢主要包括內連接、外連接等。
假設有Student
和Grade
兩個表如下:
+-----+--------+-------+ +-----+------------+--------+
| sID | sName | sDept | | gID | gCourse | gScore |
+-----+--------+-------+ +-----+------------+--------+
| 1 | Paul | CS | | 1 | Math | 87 |
| 2 | Oliver | MS | | 2 | English | 95 |
| 3 | Jack | SE | | 3 | Physics | 76 |
| 4 | Robin | CS | | 7 | Philosophy | 76 |
+-----+--------+-------+ +-----+------------+--------+
1.1 內連接
內連接(INNER JOIN)使用比較運算符進行表間列數據的比較操作,並列出這些表中與連接條件相匹配的數據行,組合成新的記錄。
當比較操作符是=
時,稱爲等值連接:
SELECT * FROM Student INNER JOIN Grade ON Student.sID = Grade.gID;
等價於
SELECT * FROM Student,Grade WHERE Student.sID = Grade.gID;
結果如下:
+-----+--------+-------+-----+---------+--------+
| sID | sName | sDept | gID | gCourse | gScore |
+-----+--------+-------+-----+---------+--------+
| 1 | Paul | CS | 1 | Math | 87 |
| 2 | Oliver | MS | 2 | English | 95 |
| 3 | Jack | SE | 3 | Physics | 76 |
+-----+--------+-------+-----+---------+--------+
可以看出,在內連接查詢中,只有滿足條件的記錄才能出現在結果關係中。
1.2 外連接
與內連接不同的是,外連接返回的查詢結果集中不僅包含符合連接條件的行,而且還包括左表(左連接)、右表(右連接)或兩個表(全外連接)中的所有數據行。
1.2.1 左連接
LEFT JOIN(左連接),即LEFT OUTER JOIN
,返回左表的全部記錄,即使右表中沒有對應匹配記錄。
SELECT * FROM Student LEFT JOIN Grade ON Student.sID = Grade.gID;
結果如下:
+-----+--------+-------+------+---------+--------+
| sID | sName | sDept | gID | gCourse | gScore |
+-----+--------+-------+------+---------+--------+
| 1 | Paul | CS | 1 | Math | 87 |
| 2 | Oliver | MS | 2 | English | 95 |
| 3 | Jack | SE | 3 | Physics | 76 |
| 4 | Robin | CS | NULL | NULL | NULL |
+-----+--------+-------+------+---------+--------+
1.2.2 右連接
RIGHT JOIN(右連接),即RIGHT OUTER JOIN
,返回右表的全部記錄,即使左表中沒有對應匹配記錄。
SELECT * FROM Student RIGHT JOIN Grade ON Student.sID = Grade.gID;
結果如下:
+------+--------+-------+-----+------------+--------+
| sID | sName | sDept | gID | gCourse | gScore |
+------+--------+-------+-----+------------+--------+
| 1 | Paul | CS | 1 | Math | 87 |
| 2 | Oliver | MS | 2 | English | 95 |
| 3 | Jack | SE | 3 | Physics | 76 |
| NULL | NULL | NULL | 7 | Philosophy | 76 |
+------+--------+-------+-----+------------+--------+
1.2.3 全連接
FULL JOIN(全連接),即FULL OUTER JOIN
,返回左表、右表的全部記錄,即使沒有對應的匹配記錄。
**注意:**MySQL不支持FULL JOIN
,不過可以通過UNION
關鍵字來合併 LEFT JOIN 與 RIGHT JOIN來模擬。
SELECT * FROM Student LEFT JOIN Grade ON Student.sID = Grade.gID
UNION
SELECT * FROM Student RIGHT JOIN Grade ON Student.sID = Grade.gID;
結果如下:
+------+--------+-------+------+------------+--------+
| sID | sName | sDept | gID | gCourse | gScore |
+------+--------+-------+------+------------+--------+
| 1 | Paul | CS | 1 | Math | 87 |
| 2 | Oliver | MS | 2 | English | 95 |
| 3 | Jack | SE | 3 | Physics | 76 |
| 4 | Robin | CS | NULL | NULL | NULL |
| NULL | NULL | NULL | 7 | Philosophy | 76 |
+------+--------+-------+------+------------+--------+
另外,如果在一個連接查詢中涉及到的兩個表是同一個表,這種查詢稱爲自連接查詢。爲了防止產生二義性,自連接查詢中通常對錶使用別名。
二、子查詢
子查詢是指一個查詢語句嵌套在另一個查詢語句內部的查詢。在 SELECT 子句中先計算子查詢,子查詢結果作爲外層另一個查詢的過濾條件。
子查詢中常用的操作符有ANY
、SOME
、ALL
、EXISTS
、IN
,也可以使用比較運算符。子查詢可以添加到 SELECT、UPDATE 和 DELETE 語句中,而且可以進行多層嵌套。
2.1 在條件表達式中產生標量的子查詢
SELECT *
FROM score
WHERE id = (SELECT event_id
FROM event
WHERE date='2015-07-01'
AND type='Q');
所謂標量,就是單個屬性的一個原子值。當子查詢出現在 WHERE 子句中的比較運算符(= ,>, >= ,< , <= ,<>)的右邊,其輸出結果應該只有一個纔對。很容易理解,如果返回多條結果,就無法進行比較,系統就會報錯。
又如:
SELECT * FROM teacher WHERE birth = MIN(birth); /*錯誤*/
這個查詢是錯的!因爲MySQL不允許在子句裏面使用統計函數,所以改用子查詢:
SELECT *
FROM teacher
WHERE birth = (SELECT MIN(birth)
FROM teacher);
2.2 在條件表達式中產生集合的子查詢
如果子查詢的輸出是一個結果集合,可以通過 ANY、ALL、IN 進行比較。
2.2.1 ANY與SOME
ANY
和SOME
關鍵字是同義詞,表示滿足其中任一條件。它們允許創建一個表達式對子查詢的返回結果集進行比較:
SELECT num1
FROM t1
WHERE num1 > ANY(SELECT num2
FROM t2);
上面的子查詢返回 t2 的 num2 列,然後將 t1 中的 num1 值與之進行比較,只要大於 num2 的任何一個值,即爲符合查詢條件的結果。
等價於:
SELECT num1
FROM t1
WHERE num1 > SOME(SELECT num2
FROM t2);
2.2.2 ALL
與ANY/SOME
不同,使用ALL
時需要同時滿足所有內層查詢的條件。
SELECT num1
FROM t1
WHERE num1 > ALL(SELECT num2
FROM t2);
上面的子查詢還是返回 t2 的 num2 列,然後將 t1 中的 num1 值與之進行比較。但是隻有大於所有 num2 值的 num1 纔是符合查詢條件的結果。
2.2.3 IN
IN
關鍵字後接一個子查詢,若在子查詢結果集中,返回true,否則返回false。與之相對的是NOT IN
。
SELECT num1
FROM t1
WHERE num1 IN (SELECT num2
FROM t2);
2.3 在條件表達式中測試空/非空的子查詢
EXISTS
關鍵字後接一個任意的子查詢,系統對子查詢進行運算以判斷它是否返回行。
- 若至少返回一行,那麼 EXISTS 的結果爲 true,此時外層查詢語句將進行查詢;
- 若沒有返回任何行,那麼 EXISTS 的結果爲 false,此時外層語句將不進行查詢。
SELECT sName
FROM Student
WHERE EXISTS (SELECT *
FROM Grade
WHERE gScore < 60);
EXISTS
和NOT EXISTS
的結果只取決於是否會返回行,而不取決於這些行的內容。
2.4 關聯子查詢
一般的子查詢只計算一次,其結果用於外層查詢。但關聯子查詢需要計算多次。
子查詢中使用了主查詢中的某些字段,主查詢每掃描一行都要執行一次子查詢,這種子查詢稱爲關聯子查詢(Correlated Subquery)。
SELECT sName
FROM Student
WHERE '450' NOT IN (SELECT courseID
FROM Course
WHERE sID = Student.sID);
上面的子查詢中使用了 Student 表的 sID 字段。對於 Student 表中每一個 sID 都會執行一次子查詢。
2.5 FROM子句中的子查詢
子查詢可以用括號括起來作爲一個關係,從而出現在 FROM 列表中。由於子查詢的結果關係沒有正式的名字,故必須給它取一個別名。
SELECT *
FROM Grade,
(SELECT * FROM Student WHERE sDept='CS')x
WHERE x.sID=Grade.gID;
x 就是子查詢的結果關係的別名。
三、合併查詢結果
利用UNION
或UNION ALL
關鍵字,可以將多個 SELECT 語句的結果組合成單個結果集。合併時,兩個表對應的列數和數據類型必須相同。
- UNION:合併查詢結果時,刪除重複的記錄,返回的行都是唯一的。
- UNION ALL:合併查詢結果時,不刪除重複行。
3.1 UNION ALL
SELECT * FROM Student
UNION ALL
SELECT * FROM Student;
結果如下:
+-----+--------+-------+
| sID | sName | sDept |
+-----+--------+-------+
| 1 | Paul | CS |
| 2 | Oliver | MS |
| 3 | Jack | SE |
| 4 | Robin | CS |
| 1 | Paul | CS |
| 2 | Oliver | MS |
| 3 | Jack | SE |
| 4 | Robin | CS |
+-----+--------+-------+
3.2 UNION
SELECT * FROM Student
UNION
SELECT * FROM Student;
結果如下:
+-----+--------+-------+
| sID | sName | sDept |
+-----+--------+-------+
| 1 | Paul | CS |
| 2 | Oliver | MS |
| 3 | Jack | SE |
| 4 | Robin | CS |
+-----+--------+-------+