所謂復雜查詢,指涉及多個表、具有嵌套等復雜結構的查詢。這里簡要介紹典型的幾種復雜查詢格式。
一、連接查詢
連接是區別關系與非關系系統的最重要的標志。通過連接運算符可以實現多個表查詢。連接查詢主要包括內連接、外連接等。
假設有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 |
+-----+--------+-------+